AnsibleのCopyモジュールでsudoers.d配下にファイル追加をしたらroot権限がお亡くなりになった

こんなエラーが吐かれたあと、sudo権限がお亡くなりになった。

```
TASK [users : copy sudoers] *****************************************************************************************************************************************************************changed: [ipaddress] => (item={u'dest': u'.', u'src': u'roles/users/files/usersfile'})
fatal: [ipaddress]: FAILED! => {"failed": true, "msg": "Failed to get information on remote file (/etc/sudoers.d/.): Shared connection to ipaddress closed.\r\n"}
        to retry, use: --limit @xxx.retry
```


ansibleの公式を見るとわかるが、sodoers関連の管理はlineinfileモジュールを使うのが定石のよう。

```
# Validate the sudoers file before saving
- lineinfile:
    path: /etc/sudoers
    state: present
    regexp: '^%ADMIN ALL='
    line: '%ADMIN ALL=(ALL) NOPASSWD: ALL'
    validate: 'visudo -cf %s'
```

> http://docs.ansible.com/ansible/lineinfile_module.html

# で、原因は？
諸説あり。今後、正確に特定したい。
今回確認したところをメモ。

## 追記　2017/5/16
**改行コードが原因だった。**
MacとWindowsとでは改行コードが異なる。
windowsの改行コードはCRLFだ。

> [備忘録：改行コード「LF」と「CR」と「CRLF」の違い](http://marusunrise2.blogspot.jp/2014/06/lfcrcrlf.html)

### 解決策

* Windowsで使用していたAtomの設定を変える

「OSデフォルトの文字コードを使用する」設定になっていた。
なので、Atomユーザは以下の記事を参考にして**改行コード設定をLFに変換**しておく。

> [Atomエディタ上からファイルを新規作成する際のデフォルトの改行コードを設定する（Windows環境）]
(http://qiita.com/nyoro_712/items/8c80e32aa49f7a2d2eaf)

* .gitconfigで改行コードの設定をする

MacからPushしたコードの改行コードはLFなのに、windowsでPullしたコードはCRLFになってしまったりするので、設定を追記しておく。

> [windows環境の git で改行コードの自動変換に注意](http://qiita.com/yokoh9/items/1ec8099696ade0c1f36e)

* 試しに新規作成したファイルの改行コードを確認

以下のコマンドで改行コードを確認できる。
$がついていれば改行コードLF。

```
$ cat -e <filename>
```

また、**Atomユーザは右下のステータスで簡単に改行コードを確認・変更ができる。**

## sudoers.d配下のファイルアクセス権が変わった？
検証環境のサーバで試しにアクセス権を640に変更して、そのユーザがsudoに**なることができない**か試したが、sudoが使用できたので違うっぽい。

## ソースコードをvisudo -cfしたらsyntax errorだった
いやもうこれだろって感じ。ただ、ファイルの内容は既存のファイルからコピーしたものだった。

## bash on Windowsから実行したことによる文字コード
上記のsyntax errorが出ていた理由はこれではないか。
サーバに設定されていた文字コードは```ASCII (LF)```
失敗した際の文字コードは```ASCII (CRLF)```だった。
実行環境（Windows、Mac、Linuxなど）によって差分がある説。

# 対応策
* Copyモジュールでsudoers.d/にファイルを置くのではなく/etc/sudoersに直接記述させる
* syntaxチェックをするコマンドを叩かせる
* 万が一root権限が死んでもvisudo出来る方法を確立させる

## Copyモジュールでsudoers.d/にファイルを置くのではなく/etc/sudoersに直接記述させる
冒頭の通り、lineinfileモジュールとregexpを使って記述するように変える。

## syntaxチェックをするコマンドを叩かせる

まるまる引用で申し訳ないが、commandモジュールを使って万が一に備える方法って感じ。
**Ansible的にはcommand、Shellモジュールを使うのは冪等性が保てないので、やるとしても最終手段という認識。**
そして今回はその最終手段を使うべきとき。

```
tasks:

        - name: Copy sudoers file for safety
          command: cp -f /etc/sudoers /etc/sudoers.tmp

        - name: Create sudoers file backup
          command: cp -f /etc/sudoers /etc/sudoers.bak

        - name: Create admins group
          group: name=admins system=yes state=present

        - name: make sure we can sudo as admin group
          lineinfile: dest=/etc/sudoers.tmp state=present regexp='^%admin' line='%admin ALL=(ALL) ALL'

        - name: also make sure ssh-agent works via sudo
          lineinfile: dest=/etc/sudoers.tmp state=present regexp='^Defaults env_keep\+\=SSH_AUTH_SOCK' line='Defaults env_keep+=SSH_AUTH_SOCK'

        - name: Final sudoers file check
          shell: visudo -q -c -f /etc/sudoers.tmp && cp -f /etc/sudoers.tmp /etc/sudoers
```

> [sudoers safety and sanity checking in playbook](https://raymii.org/s/tutorials/Ansible_-_Sudo_Safety_and_Sanity_Checks.html)

## 万が一root権限が死んでもvisudo出来る方法を確立させる
root権限がない時点でお手上げ。と思いきや、探せばあるもので。
```pkexec visudo```コマンドでなんとスーパーユーザでvisudoを実行できるらしい。
ただ、この```pkexec```は予めインストールしてないと打てない・・・。**今回の件のように、お亡くなりになってからでは遅いので注意。**
今回はスナップショットで復元することになった・・・。これがあればサーバを止めなくてもよかったなあ。

> [/etc/sudoersがsyntax errorでsudo使えなくなった時の対処法](http://hirayami.hatenablog.jp/entry/2013/05/12/110931)

# おわりに
sudo関連は怖い。と、同時に、万が一に備えてrootになる方法を把握しておかなければならないなと思った。
「amazonLinuxはデフォルトでrootユーザになれないので、cfgをいじる必要がある」とかも知らなかった。