Ansibleをコンテナで準備するのにドハマりした

■はじめに

以前のエントリの通り、macだとAnsibleの準備は簡単です。
が、諸事情によりWindowsで準備をしないとならない状況となりました。

WSL2で入れようかなとも思ったのですがalpineで比較的簡単に準備できそうなので、コンテナで準備することにしました。

が。。
いろんな要因が折り重なって解決まで丸2日かかってしまった。。

ハマりポイントを整理します。

■前提

AzureVM(ターゲットノード)

コンテナ(コントロールノード)

  • Docker Desktop (Docker Engine v.19.03.12)
  • alpine 3.12.0
  • Ansible 2.9.9

■まずやったこと

ターゲットノードの準備

まずVMを用意します。
実はここ最近仕様が若干変わったようで、以前はなかった(と思う)構築時のSSHデフォルト適用が入るようになっています。

f:id:theboyalex:20200805172305p:plain

自然な流れで認証はSSHを選択します。
VMを作ると、そのまま認証キー(pemファイル)がローカルにダウンロードされます。

f:id:theboyalex:20200805172943p:plain

DNS名はselenoid-qa-example-01とします。

f:id:theboyalex:20200805173916p:plain

コントロールノードの準備

以下のページを参考にDockerfileを準備します。

wiki.alpinelinux.org

公開鍵がローカルに払い出されたので、それを使用してアクセスすればいけるんじゃないかな?
ということで、通常の方法(Ansible内で発行した公開鍵をターゲットノードに配置する方法)をとらず、Dockerのv18.03で実装されたBuildkitを使用して認証キーをコンテナに配置して、これをAnsibleで使おうと思いました

# syntax = docker/dockerfile:1.1-experimental
FROM alpine:3.12.0

RUN set -x \
  && apk add --no-cache bash \
  && apk add --no-cache ansible \
  && apk add --no-cache python3 \
  && apk add --no-cache openssh-client \
  && mkdir -p /etc/ansible \
  && mkdir -p -m 700 ~/.ssh
  && ssh-keyscan selenoid-qa-example-01.eastus.cloudapp.azure.com >> ~/.ssh/known_hosts

RUN --mount=type=secret,id=ssh,target=~/.ssh/test_key.pem
RUN cd /etc/ansible
RUN ansible -i inventory_node.ini selenoid-qa-example-01.eastus.cloudapp.azure.com -m ping
#RUN ansible-playbook -i inventory.ini playbook_node.yml

■実行してみる

その1:VMから払い出されたpemで動かす

実はこのDockerfile、問題なくビルドされるのですが正しく動きません。
(ログも出ない)

なので実際にdocker execで潜ってpingを打つとPermission denied (publickey)になります。
どうにも公開鍵が認識されない。

selenoid-qa-example-01.eastus.cloudapp.azure.com | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: Warning: Permanently added the ECDSA host key for IP address 'VMのIP' to the list of known hosts.\r\nazureuser@selenoid-qa-example-01.eastus.cloudapp.azure.com: Permission denied (publickey).",
    "unreachable": true
}

試しにpemをCOPYコマンドでコンテナ内でコピーし、そこからsshコマンドを実行すると普通に起動されます。

bash-5.0# ssh -i ~/.ssh/test_key.pem azureuser@selenoid-qa-example-01.eastus.cloudapp.azure.com
load pubkey "/root/.ssh/test_key.pem": invalid format
Welcome to Ubuntu 18.04.4 LTS (GNU/Linux 5.3.0-1034-azure x86_64)
(中略)
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

azureuser@test:~$ 

つまりAnsible経由ではVM作成時に発行した公開鍵は使えませんでした
この事象が分かるまで半日ぐらい費やしてしまった。。

その2:Ansible内で公開鍵を発効して配布

さて次にやるのはAnsibleのオーソドックスな公開鍵配布方法です。
コントロールノードで発効し、ターゲットノードに配布する方法。

Dockerfileを上記サイトにあるコマンドを追加して、以下のように書き換えます。

# syntax = docker/dockerfile:1.1-experimental
FROM alpine:3.12.0

RUN set -x \
  && apk add --no-cache bash \
  && apk add --no-cache ansible \
  && apk add --no-cache python3 \
  && apk add --no-cache openssh-client \
  && mkdir -p /etc/ansible \
  && mkdir -p -m 700 ~/.ssh \
  && ssh-keyscan selenoid-qa-example-01.eastus.cloudapp.azure.com >> ~/.ssh/known_hosts

COPY inventory_node.ini /etc/ansible
COPY playbook_node.yml /etc/ansible
COPY ansible.cfg /etc/ansible

RUN ssh-keygen -q -N '' -t ed25519 -f ~/.ssh/id_ed25519
RUN ssh azureuser@selenoid-qa-example-01.eastus.cloudapp.azure.com 'cat ~/.ssh/id_ed25519.pub' | cat - >> ~/.ssh/authorized_keys

RUN cd /etc/ansible
RUN ansible -i inventory_node.ini selenoid-qa-example-01.eastus.cloudapp.azure.com -m ping

Buildkitで転送していた公開鍵の読み込みを削除して、内部でssh-keygenします。

で、相変わらずこれも何事もなくビルドされるので、execして確認します。
するとこれも先ほどと同様にPermission denied (publickey)になります。
ひとつずつ確認します。
まず公開鍵があるのか確認します。

bash-5.0# ls -la
total 32
drwx------    1 root     root          4096 Aug  5 13:09 .
drwx------    1 root     root          4096 Aug  5 13:09 ..
-rw-r--r--    1 root     root             0 Aug  5 13:09 authorized_keys
-rw-------    1 root     root           411 Aug  5 13:09 id_ed25519
-rw-r--r--    1 root     root           102 Aug  5 13:09 id_ed25519.pub
-rw-r--r--    1 root     root           944 Aug  5 13:09 known_hosts
-rwx------    1 root     root          2494 Aug  5 08:29 test_key.pem

ありますね。

次、転送は?

> ssh azureuser@selenoid-qa-example-01.eastus.cloudapp.azure.com 'cat ~/.ssh/id_ed25519.pub' | cat - >> ~/.ssh/authorized_keys
azureuser@selenoid-qa-example-01.eastus.cloudapp.azure.com: Permission denied (publickey).

おーこけとる。
そしてよくよく確認してみる。

あ。。そもそもターゲットノードってSSH認証しないとならないのでは?

丁度先ほどCOPYして配置したtest_key.pemが残っていたので、以下のように書き換ええ動作確認。

> ssh -i ~/.ssh/test_key.pem azureuser@selenoid-qa-example-01.eastus.cloudapp.azure.com 'cat ~/.ssh/id_ed25519.pub' | cat - >> ~/.ssh/authorized_keys
load pubkey "/root/.ssh/test_key.pem": invalid format
cat: /home/azureuser/.ssh/id_ed25519.pub: No such file or directory

(invalid formatは先ほどのsshsでも出てたので一旦置いておくとして)
/home/azureuser/.ssh/id_ed25519.pubが無いよ、って言われます。

これはよくよく考えてみたら当たり前です。
ターゲットノードに配布する前なので/home/azureuser/に公開鍵があるわけありません。

今一度コマンドの中身を確認しましょう。

1:ssh
2: -i ~/.ssh/test_key.pem
3: azureuser@selenoid-qa-example-01.eastus.cloudapp.azure.com
4: 'cat ~/.ssh/id_ed25519.pub'
5: |
6: cat - >> ~/.ssh/authorized_keys

1〜3行目はOKですね。普通にSSHしてます。
エラってるのは4行目。

あれ待って。
ここは文脈的には「SSH元の公開鍵の中身」ってしたいのに何でSSH先の公開鍵が出てるのかしら?
色々調べていくうちに以下のQiita記事にたどり着く。感謝。

qiita.com

そう、シングルクォートで囲むとコマンド結果の文字列が出てくるのだけど、同時にパスの対象がSSH先になってしまう!
試しにシングルクォートを外すと見事にSSH元パスとして評価された。

cat: /root/.ssh/id_ed25519.pub: Permission denied

つまり

  • シングルクォートで囲まなければパスが正常に動作する
  • シングルクォートで囲まないとコマンド結果が文字列で得られない(ので当然authrized_keysに追記できない)

と言うジレンマに陥ってしまいました。
つまり上記コマンドは使えない!と言う結論です。。。alpineのページ。。orz

で、さらに調べてたどり着いたQiita記事が以下。感謝。

qiita.com

おー!ワンライナーの記述あるやんけ!

と言うことで早速実行してみる。

bash-5.0# cat ~/.ssh/id_ed25519.pub | ssh -i ~/.ssh/test_key.pem azureuser@selenoid-qa-example-01.eastus.cloudapp.azure.com 'cat >> .ssh/authorized_keys'
load pubkey "/root/.ssh/test_key.pem": invalid format
bash-5.0# 

ん、なんか行ったっぽ。
sshVMに潜って確認してみる。(ややこしw)

> ssh -i ~/.ssh/test_key.pem azureuser@selenoid-qa-example-01.eastus.cloudapp.azure.com
load pubkey "/root/.ssh/test_key.pem": invalid format
Welcome to Ubuntu 18.04.4 LTS (GNU/Linux 5.3.0-1034-azure x86_64)

(中略)

azureuser@test:~$ cd ~/.ssh/
azureuser@test:~/.ssh$ ls -la
total 12
drwx------ 2 azureuser azureuser 4096 Aug  5 08:30 .
drwxr-xr-x 5 azureuser azureuser 4096 Aug  5 13:18 ..
-rw------- 1 azureuser azureuser  757 Aug  5 13:56 authorized_keys
azureuser@test:~/.ssh$ less authorized_keys

ssh-ed25519 *******(公開鍵の中身) root@buildkitsandbox

ちゃんと反映されていますね!

ではいよいよpingです。

ansible -i inventory_node.ini selenoid-qa-example-01.eastus.cloudapp.azure.com -m ping
[WARNING]: log file at /Users/User/ansible_log/ansible.log is not writeable and we cannot create it, aborting

[DEPRECATION WARNING]: Distribution Ubuntu 18.04 on host selenoid-qa-example-01.eastus.cloudapp.azure.com should use /usr/bin/python3, but is using /usr/bin/python for backward compatibility
 with prior Ansible releases. A future Ansible release will default to using the discovered platform python for this host. See 
https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information. This feature will be removed in version 2.12. Deprecation warnings can be disabled 
by setting deprecation_warnings=False in ansible.cfg.
selenoid-qa-example-01.eastus.cloudapp.azure.com | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}

やたー!ping:pongした!

と言うわけで本日はここまで。

ではでは。