UNIX系のサーバーでは、クライアントのリモート通信に SSH を使うことが標準となっています。すなわち、公開サーバーで SSHサーバー(sshd) が動作していることは容易に推測できるため、攻撃の格好の的になることはどうしても避けられません。そこで今回は、 SSHのセキュリティの3点セット「パスワード認証の無効化」「rootユーザでのログイン禁止」「sshdの待受ポートの変更」を、Ansible で設定してみたいと思います。
この記事では、Ansible で実行するタスクは、ロール(Role)に書いて、そのロールを Playbook から呼び出すといった手順で設定しています。ロールについては「Ansible Roles の使い方(Playbookの分割と再利用)」の記事をご参照ください。
SELinux 無効化ロールの作成
sshd 等、各種ミドルウェアの設定を変更するには、SELinux の設定も変更する必要があります。賛否はあるかと思いますが、今回は SELinux を無効にすることにします。
ロールディレクトリの作成(ロール名は「disable_selinux」になります)
タスクディレクトリの作成
SELinux 無効化タスクの作成
vi /etc/ansible/roles/disable_selinux/tasks/main.yml
--- - name: SELinuxを無効化 selinux: state=disabled
---
YAMLフォーマットのファイルは、慣習的に「---」(ハイフン3つ)から始めます。
- name:
タスクの簡単な説明を書きます。(これはどのタスクでも共通です)
selinux:
SELinuxモジュールを使って、SELinuxを無効にします。「state=disabled」を指定した場合は、「setenforce 0」コマンドの実行と、SELinuxの設定ファイル(/etc/selinux/config)の「SELINUX=enforcing」を「SELINUX=disabled」に書換えてくれます。
SSHD セキュリティ設定ロールの作成
本題の sshd のセキュリティ設定を行うロールの作成です。
ロールディレクトリの作成(ロール名は「sshd」になります)
タスクディレクトリの作成
パスワード認証の無効化
パスワード認証の無効化に加えて、チャレンジ・レスポンス認証(ChallengeResponseAuthentication)が有効の場合、事実上パスワード認証も有効になってしまいますので、こちらも合わせて no にしておきます。
vi /etc/ansible/roles/sshd/tasks/main.yml
--- - name: パスワード認証の無効化 lineinfile: dest: /etc/ssh/sshd_config regexp: "^PasswordAuthentication" insertafter: "^#PasswordAuthentication" line: "PasswordAuthentication no" - name: チャレンジ・レスポンス認証の無効化 lineinfile: dest: /etc/ssh/sshd_config regexp: "^ChallengeResponseAuthentication" insertafter: "^#ChallengeResponseAuthentication" line: "ChallengeResponseAuthentication no"
lineinfile:
lineinfileモジュールを使って、sshdの設定ファイルを編集します。
dest:
編集するファイルのパスを指定
regexp:
ここに指定した正規表現にマッチした行を、lineで指定した文字列に置き換える。(ただし lineで指定した文字列と、まったく同じ場合はなにもしない)
insertafter:
regexp にマッチする行が無かった場合、ここに指定した正規表現にマッチした次の行に、lineで指定した文字列を挿入する。マッチしない場合はファイル末尾に、lineで指定した文字列が挿入されます。
line:
置換または挿入する文字列を指定します。
lineinfileモジュールの動作サンプル
上の説明だと regexp、insertafter、line の動作が少々分かりづらいですね(^^;) 以下にこのタスクを実行した時の置換/挿入例をあげておきます。
regexp にマッチするので lineで指定した文字列に置き換える。
↓
PasswordAuthentication no
regexp にマッチするが lineで指定した文字列と同じなのでなにもしない。
regexp にマッチしないので、insertafter にマッチした次の行に、 lineで指定した文字列を挿入する。
↓
#PasswordAuthentication yes
PasswordAuthentication no
regexp にも insertafter にもマッチしない場合は、 lineで指定した文字列がファイル末尾に挿入されます。
root ユーザでのログイン禁止
同様に、root ユーザでのログイン禁止設定もlineinfileモジュールを使います。
vi /etc/ansible/roles/sshd/tasks/main.yml(続き)
- name: rootユーザのログイン禁止 lineinfile: dest: /etc/ssh/sshd_config regexp: "^PermitRootLogin" insertafter: "^#PermitRootLogin" line: "PermitRootLogin no"
sshd の待受ポート変更
タスクを作成する前に、設定するsshdの待受ポートを変数として定義しておきます。
ロールの変数の定義は、変数の優先度が高い「vars」ディレクトリまたは、優先度が一番低い「defaults」ディレクトリで定義します。Playbook などから変数を上書き出来るように、今回は defaults ディレクトリで定義することにします。
Ansibleの変数の優先度については下記をご参照ください。
Variable Precedence: Where Should I Put A Variable? | Ansible Documentation
変数定義ディレクトリの作成
変数定義ファイルの作成
vi /etc/ansible/roles/sshd/defaults/main.yml
sshd_port: 50022
ロールで定義する変数は、先頭にロール名(上の場合は sshd_ )をプレフィックスとして付けておくのが良いそうです。ポートは、動的/プライベートポート(49513~65535)の中からお好みでご選択ください。
sshdの待受ポート変更するタスクの作成です。
vi /etc/ansible/roles/sshd/tasks/main.yml(続き)
- name: "待受ポートを {{ sshd_port }} に変更" lineinfile: dest: /etc/ssh/sshd_config regexp: "^Port" insertafter: "^#Port" line: "Port {{ sshd_port }}"
先ほど登録した変数 sshd_port を {{ と }} で囲んで呼び出します。「Port {{ sshd_port }}」は「Port 50022」に展開されます。また、変数を値に使う場合は、「"」もしくは「'」でクォートしないとエラーになりますので注意です。
sshd の再起動
このタスクが実行されたタイミングで、sshdの待受ポートが 22番 から 50022番 に変更されますが、AnsibleのSSH接続はポート22番のまま維持されますので、この後のタスクも問題無く実行できます。
vi /etc/ansible/roles/sshd/tasks/main.yml(続き)
- name: 再起動 service: name: sshd state: restarted
service:
serviceモジュールを使って、sshdサービスを再起動します。
name:
対象のサービス名を指定します(今回は sshd)
state:
行う操作を指定します。サービスを再起動する場合は「restarted」を指定します。
firewalld ポートオープンとクローズ
firewalldで、変更したsshdポートをオープンし、標準のsshdポート(22番)をクローズします。
firewalld で変更したsshdポートをオープン
vi /etc/ansible/roles/sshd/tasks/main.yml(続き)
- name: "firewalld ポート {{ sshd_port }}/tcp のオープン" firewalld: port: "{{ sshd_port }}/tcp" permanent: true state: enabled immediate: true
firewalld:
firewalldモジュールを使って、firewalld設定を変更します。
port:
変更したsshdポートと、IPプロトコルを指定します。sshdの場合はIPプロトコルはTCPになりますので、「{{ sshd_port }}/tcp」のように指定します。
permanent:
OS再起動後もこの設定を有効にする場合は「true」を指定します。
state:
ポートをオープンにする場合は「enabled」、クローズする場合は「disabled」を指定します。
immediate:
permanent: true としている場合は、必ず「true」を指定しておきましょう「firewall-cmd --reload」を実行してくれます。permanent: true の時に、immediate: true を指定しないと、firewalldが再起動されるまで設定が有効にならないので注意です。
firewalld で標準のsshdポート(22番)をクローズ
最後に、firewalldで標準のsshdポートをクローズします。タスクの「service: ssh」と「port: 22/tcp」は、同じ意味です。CentOS7では、サービス指定(ssh)で標準のsshdポート(22番)がオープンされていますので、「service: ssh」だけでも構いませんが、念のため「port: 22/tcp」もクローズするように設定しています。
vi /etc/ansible/roles/sshd/tasks/main.yml(続き)
- name: "firewalld サービス ssh のクローズ" firewalld: service: ssh permanent: true state: disabled immediate: true - name: "firewalld ポート 22/tcp のクローズ" firewalld: port: 22/tcp permanent: true state: disabled immediate: true
完成したSSHD セキュリティ設定ロール
以上でSSHDセキュリティ設定ロールの完成です。まとめると下記のようになります。
タスクファイル
vi /etc/ansible/roles/sshd/tasks/main.yml
--- - name: パスワード認証の無効化 lineinfile: dest: /etc/ssh/sshd_config regexp: "^PasswordAuthentication" insertafter: "^#PasswordAuthentication" line: "PasswordAuthentication no" - name: チャレンジ・レスポンス認証の無効化 lineinfile: dest: /etc/ssh/sshd_config regexp: "^ChallengeResponseAuthentication" insertafter: "^#ChallengeResponseAuthentication" line: "ChallengeResponseAuthentication no" - name: rootユーザのログイン禁止 lineinfile: dest: /etc/ssh/sshd_config regexp: "^PermitRootLogin" insertafter: "^#PermitRootLogin" line: "PermitRootLogin no" - name: "待受ポートを {{ sshd_port }} に変更" lineinfile: dest: /etc/ssh/sshd_config regexp: "^Port" insertafter: "^#Port" line: "Port {{ sshd_port }}" - name: 再起動 service: name: sshd state: restarted - name: "firewalld ポート {{ sshd_port }}/tcp のオープン" firewalld: port: "{{ sshd_port }}/tcp" permanent: true state: enabled immediate: true - name: "firewalld サービス ssh のクローズ" firewalld: service: ssh permanent: true state: disabled immediate: true - name: "firewalld ポート 22/tcp のクローズ" firewalld: port: 22/tcp permanent: true state: disabled immediate: true
変数定義ファイル
vi /etc/ansible/roles/sshd/defaults/main.yml
sshd_port: 50022
呼出し元の Playbook の作成
呼出し元の Playbook では、対象ホストとAnsible実行ユーザを指定します。
vi /etc/ansible/site.yml
--- - hosts: 172.16.1.111 remote_user: ansible become: true roles: - disable_selinux - sshd
- hosts:
処理対象のホストを指定します。ホストのIPアドレスまたはFQDNが、インベントリファイル「/etc/ansible/hosts」に、記載されている必要があります。
remote_user:
Ansible実行ユーザを指定します。ユーザの作成方法は「Ansible でユーザの作成」をご参照ください。
become:
タスクを root 権限で事項する場合は、「true」を指定します。サーバーの各種設定は、ほとんどの場合 root 権限が必要になりますので、remote_user が root 以外の場合は、必須の設定になるかと思います。
「Become (Privilege Escalation) | Ansible Documentation」
roles:
この下に、処理を実行するロールを書いていきます。ロールの指定方法には色々オプションがあるようです、詳細は公式マニュアルをご参照ください。
Playbook Roles and Include Statements | Ansible Documentation
Playbook(ロール)の実行
構文チェック
Playbook を「--syntax-check」オプションを指定して実行します。なにもエラーが表示されなければOKです。
ansible-playbook site.yml --syntax-check
playbook: site.yml
実行
Playbook(ロール)を実行します。
ansible-playbook site.yml
以下のような表示であれば成功です。
PLAY [172.16.1.111] ************************************************************ TASK [setup] ******************************************************************* Enter passphrase for key '/home/foo/.ssh/id_rsa': ok: [172.16.1.111] TASK [disable_selinux : SELinuxを無効化] ******************************************* changed: [172.16.1.111] TASK [sshd : パスワード認証の無効化] ****************************************************** changed: [172.16.1.111] TASK [sshd : チャレンジ・レスポンス認証の無効化] ************************************************ ok: [172.16.1.111] TASK [sshd : rootユーザのログイン禁止] *************************************************** changed: [172.16.1.111] TASK [sshd : 待受ポートを 50022 に変更] ************************************************* changed: [172.16.1.111] TASK [sshd : 再起動] ************************************************************** changed: [172.16.1.111] TASK [sshd : firewalld ポート 50022/tcp のオープン] ************************************ changed: [172.16.1.111] TASK [sshd : firewalld サービス ssh のクローズ] ***************************************** changed: [172.16.1.111] TASK [sshd : firewalld ポート 22/tcp のクローズ] *************************************** ok: [172.16.1.111] PLAY RECAP ********************************************************************* 172.16.1.111 : ok=10 changed=7 unreachable=0 failed=0
SSH標準ポート(22番)で、接続できないことや、パスワード認証ができないことを確認できるかと思います。
ssh: connect to host 172.16.1.111 port 22: No route to host
ssh -p50022 ansible@172.16.1.111 -o PreferredAuthentications=password
Permission denied (publickey,gssapi-keyex,gssapi-with-mic).
終わりに
sshdの待受ポートの変更と、firewalldの設定変更を間違ってしまい、サーバーにまったく接続できなくなった苦い思いは、皆さんも一度は経験があるのではないでしょうか(^^;)
手作業で設定している以上、どうしてもミスは付き物です。Ansibleで設定を自動化してしまえば、このような苦い思いをすることも少なくできるかもしれませんね。
コメント