ytooyamaのブログ

サーバ構築とか、仕事で発見したこととか、趣味のこととかを書いています。

Ansibleでリモートセットアップしようとしたら "Missing sudo password" と言われたときに見る記事

Ansibleを使ってリモートサーバーのセットアップをするときにsudoをしばしば使うのですが、定期的に "Missing sudo password" と怒られるのをよくやるので、ここにまとめておきたいと思います。

Summary for the impatient

リモートサーバーのsshdの設定が適切でないと、Ansibleによる自動化は失敗します。Ansibleを実行するノードではなくAnsibleで自動化する対象のノードに対して以下のような設定を行えば回避可能です。「cloud-user」のところは実際に存在するユーザーに置き換えるか、Ansible用のユーザーを作っておくと良いでしょう。Ansible Playbookで使うユーザーに対してssh-copy-idコマンドでキーペアの交換を行っておきましょう。

wheelグループはrootライクな権限を持つグループです。suコマンドを通じて、rootユーザになることができます。Linuxディストリビューションによっては違うグループ名の場合があります。たとえばDebian/Ubuntuの場合は sudoです。

$ sudo vi /etc/ssh/sshd_config
PasswordAuthentication no                  #sshによるパスワード認証を許可しない
$ sudo systemctl restart sshd              #設定反映のためsshdを再起動

$ sudo usermod -aG wheel cloud-user      #wheelグループにcloud-userを追加
$ sudo visudo                                               #sudoの設定変更
...
#%wheel  ALL=(ALL)     ALL                          #デフォルト設定。コメント化する
%wheel  ALL=(ALL)       NOPASSWD: ALL    #追記。sudo実行時のパスワード入力をやめる

問題の詳細と解決策

問題の詳細

Ansibleでまず、こんなインベントリーを作ると思います。インベントリーファイルは対象のサーバーを記述するためのファイルです。そのほか、認証に必要な情報(例えばユーザー名とか秘密鍵、使うPythonの指定)を行います。 ansible_python_interpreter を指定した場合はそのバージョンが対象のホストにインストールされている必要があります。

% cat hosts
[node]
192.168.0.100

[all:vars]
ansible_python_interpreter=/usr/bin/python3
ansible_user=cloud-user
ansible_ssh_private_key_file=/Users/macuser/.ssh/id_rsa

Playbookはとりあえず今回はこれを使うことにします。単純にpingを実行するだけのPlaybookです。ちなみにpingを実行するのに多くの場合sudoコマンドは実行する必要がありませんが、一般的にPlaybookで何かセットアップするときにはsudoはほぼ使うため、become: yes をあえて追加しています。

% cat playbook.yaml
---
- hosts: all
  become: yes
  tasks:
   - name: try ping
     ping:

そして次のように実行するわけですが、こんな感じで失敗するはずです。

% ansible-playbook playbook.yaml -i hosts
...
TASK [Gathering Facts] *********************************************************
fatal: [192.168.0.100]: FAILED! => {"msg": "Missing sudo password"}

普通にLinuxをセットアップすると、ユーザに対してパスワード認証が可能になっていると思います。またデフォルトのLinuxの設定だとsudoを実行するときにユーザーのパスワードを入力するように設定されていると思います。このため、セットアップ後デフォルトのままのLinuxホストをAnsibleで何らかの自動化をしようとすると何らかの問題が発生してうまくいかないと思います。

今回エラーとして表示された「Ansible Missing sudo password」メッセージを検索すると、次の情報を見つけると思います。ヒントにはなりますが色々書かれていること、書かれていないことがあるので、混乱してしまいます。そこで今回、対応方法をまとめておこうと思いました。

stackoverflow.com

問題の解決策

これを下記のように設定すると回避できます。

SSHDの設定変更

パスワード認証の無効化を行い、SSH Server (sshd) を再起動します。

$ sudo vi /etc/ssh/sshd_config
PasswordAuthentication no
$ sudo systemctl restart sshd

公開鍵認証を出来るようにするために、事前に次のコマンドを実行するか、/home/cloud-user/.ssh/authorized_keysファイルに公開鍵の入力を忘れずに(ssh-copy-idを使う場合は、初回の実行時に該当ユーザーのパスワードの入力が必要です)。

$ ssh-copy-id -i ~/.ssh/id_rsa cloud-user@192.168.1.100

sudoの設定変更

任意のユーザーでsudoコマンドを実行したときにパスワードが聞かれないように設定変更します。

$ sudo visudo
...
cloud-user    ALL=(ALL)    NOPASSWD: ALL

これで終わりのはずですが、実はこの設定をしても引き続きsudoコマンドを実行するとパスワードを聞かれてしまいます。このままだとAnsibleで公開鍵認証でリモートサーバーのセットアップは出来ません。

[cloud-user@cent8-test2 ~]$ sudo visudo
[sudo] cloud-user のパスワード:

実際に実行してみると次のような感じです。

% ansible-playbook playbook.yaml -i hosts

PLAY [all] *********************************************************************

TASK [Gathering Facts] *********************************************************
fatal: [192.168.0.100]: FAILED! => {"ansible_facts": {}, "changed": false, "failed_modules": {"setup": {"failed": true, "module_stderr": "Shared connection to 192.168.0.100 closed.\r\n", "module_stdout": "sudo: パスワードが必要です\r\n", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}}, "msg": "The following modules failed to execute: setup\n"}

この状態では-Kオプションをつけて実行することで、対象のサーバーのsudoパスワードを聞かれるので、正しいパスワードを入力すれば一応通すことは出来ます。ただこれは自動化ではないですし、やはりパスワードを聞かれる状態は格好良くはありません。

% ansible-playbook playbook.yaml -i hosts -K
BECOME password:

PLAY [all] *********************************************************************

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

TASK [try ping] ****************************************************************
ok: [192.168.0.100]

PLAY RECAP *********************************************************************
192.168.0.100   : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

そこで、該当のユーザーをwheelグループに追加します。先ほど追加した行はコメント化するか削除して、wheelグループの場合はsudoコマンド実行時にパスワードを聞かれない設定を追加します。

なお、DebianやUbuntuではwheelグループではなくsudoグループです。

$ sudo usermod -aG wheel cloud-user
$ sudo visudo
#cloud-user    ALL=(ALL)    NOPASSWD: ALL  #コメント化か削除
#%wheel  ALL=(ALL)       ALL               #デフォルト設定。コメント化する
%wheel  ALL=(ALL)       NOPASSWD: ALL      #追記

設定変更後、-Kオプションを取り除いて実行してみます。

% ansible-playbook playbook.yaml -i hosts

PLAY [all] *********************************************************************

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

TASK [try ping] ****************************************************************
ok: [192.168.0.100]

PLAY RECAP *********************************************************************
192.168.0.100   : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

問題なく実行できました。

まとめ

今回取り上げた問題ですが、おそらく何らかのオーケストレーションツールを使ってデプロイされたサーバーやクラウド上のサーバーではデフォルトで今回のような設定がされており、はまることはないと思います。

今回のような設定を一切変更せずパスワード認証でPlaybookを流したい場合は、 sshpassansible-playbook コマンドの -K および -k オプションを使う方法があります。ただ色々と面倒くさい(例えばクライアント側にsshpassを入れる必要があったり、パスワードの管理がちょっと面倒だったり、Missing sudo password in Ansibleにパスワードを書いておく方法はあるけどセキュアじゃないし、CLIの実行時にパスワードを入力する方法もあるけどコマンドの履歴にパスワードが残ってしまいセキュアではないし..)ので、公開鍵認証の方が良いです。

おまけ

ちなみに sshpass の件を調査していたら、次の記事を見つけました。7年前の自分が書いてましたね。

ytooyama.hatenadiary.jp

このブログサイトはJavaScriptを使っていますが、読み込んでいるJavaScriptは全てはてなが提供しているものであり、筆者が設置しているものではありません。