AnsibleでUbuntuにWebサーバを構築する手順【Nginx使用】

Ansibleを使ってWebサーバを構築します。
要件は下記のとおりです。

1. ローカルホストをターゲットノードとする
1. MWはNginxを用いる
1. 自己証明書を用いてhttps化する

この要件を満たすようにplaybookを作成します。
具体的な処理は以下一覧

1. Nginxをインストールする
1. pipをインストールする
1. pyOpenSSLをインストールする
1. 自己証明書配置先ディレクトリを作成
1. 秘密鍵を作成
1. 変数読み込み
1. CSR(証明書要求)を作成
1. 証明書を作成
1. Nginxの設定ファイルを配置
1. Nginxを再起動

※ aptは既にインストールされているという前提です。

## 使用環境
・ ゲストOS: Windows10 Home
・ ホストOS: Ubuntu20.04(Ansibleを実行するホストです)
・ Ansible: 2.9.6


## ディレクトリ構成

```bash
/etc/ansible/
│  ## playbook本体  
├ test1.yml
├ files
   │  ## CSR発行時に使用する変数 
   ├ certificate_vars.ini
   │  ## nginxの設定ファイル
   └ nginx_ssl.conf

``````

playbookのtaskがroleに分割されていない、ファイルが用途に関わらず同じディレクトリに配置されているなど典型的なアンチパターンになっていますが、お試しということでひとまずこれでいきます。


## 実装例

```test1.yml
---
- hosts: localhost
  become: yes
  # 1. Nginxをインストールする
  tasks:
    - name: Install Nginx
      apt:
        name: nginx 
        state: latest

  # 2. pipをインストールする
    - name: Install pip
      apt: 
        name: pip
        state: latest

  # 3. pyOpenSSLをインストールする(鍵発行用)
    - name: Install pyOpenSSL
      pip:
        name:
          - pyOpenSSL

  # 4. 自己証明書配置先ディレクトリを作成
    - name: nginx ssl directory
      file: path=/etc/nginx/ssl state=directory owner=root group=root mode=00755

  # 5. 秘密鍵を作成
    - name: Create Private Key
      openssl_privatekey:
        path: /etc/nginx/ssl/server.key
        state: present

  # 6. 変数読み込み
    - name: Include Vars File
      include_vars: "/etc/ansible/files/certificate_vars.ini"

  # 7. CSR(証明書要求)を作成
    - name: create CSR
      openssl_csr:
        commonName: "{{ca_cn}}"
        countryName: "{{ca_c}}"
        emailAddress: "{{ca_email}}"
        localityName: "{{ca_l}}"
        organizationName: "{{ca_o}}"
        organizationalUnitName: "{{ca_ou}}"
        stateOrProvinceName: "{{ca_st}}"
        path: /etc/nginx/ssl/server.csr
        privatekey_path: /etc/nginx/ssl/server.key
        state: present
    
    # 8. 証明書を作成
    - name: ensure self signed certificate
      openssl_certificate:
        csr_path: /etc/nginx/ssl/server.csr
        path: /etc/nginx/ssl/server.crt
        privatekey_path: /etc/nginx/ssl/server.key
        provider: selfsigned
        state: present

    # 9. Nginxの設定ファイルを配置
    - name: deploy nginx_ssl.conf
      copy:
        src: /etc/ansible/files/nginx_ssl.conf
        dest: /etc/nginx/conf.d

    # 10. Nginxを再起動
    - name: Start Nginx
      service:
        name: nginx
        state: started
```

```ini:/etc/ansible/files/certificate_vars.ini
---
#commonName
ca_cn: oreoreCA.fugafuga

#countryName
ca_c: JP

#emailAddress
ca_email: fugafuga@example.com

#localityName
ca_l: Shibuya

#orgName
ca_o: hoge company

#orgUnitName
ca_ou: hoge division

#PrefName
ca_st: Tokyo

```

```conf:/etc/ansible/files/nginx_ssl.conf
server {
    # 443番ポートを許可し、SSL機能をON
    # listen 80;
    listen 443 ssl;

    # 証明書を設定
    ssl_certificate     /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;
}
```

playbookと各種ファイルを作成し、以下のコマンドを実行します。

```bash
cd /etc/ansible
ansible-playbook /etc/ansible/test1.yml

[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit
localhost does not match 'all'

PLAY [localhost] ******************************************************************************

TASK [Gathering Facts] ************************************************************************
ok: [localhost]

TASK [Install Nginx] **************************************************************************
ok: [localhost]

TASK [Install pip] ****************************************************************************
ok: [localhost]

TASK [Install pyOpenSSL] **********************************************************************
ok: [localhost]

TASK [nginx ssl directory] ********************************************************************
ok: [localhost]

TASK [Create Private Key] *********************************************************************
ok: [localhost]

TASK [Include Vars File] **********************************************************************
ok: [localhost]

TASK [create CSR] *****************************************************************************
ok: [localhost]

TASK [ensure self signed certificate] *********************************************************
ok: [localhost]

TASK [deploy nginx_ssl.conf] ******************************************************************
ok: [localhost]

TASK [Start Nginx] ****************************************************************************
changed: [localhost]

PLAY RECAP ************************************************************************************
localhost                  : ok=11   changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  
```

failed=0になればokです。

## 使用モジュール

### ① aptモジュール

##### 用途
aptパッケージを管理します。

##### 主なパラメータ

|パラメータ  |主な値  |説明  |
|---|---|---|
|name |対象パッケージ  |インストールする <br>パッケージを指定|
|state  |present,absent,latest |パッケージの状態を指定  |


### ② fileモジュール

ファイルやディレクトリを操作します。
よく使いそう

##### 主なパラメータ

|パラメータ  |主な値  |説明  |
|---|---|---|
|path |任意のディレクトリ  |ファイルを作成・配置するパス|
|state|file,directory,link  |作成するリソースの種類|
|mode|0755, u+rwx|ファイルの権限を指定|

### ③ openssl_privatekeyモジュール

秘密鍵を管理します。(使用にあたってpyOpenSSLパッケージが必要です)

##### 主なパラメータ

|パラメータ  |主な値  |説明  |
|---|---|---|
|path |任意のディレクトリ  |鍵の名前・配置先|
|backup|no,yes  |上書きする際にバックアップを作成するかどうか|
|mode|0755, u+rwx|ファイルの権限を指定|
|size|2024,4096|鍵の大きさを指定|
|type|RSA, DSA|鍵生成時のアルゴリズム。<br>デフォルトではRSA|


### ④ openssl_csrモジュール

CSR(証明書要求)を作成します。
※ pyOpenSSLが必要です

##### 主なパラメータ

|パラメータ|主な値|説明|
|---|---|---|
|path|任意のディレクトリ|CSRの配置先|
|privatekey_path|秘密鍵のパス|証明書要求を送る際に使う<br>秘密鍵のパス|


### ⑤ openssl_certificateモジュール

サーバ証明書を作成します。
※pyOpenSSLが必要です

##### 主なパラメータ

|パラメータ|主な値|説明|
|---|---|---|
|csr_path|CSRのパス|証明書要求のパス|
|privatekey_path|秘密鍵のパス|秘密鍵のパス|
|provider|selfsigned, ownca|証明書の発行元|

#### ⑥ copyモジュール

ファイルをターゲットノードにコピーします。
これもよく使いそう

###### 主なパラメータ

|パラメータ|主な値|説明|
|---|---|---|
|src|任意のファイル|コピーするファイル|
|dest|任意のディレクトリ|コピー先ディレクトリ|
|remote_src|yes,no|yesの場合、ターゲットノード間でファイルをコピーする|



## まとめ

Ansibleを使うとサーバの状態がコードで管理されるので便利です。

設定ファイルの書き換えや作成については、srcモジュールでコントロールノードから配置するのか
文字列置換をするのがよいか分かりません。(そのあたりのベストプラクティスをまた調べる必要がありそうです。)