CentOS6上のAnsible よりWindowsに対してKerberos認証をしようとすると"plaintext: 401 Unauthorized. basic auth failed"エラーがでてしまう。

## はじめに
CentOS6上のAnsibleから、Windows2012Serverに対してアクティブディレクトリユーザーで接続（Kerberos認証）をしようとした際に"plaintext: 401 Unauthorized. basic auth failed"のエラーでハマリましたので備忘も含めて記事にします。
本記事について、長文かつ、なんだかまとまりがなくなってしまったのでどこかでリライトするかもしれません。

## 事象
[公式サイトのWindows Support](http://docs.ansible.com/ansible/intro_windows.html)の通りCentOS6上で以下Kerberos関連の設定を行い、Windows2012Serverへ接続するplaybookを実行すると`"plaintext: 401 Unauthorized. basic auth failed"`と
Kerberos認証ではなくパスワード認証で接続NG（401エラー）となっていました。

``` 
# WinRM　及び　Kerberos関連のモジュール等をインストール

pip install "pywinrm>=0.1.1"

# Installing python-kerberos dependencies
yum -y install python-devel krb5-devel krb5-libs krb5-workstation

# Installing python-kerberos
pip install kerberos

# Configuring Kerberos
# /etc/krb5.conf　を自環境の値に変更
[realms]

 MY.DOMAIN.COM = {
  kdc = domain-controller1.my.domain.com
  kdc = domain-controller2.my.domain.com
 }
[domain_realm]
    .my.domain.com = MY.DOMAIN.COM


# Testing a kerberos connection
kinit -C user@MY.DOMAIN.COM
klist

# Inbentoryhost
[windows]
winserver1.example.com
winserver2.example.com

[windows:vars]
ansible_user=Administrator@
ansible_password=SecretPasswordGoesHere
ansible_port=5985
ansible_connection=winrm
ansible_winrm_server_cert_validation=ignore

```
## 原因
- CentOS6では、Kerberos認証モジュールが動作していませんでした。
  - 具体的にはCentOS6のPython Ver.2.6ではpipでインストールしたKerberosのモジュールがうまく動作していない（import等に失敗）ことが分かりました。

## 対策
以下いずれかの方法でPythonのバージョンを2.7.xに上げてから`pip install kerberos`を行います。

1. AnsibleホストをCentOS6（Python Ver.2.6）ではなく、CentOS7（Python Ver.2.7.5）にする。
   + CentOS7で[公式サイトのWindows Support](http://docs.ansible.com/ansible/intro_windows.html)の通り設定を行ったところ、問題なくKerberos認証がうまくいきました。
2. CentOS6上でpynevを使いPython Ver.を2.7.8以降(注1)にする。
   + CentOS6にて、2.7系のPythonを別途インストールしたのですが、pipとの依存関係等でAnsible含めてうまく動作させることができなかったのでpyenvを利用しました。
   + 注1：当初CentOS7と同じバージョン2.7.5をインストールしましたが、Ansibleで必要なparamiko（pythonモジュール）がこのバージョンのpyenvではインストールできないことが分かったため、2.7.8をいれています。
     + こちらに[pyenvをインストール・設定するAnsible playbook](http://qiita.com/tbuchi888/items/d44d15dcc6f63ae440d9)のQiita記事を書きましたので参考まで。

## おまけ
### `/etc/krb5.conf`やインベントリファイルの設定例
`klist`の結果`Default principal:`に両ファイルを合わせるのコツです。
特に.LOCALなど大文字小文字区別されます。

以下kinit ,　/etc/krb5.conf　, インベントリファイルの設定例

```
[root@AnsibleSV krbs_test]# kinit foo@MYDOMAIN.local
Password for foo@MYDOMAIN.local:
kinit: KDC reply did not match expectations while getting initial credentials
[root@AnsibleSV krbs_test]# kinit -C foo@MYDOMAIN.local
Password for foo@MYDOMAIN.local:
[root@AnsibleSV krbs_test]# klist
Ticket cache: FILE:/tmp/krb5cc_0
Default principal: foo@MYDOMAIN.LOCAL

Valid starting     Expires            Service principal
06/01/16 17:30:17  06/02/16 03:30:18  krbtgt/MYDOMAIN.LOCAL@MYDOMAIN.LOCAL
        renew until 06/08/16 17:30:17
[root@AnsibleSV krbs_test]# cat /etc/krb5.conf
[logging]
 default = FILE:/var/log/krb5libs.log
 kdc = FILE:/var/log/krb5kdc.log
 admin_server = FILE:/var/log/kadmind.log

[libdefaults]
 default_realm = MYDOMAIN.LOCAL
 dns_lookup_realm = false
 dns_lookup_kdc = false
 ticket_lifetime = 24h
 renew_lifetime = 7d
 forwardable = true

[realms]
 MYDOMAIN.LOCAL = {
  kdc = 192.168.200.1
  admin_server = 192.168.200.1
 }

[domain_realm]
 .mydomain.local = MYDOMAIN.LOCAL
 mydomain.local = MYDOMAIN.LOCAL
[root@AnsibleSV krbs_test]# cat krbs_host
[win]
win01.MYDOMAIN.LOCAL
win02.MYDOMAIN.LOCAL

[win:vars]
ansible_user=foo@MYDOMAIN.LOCAL
ansible_password=SecretPassword
ansible_port=5985
ansible_connection=winrm
ansible_winrm_transport=kerberos
ansible_winrm_server_cert_validation=ignore
```

### トラブルシュート方法
+ python kerberosモジュールが動作するか試す一例としては、Ansible Serverへコマンドライン上で以下コマンドの結果としてなにもエラーがでなければ、おそらくインストールはうまくいっているので、krb5.confやインベントリファイルの設定の問題の可能性が高いです。

以下エラーの例。

```
# python
Python 2.6.6 (r266:84292, Jul 23 2015, 15:22:56)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-11)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import kerberos
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: /usr/lib64/python2.6/site-packages/kerberos.so: undefined symbol: PyCapsule_CheckExact
>>>
# Ctrl + d でpythonを終了
```

ちなみに上記エラー状態で`pip list`や`pip freeze`を行った結果が以下です。

```
# pip list | grep kerberos
DEPRECATION: Python 2.6 is no longer supported by the Python core team, please upgrade your Python. A future version of pip will drop support for Python 2.6
kerberos (1.2.4)
# pip freeze | grep kerberos
DEPRECATION: Python 2.6 is no longer supported by the Python core team, please upgrade your Python. A future version of pip will drop support for Python 2.6
kerberos==1.2.4
```


+ `kinit -C user@MY.DOMAIN.COM` や`klist`でAnsibleServerとWindows間でKerberos認証ができるか確認します。Ansible/pythonとの切り分け。
  + なお、チケットには有効期限（`krb5.conf`の`ticket_lifetime`で設定 また、`klist`結果のExpiresで確認可能）があるので注意。有効期限が切れた場合は、再度チケットを発行。

+ デフォルトでは、接続ユーザー名に@が入っている場合、kerberos認証を試し、エラーの場合、パスワード認証を行うようです。エラー内容がパスワード認証のままの場合は、インベントリファイルに`ansible_winrm_transport=kerberos`を追加して、明示的にkerberos認証のみで接続を試みるとエラーメッセージがkerberos認証のもに変わるのでメッセージに沿った対応(python kerberosモジュールのインストール問題かkrb5.confやインベントリファイルの設定の問題かどうかがわかります)をします。

## 参考

### 環境
|サーバ種別|種類|ver.|
|:-:|:-:|:-:|
|Ansible Server1|OS|CentOS 6.7|
||Ansible| ansible 2.0.2.0 yum install |
||Python| Python 2.6.6 →　pyenv 2.7.8|
|||pip 8.1.2 (python 2.6) → pip 8.1.2 (python 2.7.8)|
|Ansible Server2|OS|CentOS 7|
||Ansible| epel yum install |
||Python| 2.7.5 |
|||pip 8.1.2 (python 2.7.5)|
|Ansible Client|OS|Windows2012|
||OS|Windows2012R2|

### 実行結果エラー時

#### テスト用プレイブック
```
[root@AnsibleSV krbs_test]# cat krbs_test.yml
---
- hosts: win
  gather_facts: no
  vars:
    CMD:
      - cmd: CMD /c "dir"
  tasks:
    - name: "CMD TEST"
      raw:  "{{ item.cmd }}"
      register: CMD_RESULT
      with_items: '{{ CMD }}'

    - name: "debug: output CMD results"
      debug: var=CMD_RESULT.results

```

#### `"plaintext: 401 Unauthorized. basic auth failed"`エラー
`ansible_winrm_transport=kerberos`指定なし　、　Python Ver.2.6

```
[root@AnsibleSV krbs_test]# ansible-playbook -i krbs_host krbs_test.yml

PLAY [win] *********************************************************************

TASK [CMD TEST] ****************************************************************
fatal: [win01.MYDOMAIN.LOCAL]: FAILED! => {"failed": true, "msg": "plaintext: 401 Unauthorized. basic auth failed"}
fatal: [win02.MYDOMAIN.LOCAL]: FAILED! => {"failed": true, "msg": "plaintext: 401 Unauthorized. basic auth failed"}

NO MORE HOSTS LEFT *************************************************************
        to retry, use: --limit @krbs_test.retry

PLAY RECAP *********************************************************************
win01.MYDOMAIN.LOCAL        : ok=0    changed=0    unreachable=0    failed=1
win02.MYDOMAIN.LOCAL        : ok=0    changed=0    unreachable=0    failed=1

[root@AnsibleSV krbs_test]#

```

#### "kerberos: the python kerberos library is not installed"エラー
インベントリファイルへ`ansible_winrm_transport=kerberos`を追加して
明示的にkerberos認証のエラーメッセージを出力。

```
[root@AnsibleSV krbs_test]# ansible-playbook -i krbs_host krbs_test.yml

PLAY [win] *********************************************************************

TASK [CMD TEST] ****************************************************************
fatal: [win02.MYDOMAIN.LOCAL]: FAILED! => {"failed": true, "msg": "kerberos: the python kerberos library is not installed"}
fatal: [win01.MYDOMAIN.LOCAL]: FAILED! => {"failed": true, "msg": "kerberos: the python kerberos library is not installed"}

NO MORE HOSTS LEFT *************************************************************
        to retry, use: --limit @krbs_test.retry

PLAY RECAP *********************************************************************
win01.MYDOMAIN.LOCAL        : ok=0    changed=0    unreachable=0    failed=1
win02.MYDOMAIN.LOCAL        : ok=0    changed=0    unreachable=0    failed=1

[root@AnsibleSV krbs_test]# 
```