AnsibleでVPSの初期設定

VPSのセットアップ(userの作成, ssh, firewall設定など)をAnsibleでやってみます。

rootで行うか、作成した管理用userで実行するかの切り分けが難しいですが、以下のような流れにします。

1. rootでinit.ymlを実行
 1. userの作成
 1. sshの設定
 1. firewallの設定
1. 管理用userでbootstrap.ymlを実行
 * zsh, git, nginx, dockerをインストール

Vagrantで立てたVM(192.168.33.12)をテスト用に使い、うまくいったら実際のVPSに適用します。

下記のサイトをかなり参考にさせていただきました。
[Ansible でさくらのVPS の環境構築を自動化　～ハマりポイントとともに～](http://akiyoko.hatenablog.jp/entry/2013/12/16/020529)
[AnsibleでVPS初期設定](http://blog.ieknir.com/blog/beginning-conoha-vps-with-ansible/)

## ファイル構成
```
├── bootstrap.yml
├── group_vars
│   ├── production
│   └── test
├── hosts
├── init.yml
├── roles
│   ├── docker
│   │   └── tasks
│   │       └── main.yml
│   ├── git
│   │   └── tasks
│   │       └── main.yml
│   ├── nginx
│   │   └── tasks
│   │       └── main.yml
│   ├── yum
│   │   └── tasks
│   │       └── main.yml
│   └── zsh
│       ├── files
│       │   └── zsh
│       │       └── completion
│       │           ├── _git
│       │           └── git-completion.bash
│       ├── tasks
│       │   └── main.yml
│       └── templates
│           └── zshrc
└── templates
    └── iptables.j2
```

## インベントリ関連

```ini:hosts
[production]
# VPSのIPアドレス
[test]
192.168.33.12
```

```ini:group_vars/test
# 初期設定後に使う接続情報
ansible_ssh_user: admin
ansible_ssh_port: 10022
ansible_ssh_private_key_file: keys/id_rsa-test # init.ymlを実行することで、このファイルが生成される

# 初期設定用の変数
ssh_user: admin
ssh_port: 10022
ssh_user_password: **** # 次のコマンドにて生成 $ openssl passwd -salt <salt> -1 <password>
ssh_private_key_file: id_rsa-test # このファイル名で、管理用userの秘密鍵を取得する
```

```ini:group_vars/production
ansible_ssh_user: admin
ansible_ssh_port: 10022
ansible_ssh_private_key_file: keys/id_rsa-production

ssh_user: admin
ssh_port: 10022
ssh_user_password: ****
ssh_private_key_file: id_rsa-production
```

## 初期設定

```yaml:init.yml
---
- hosts: test
  tasks:
  - name : add user
    user :
      name             : "{{ ssh_user }}"
      groups           : wheel
      password         : "{{ ssh_user_password }}"
      generate_ssh_key : yes
      ssh_key_bits     : 2048
  - name    : register public key
    command : /bin/cp /home/{{ ssh_user }}/.ssh/id_rsa.pub /home/{{ ssh_user }}/.ssh/authorized_keys
  - name : change permission
    file :
      path  : /home/{{ ssh_user }}/.ssh/authorized_keys
      owner : "{{ ssh_user }}"
      group : "{{ ssh_user }}"
      mode  : 0600
  - name       : allow wheel group to sudo
    lineinfile :
          dest     : /etc/sudoers
          state    : present
          regexp   : "^#\\s*(%wheel\\s+ALL=\\(ALL\\)\\s+NOPASSWD{{':'}}\\s+ALL)"
          line     : "\\1"
          backrefs : yes
          validate : "visudo -cf %s"
          backup   : yes
  - name       : forbid ssh password login
    lineinfile :
      dest     : /etc/ssh/sshd_config
      state    : present
      regexp   : "^PasswordAuthentication yes"
      line     : "PasswordAuthentication no"
      backrefs : yes
      backup   : yes
  - name : permit specific user to ssh login
    lineinfile :
      dest     : /etc/ssh/sshd_config
      state    : present
      regexp   : "^AllowUsers"
      line     : "AllowUsers {{ ssh_user }}"
      backup   : yes
  - name       : change ssh port
    lineinfile :
      dest     : /etc/ssh/sshd_config
      state    : present
      regexp   : "#^Port"
      line     : "Port {{ ssh_port }}"
      backup   : yes
  - name  : fetch private key
    fetch :
      src             : /home/{{ ssh_user }}/.ssh/id_rsa
      dest            : keys/{{ ssh_private_key_file }}
      validate_md5    : yes
      fail_on_missing : yes
      flat            : yes
  - name     : update firewall setting
    template :
      src  : templates/iptables.j2
      dest : /etc/sysconfig/iptables
  - name    : restart services
    command : service sshd restart && service iptables restart
```

sshd, iptablesの再起動にserviceモジュールを使わないのは、別taskだとssh connectionも別になり、閉め出されてしまうためです。

Firewallは、sshポートと80番だけ開けるようにします。

```jinja:templates/iptables.j2
# Firewall configuration written by system-config-firewall
# Manual customization of this file is not recommended.
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport {{ ssh_port }} -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
COMMIT
```

## 管理用userで実行する部分

```yaml:bootstrap.yml
---
- hosts : test
  sudo  : yes
  roles :
    - yum
    - zsh
    - git
    - nginx
    - docker
```

```yaml:roles/yum/tasks/main.yml
---
- name : add epel repository
  yum  :
    name : http://ftp.riken.jp/Linux/fedora/epel/6/x86_64/epel-release-6-8.noarch.rpm
- name : update
  yum  :
    name  : "*"
    state : latest
```

```yaml:roles/zsh/tasks/main.yml
---
- name : install zsh
  yum  :
    name  : zsh
    state : present
- name : change default shell
  user :
    name : "{{ ansible_ssh_user }}"
    shell: /bin/zsh
    state : present
- name : update zshrc
  template:
    src: zshrc
    dest: /home/{{ ansible_ssh_user }}/.zshrc
- name : setup git completion files
  copy :
    src  : zsh
    dest : /home/{{ ansible_ssh_user }}/.zsh
- name : rebuild zcompdump
  command: "rm -f ~/.zcompdump; compinit"
```
templateモジュールは`ロール名/templates`ディレクトリの中、
copyモジュールは`ロール名/files`ディレクトリの中を見に行くので、zsh(ディレクトリ)はfilesに置いています。


```yaml:roles/git/tasks/main.yml
---
- name : install git
  yum  :
    name  : git
    state : present
```

```yaml:roles/nginx/tasks/main.yml
---
- name: add nginx rpm
  yum:
    name: http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm
    state: present
- name: install nginx
  yum:
    name: nginx
    state: present
- name: activate service
  service:
    name: nginx
    state: restarted
    enabled: yes
```

```yaml:roles/docker/tasks/main.yml
---
- name: install docker
  yum:
    name: docker-io
    state: present
- name: activate service
  service:
    name: docker
    state: started
    enabled: yes
```

## 実行手順

### 初期設定
使っているVPSなど環境によって違いますが、22番+パスワードでログインする場合、以下のようなコマンドを叩きます。

```shell-session
$ ansible-playbook init.yml -i hosts -k -c paramiko -e ansible_ssh_port=22 -e ansible_ssh_user=root
```
-kは--ask-pass、-cはssh接続に使用するconnectionの種類です。
defaultのconnectionでは、パスワード認証が許可されないため、paramiko(pythonライブラリ)を指定します。

-eは--extra-varsで、変数を上書きするために使用します。

### 初期設定後
初期設定によって秘密鍵が取得できたので、以降はこれでOKです。

```shell-session
$ ansible-playbook bootstrap.yml -i hosts
```

## 注意点
VPSに適用した際、`AllowUsers {{ ssh_user }}`が`/etc/ssh/sshd_config`のMatchブロックの中に挿入されたため、sshdが起動できなくなりました。

VPSの管理コンソールで入って難を逃れましたが、正規表現で置換する系のタスクは、まず初期状態を確認するか、templateモジュールにしたほうがよいかもしれません。

## まとめ

* roleを分けたが、各タスクで大したことしてないので、あまり意味はなかった。
* テスト用VMは実際のVPSと同じ環境を用意するべき（当たり前か）
* 冪等性を保証するのが難しい。