ansibleで複数ユーザ管理（アクセス権付与）をやってみた



## 前提

- すでに仮想環境(ターゲットホスト)を`vagrant`で構築済である。
- `ansible`はインストールしているが実行させるプレイブックがない。

## やりたいこと

`user01`は、`user02`配下にアクセス権限はあるが、
`user02`は、`user01`配下にアクセス権限がない状態を実現したい。

グループ`sample2`に、`user01`を追加する。

```bash
[root@websvr ~]# usermod -aG sample2 user01
```

確認

```bash
[root@websvr ~]# groups user01
user01 : sample1 sample2
[root@websvr ~]# groups user02
user02 : sample2
```

また、`user01`に対する`user02`配下のアクセス権限はファイルやディレクトリのRead権限のみとしたいので、
`user02`ディレクトリに対して、以下を実施する。

```bash
[root@websvr ~]# chmod 750 /home/user02
[root@websvr ~]# ll /home/
total 4
drwx------.  3 user01  sample1   78 Jan  1 14:56 user01
drwxr-x---.  3 user02  sample2   78 Jan  1 14:56 user02
drwx------. 17 vagrant vagrant 4096 Dec 31 19:31 vagrant
```

グループに対して、`x`を付与してやらないと、`user01`が`/home/user02`に移動出来ない。

`user01`が`user02`作成のファイルを削除出来ないことを確認出来た。

```bash
[user01@websvr user02]$ whoami
user01
[user01@websvr user02]$ pwd
/home/user02
[user01@websvr user02]$ ll
total 0
-rw-r--r--. 1 user02 sample2 0 Jan  1 15:03 test.txt
[user01@websvr user02]$ rm test.txt
rm: remove write-protected regular empty file ‘test.txt’? y
rm: cannot remove ‘test.txt’: Permission denied
```

このようなことを`ansible`で実現したい。

## 公開鍵の登録

コントロールノード（ホスト環境）から、ターゲットノード（ゲスト環境）に`ssh`でパスワード無しにログインする設定を実施する。
コントロールノードで作成した公開鍵をターゲットノードに登録する。

```bash
ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key ($HOME/.ssh/id_rsa):
$HOME/.ssh/id_rsa already exists.
Overwrite (y/n)? n
```

すでに作成してあったので、中断する。
公開鍵をターゲットノードに登録する。

```bash
ssh-copy-id vagrant@192.168.33.10
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "$HOME/.ssh/id_rsa.pub"
The authenticity of host '192.168.33.10 (192.168.33.10)' can't be established.
ECDSA key fingerprint is SHA256:mjGym7gkqWjPvW2JXhKjqWl4XC6wuhgNIukldSVtkFk.
Are you sure you want to continue connecting (yes/no)? yes
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
vagrant@192.168.33.10's password:

Number of key(s) added:        1

Now try logging into the machine, with:   "ssh 'vagrant@192.168.33.10'"
and check to make sure that only the key(s) you wanted were added.
```

パスワード無しでログイン出来ることを確認する。

```bash
ssh vagrant@192.168.33.10
Last login: Tue Dec 31 18:41:18 2019 from 10.0.2.2
[vagrant@websvr ~]$ exit
ログアウト
Connection to 192.168.33.10 closed.
```

## `ansible.cfg`の設定

コントロールノード側で`ansible.cfg`を用意する。
`ansible.cfg`は個別の設定をする事があるため、以下に作成する。

```bash
pwd
$HOME/Desktop/workspace/ansible/ansible-study
touch ansible.cfg
```

`.ansible.cfg`に以下の通り設定する。

```bash
[defaults]
forks = 10
log_path = $HOME/.ansible/ansible.log
host_key_checking = False
gathering = smart
inventory = ./inventory.ini
remote_user = vagrant
private_key_file = /path/vagrant_private_key

[ssh_connection]
```

上記の`private_key_file`は以下の`IdentityFile`から取得する。

```bash
cd $HOME/Desktop/workspace/vagrant
vagrant ssh-config
Host default
  HostName 127.0.0.1
  User vagrant
  Port 2222
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile /path/vagrant_private_key
  IdentitiesOnly yes
  LogLevel FATAL
```

## インベントリの準備

ターゲットノードの情報を`inventory.ini`に記載する。

```bash
touch inventory.ini
cat inventory.ini
[test_servers]
192.168.33.10
```

### `ansible`コマンドの実行

ターゲットノードに`ping`コマンドを実行する。

```bash
$ ansible -i inventory.ini test_servers -m ping -u vagrant
192.168.33.10 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
```

`ansible.cfg`に必要な情報を記載しているため、以下のコマンドでも良い。

```bash
$ ansible test_servers -m ping
192.168.33.10 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
```

## ターゲットノードのロールバック準備

ターゲットノードをロールバック出来るように、以下を実施する。
プレイブック実行時に失敗しても何度もやり直せる。

```bash
$ vagrant sandbox status
[default] Sandbox mode is off
```

設定を有効化

```bash
$ vagrant sandbox on
[default] Starting sandbox mode...
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
```

設定確認

```bash
$ vagrant sandbox status
[default] Sandbox mode is on
```
## プレイブック実行


上記のやりたいことを実現させるプレイブックは以下の通りとなる。

```yaml:create_user.yml
---
- hosts: test_servers
  become: yes
  vars_files:
    - user_list.yml
  tasks:
    - name: Create group
      group:
        name: "{{ item.name }}"
      with_items:
        - "{{ group_list }}"
      when: group_list

    - name: Create user
      user:
        name: "{{ item.name }}"
        group: "{{ item.group }}"
        groups: "{{ item.groups }}"
        password: "{{ item.password }}"
        shell: /bin/bash
        state: present
      with_items:
        - "{{ users_list }}"
      when: users_list

    - name: Permission of directory /home/user02 is '0750'
      file:
        dest: /home/{{ item.name }}
        mode: "0750"
      with_items:
        - "{{ subordinate_authority }}"
```

```yaml:user_list.yml
group_list:
  - { name: sample1 }
  - { name: sample2 }

users_list:
  - {
      name: "user01",
      group: "sample1",
      groups: "sample1, sample2",
      password: "{{ 'password'|password_hash('sha512') }}",
      comment: "user01",
    }
  - {
      name: "user02",
      group: "sample2",
      groups: "sample2",
      password: "{{ 'password'|password_hash('sha512') }}",
      comment: "user02",
    }

superior_authority:
  - { name: user01 }

subordinate_authority:
  - { name: user02 }
```

実行する。

```bash
ansible-playbook create_user.yml

PLAY [test_servers] *************************************************************************************************************************************************************************************************************************

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

TASK [Create group] *************************************************************************************************************************************************************************************************************************
[DEPRECATION WARNING]: evaluating [{'name': 'sample1'}, {'name': 'sample2'}] as a bare variable, this behaviour will go away and you might need to add |bool to the expression in the future. Also see CONDITIONAL_BARE_VARS configuration
toggle.. This feature will be removed in version 2.12. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
changed: [192.168.33.10] => (item={'name': 'sample1'})
changed: [192.168.33.10] => (item={'name': 'sample2'})

TASK [Create user] **************************************************************************************************************************************************************************************************************************
[DEPRECATION WARNING]: evaluating [{'name': 'user01', 'group': 'sample1', 'groups': 'sample1, sample2', 'password': '$6$cuuPF7HsudjoJ9/v$G1zztM4ZLPs6UJk/fxwgjbOAkXJ32pszjjw.J.M2t.KTlO99Bus2D1AzucT.872WBs7oqp.RfOIEjf187X6cR1', 'comment':
 'user01'}, {'name': 'user02', 'group': 'sample2', 'groups': 'sample2', 'password': '$6$cuuPF7HsudjoJ9/v$G1zztM4ZLPs6UJk/fxwgjbOAkXJ32pszjjw.J.M2t.KTlO99Bus2D1AzucT.872WBs7oqp.RfOIEjf187X6cR1', 'comment': 'user02'}] as a bare variable,
this behaviour will go away and you might need to add |bool to the expression in the future. Also see CONDITIONAL_BARE_VARS configuration toggle.. This feature will be removed in version 2.12. Deprecation warnings can be disabled by
setting deprecation_warnings=False in ansible.cfg.
changed: [192.168.33.10] => (item={'name': 'user01', 'group': 'sample1', 'groups': 'sample1, sample2', 'password': '$6$VpS/Wgs9fy5KDTI8$ZXwfSsnz4jeQ6rAOX4X8HP4xu3ndeVtasHiY0SOaeZoYUvPU3CQQQ4ww3y6VfEJAlS4jJPpXn7rRxUljY.Sc60', 'comment': 'user01'})
[DEPRECATION WARNING]: evaluating [{'name': 'user01', 'group': 'sample1', 'groups': 'sample1, sample2', 'password': '$6$wjPKJca0K2oKgwFY$5JzcugEn1.kc1KXN9XYBJVub9e.AzUT28S4ZOGy.vwKhRVkSB9dqUjUcs/sqRPf8kDG94nqAT3.S8YaA3cime1', 'comment':
 'user01'}, {'name': 'user02', 'group': 'sample2', 'groups': 'sample2', 'password': '$6$wjPKJca0K2oKgwFY$5JzcugEn1.kc1KXN9XYBJVub9e.AzUT28S4ZOGy.vwKhRVkSB9dqUjUcs/sqRPf8kDG94nqAT3.S8YaA3cime1', 'comment': 'user02'}] as a bare variable,
this behaviour will go away and you might need to add |bool to the expression in the future. Also see CONDITIONAL_BARE_VARS configuration toggle.. This feature will be removed in version 2.12. Deprecation warnings can be disabled by
setting deprecation_warnings=False in ansible.cfg.
changed: [192.168.33.10] => (item={'name': 'user02', 'group': 'sample2', 'groups': 'sample2', 'password': '$6$VpS/Wgs9fy5KDTI8$ZXwfSsnz4jeQ6rAOX4X8HP4xu3ndeVtasHiY0SOaeZoYUvPU3CQQQ4ww3y6VfEJAlS4jJPpXn7rRxUljY.Sc60', 'comment': 'user02'})

TASK [Permission of directory /home/user02 is '0750'] ***************************************************************************************************************************************************************************************
changed: [192.168.33.10] => (item={'name': 'user02'})

PLAY RECAP **********************************************************************************************************************************************************************************************************************************
192.168.33.10              : ok=4    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```

ターゲットノードの実行結果は以下の通り。

```bash
[root@websvr ~]# ll /home/
total 4
drwx------.  3 user01  sample1   78 Jan  1 15:11 user01
drwxr-x---.  3 user02  sample2   78 Jan  1 15:11 user02
drwx------. 17 vagrant vagrant 4096 Dec 31 19:31 vagrant
```

```bash
[root@websvr ~]# cat /etc/group | grep user01
sample1:x:1002:user01
sample2:x:1003:user01,user02
```

`user02`でファイルを作成する。
その後、`user01`になり、アクセス権を確認する。

```bash
[root@websvr ~]# su - user02
[user02@websvr ~]$ touch test.txt
[user02@websvr ~]$ ll
total 0
-rw-r--r--. 1 user02 sample2 0 Jan  1 15:32 test.txt
[user02@websvr ~]$ su - user01
Password: 
Last login: Wed Jan  1 15:32:11 UTC 2020 on pts/0
[user01@websvr ~]$ cd /home/user02/
[user01@websvr user02]$ ll
total 0
-rw-r--r--. 1 user02 sample2 0 Jan  1 15:32 test.txt
```

ファイルも消せない。

```bash
[user01@websvr user02]$ rm test.txt 
rm: remove write-protected regular empty file ‘test.txt’? y
rm: cannot remove ‘test.txt’: Permission denied
```

## 参考

- [Vagrant+Ansible 環境構築メモ](https://qiita.com/snaka/items/d5a8004afbfe665e1d3a)
- [ansible.cfgの項目をリスト化してみた](https://qiita.com/croissant1028/items/33f06298d7d05bf1e295)
- [AnsibleでLinuxユーザ作成](https://qiita.com/sunack/items/22ca52254b4fbf4caac4)