Ansible:Linux サーバーセキュリティ設定の自動化
この記事でやること
以前書いた Linux サーバー SSH 設定でやった SSH とファイアウォールの設定を Ansible によって自動化し、5分くらいでできるようにします。
まぁ、手でやるほうが難易度的には圧倒的に簡単ですが、
- 遊び用のサーバーは躊躇なく破壊したいし、再構築で貴重な休日を潰したくない。
- 手でやるとどこかでミスる可能性があるので、極力サーバーは直接触りたくない。
- 複数台のサーバーでの設定を頼まれたときに起こる頭痛・めまい・吐き気等の緩和。
といった事情で完全自動化したいわけです。「ハッカーは3時間でできる作業を3日かけて自動化する」とは言ったものです。
本記事で使用するコードは以下のリポジトリにまとめてあります。今回はついでに git
, make
, docker
, docker-compose
をサーバーにインストールして、コンテナデプロイ用サーバー構築の演習とします。
実験環境
- クライアント:Dockerで仮想化しているのでなんでもいいと思います。
- ホスト:Ubuntu 20.04 LTS
Ansible とは
Ansible は SSH 通信を介してサーバーのセットアップを playbook という単位で自動化するためのソフトウェアです。Ansible には以下のような特徴があります。
- プッシュベース
- 操作を行いたいタイミングでクライアント(開発用PC)からホスト(サーバー)に必要なスクリプトやコードを送信し、ホスト上で操作を実行します。
- 対義語のプルベースの設定管理システムは、ホスト上にエージェントをインストールし、リポジトリの変更やタイマーなどの何らかのイベントをきっかけにしてリポジトリから必要なスクリプトやコードをプルし、ホスト上で操作を実行します。Ansible はプルベースの設定管理システムに比べ、ホスト上に余計なソフトウェアをインストールする必要がないため、サーバーの構成はシンプルとなり保守性が高まります。
- 宣言的
- ホストで何をするか(手続き的)ではなく、ホストがどういう状態であるかを記述します。つまり開発者は具体的な操作やその手順をあまり意識しなくてもホストを望んだ状態に設定することができます。
- 冪等性
- ホストが既に playbook によって指定された状態にある場合、Ansible は何もしません。つまり Ansible を何度実行してもホストは同じ状態になります。間違って複数回実行してもホストの環境を破壊することはありません。
最近はクラウド上に構築したシステムに CI ツールをインストールする構成がオーソドックスなので Ansible を使う機会は少ないと思いますが、私のように安い VPS を借りて細々と趣味のプログラミングをしている人にとっては便利なツールです。
また、Ansible は複数のシステムに対して一連の操作を行うオーケストレーション(たとえばメンテナンス時にネットワークの設定を変更すると同時に一部のシステムの起動や停止を実行する)に使用することも可能で、人間がいちいち手で対応できないような大規模プロジェクトでもワークします。
Ansible のインストール
Ansible はクライアントにのみインストールすれば OK で、ホスト側にはなにも特別なことをする必要はありません。インストール方法についてはこちらを参照してください。
本記事ではクライアントの Docker 上に Ansible をインストールした Ubuntu イメージを作成し、そのコンテナにログインすることで Ansible を使用します。リポジトリから抜粋した Dockerfile は以下のようになります。
FROM ubuntu:20.04
# installing tzdata stops if this environment variable is not defined
ENV DEBIAN_FRONTEND=noninteractive
# sshpass is needed for ansible first login to remote server
RUN apt-get update \
&& apt-get install --no-install-recommends -y \
make vim curl git python3 python3-pip sshpass \
software-properties-common \
&& add-apt-repository --yes --update ppa:ansible/ansible \
&& apt-get install -y ansible \
&& apt-get clean
ARG UID=1000
RUN useradd -m -u ${UID} josh
USER josh
WORKDIR /playbook
最初に注意しておくこと
Ansible は SSH を使用してリモートのホストにアクセスするので、SSH の設定を変更すると Ansible 自身が締め出されますが、それが正しい挙動です。つまり以下の 2 つの設定ファイルを用意することになります。
- SSH セットアップ用 SSH 設定
- パスワードによりホストにログインし、SSH セットアップ完了後にはこの設定でのアクセスは不可能になります。
- SSH セットアップ完了後用 SSH 設定
- 公開鍵暗号によりホストにログインし、SSH セットアップ完了後にはこの設定でのみアクセス可能になります。
「何度実行しても同じ実行結果になる」が売りの Ansible ですが、SSH の設定に限っては原理的に一度しか実行できません。複数回実行できるならドデカいセキュリティホールが開いていることになるので反省してください。
最終的なディレクトリ構成
最終的なディレクトリ構成は以下のようになります。作業ディレクトリと章番号は
- root: 『1. root: SSH セットアップ用 playbook』
- user: 『2. user: SSH セットアップ完了後用 playbook』
のように対応しているので、それぞれの章では各ディレクトリに移動して作業してください。
docker-ansible
├── Dockerfile
└── playbook
├── root
│ ├── ansible.cfg
│ ├── inventory
│ ├── playbook.yml
│ └── tasks
│ ├── 0010_install_requirements.yml
│ ├── 0020_create_groups.yml
│ ├── 0021_create_admin_user.yml
│ ├── 0022_create_common_user.yml
│ ├── 0030_ssh_security_settings.yml
│ ├── 0040_ufw_security_settings.yml
│ ├── 9999_reboot.yml
│ └── M001_ssh_config.yml
└── user
├── playbook.yml
└── inventory
0. 実験環境の準備
今回は ConoHa VPS で実験します。別に さくらのVPS でも AWS EC2 でも Vagrant 上で動かしている Ubuntu でもなんでもいいです。ポチポチとボタンを押して Ubuntu 20.04 を選択しましょう(何度か実験しているので画像では再構築ボタンになっています)。ここで入力した root パスワードは Ansible が最初にサーバーにアクセスするために必要になります。
下にチラチラ見えている SSH Key の登録ボタンを押したら幸せになれる気がしますが、それでは企画が終了してしまうので見なかったことにします[1]。
これからの作業で必要になるので、鍵は予め生成しておいてください。今回はセキュリティポリシーとして、管理者とデプロイ用ユーザーで鍵をわけることにして、それぞれ~/.ssh/id_admin
、~/.ssh/id_common
とします。
$ ssh-keygen -t ed25519
作成するユーザー名は admin
と sirius
です。
$ ssh admin
$ ssh sirius
でアクセスできる状態になってほしいので、~/.ssh/config
を(なければ作成して)以下のように編集してください。HostName
はご自身がアクセスするサーバーのものを指定し、Port
は変更する場合は指定してください。
Host admin
HostName 192.168.1.11
Port 2222
IdentityFile ~/.ssh/id_admin
User admin
Host sirius
HostName 192.168.1.11
Port 2222
IdentityFile ~/.ssh/id_common
User sirius
1. root: SSH セットアップ用 playbook
やること
サーバーの初期設定で、それなりにやることがあります。
- 必要なソフトウェアのインストール
-
git
,make
,docker
,docker-compose
-
- ユーザーグループの作成
-
admin
: 全権限を付与した管理者用グループ -
common
:make
コマンド とdocker
コマンドをsudo
で実行する権限を与えられた、サービスデプロイ用のグループ -
docker
:docker
コマンドをsudo
なしで実行可能なグループ。管理者がいちいちsudo
するのは面倒くさいので。
-
- 管理者ユーザーの作成
- 一般ユーザーの作成
- SSH の設定
- ファイアウォールの設定
- サーバーの再起動
サービスデプロイ用のユーザーは常に攻撃の危険に晒されるため、本来なら使えるコマンド等をより制限すべきでしょうが、今回は特に危険な docker
コマンドの権限を明確にしています。もしも何らかの方法で
$ docker container run -it --rm -v /:/workdir [イメージ] bash
を実行されて、しかも Docker のイメージがうっかり root ユーザのままになっていたら、そのコンテナのシェルは root 権限で任意のディレクトリにアクセス可能となり、root 権限のシェルを奪われたも同然です。Docker はコンテナが陥落してもコンテナの外に影響はありませんが、攻撃者が docker
コマンドを実行可能な状態でユーザーアカウントが陥落した場合にはそのサーバーは終わりです。
最初の playbook
Ansible では YAML 形式の playbook と呼ばれる単位でホストの設定を管理します。あとはこいつをゴリゴリと書いていくだけです。最初のディレクトリ構成は以下のようにしましょう。ansible.cfg
、inventory
、playbook.yml
はそれぞれ空のファイルとして作成しておいてください。
docker-ansible
├── Dockerfile
└── playbook
└── root
├── ansible.cfg
├── inventory
└── playbook.yml
最初に docker-ansible
のディレクトリにいるとして、このとき playbook は
$ cd playbook/root
$ ansible-playbook -i inventory playbook.yml
で実行できます。ここで inventory
は INI 形式で書かれた Ansible の設定ファイル名で、playbook.yml
は実行する playbook のファイル名です。もしもクライアント側でも Docker を使っている人は、
# イメージのビルド
## -f はビルドに用いる Dockerfile の名前で省略可能です。
## -t はイメージのタグ名で自由に設定できますが、基本は `username/imagename:version` です。
(shell) $ docker image build -f Dockerfile -t wsuzume/docker-ansible:latest .
# イメージからコンテナを作成してそのシェルにログイン
(shell) $ docker container run -it --rm -v ${PWD}/playbook:/playbook wsuzume/docker-ansible:latest bash
(docker)$ cd root
(docker)$ ansible-playbook -i inventory playbook.yml
で実行できます[2]。ただし(shell)$
は docker
コマンドを実行するシェル、(docker)$
は Docker コンテナのシェルです。
試しに以下のコマンドを実行してみてください。playbook のシンタックスに問題がないかをチェックしてくれます。
$ ansible-playbook -i inventory playbook.yml --syntax-check
正しいディレクトリで実行できていれば、playbook は存在するものの空である旨の以下のエラーが出ます。まだ何も記述していないのでこれでいいです。
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost
does not match 'all'
ERROR! Empty playbook, nothing to do
ディレクトリが誤っていれば playbook が存在しない旨の以下のエラーが出ます。
ERROR! the playbook: playbook.yml could not be found
もしこのエラーが出たら ls
コマンドを実行してファイルの存在を確認してください。以下のようにならなければおかしいです。
$ ls
inventory playbook.yml
ここまでうまく行っていたら、ansible.cfg
、inventory
、 playbook.yml
をそれぞれ以下のように編集しましょう。サーバーにアクセスしてログを出力するだけの簡易的なものです。192.168.1.11
はどう見てもご家庭内のサーバーを指しているので、みなさんはきちんと自分のサーバーのアドレスに書き直してください。
[defaults]
host_key_checking=False
[server]
# SSH で接続したいサーバーの IP アドレスまたはホスト名
192.168.1.11
# 複数のサーバーを同じ設定にしたい場合にはここに列挙します
# 192.168.1.12
# 192.168.1.13
# 192.168.1.14
# ...
[server:vars]
ansible_python_interpreter=/usr/bin/python3
ansible_port=22
ansible_user=root
# サーバーの root パスワード
ansible_ssh_pass=XXXXXX
---
- hosts: server
tasks:
- debug: msg="Hello, world!"
シンタックスチェック[3]を行います。
$ ansible-playbook -i inventory playbook.yml --syntax-check
playbook: playbook.yml
大丈夫そうです。以下のコマンドを実行します。
$ ansible-playbook -i inventory playbook.yml
PLAY [server] *********************************************************************************************
TASK [Gathering Facts] ************************************************************************************
ok: [192.168.1.11]
TASK [debug] **********************************************************************************************
ok: [192.168.1.11] => {
"msg": "Hello, world!"
}
PLAY RECAP ************************************************************************************************
192.168.1.11 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
無事に接続できました。これでサーバーは言うことを聞くようになったので、頑張って設定を書いていきます。
2021/03/24追記
以前までは $ ansible-playbook
を実行する前に SSH Host Key Checking があるため以下の手順を行うよう書いていました。しかしこの手順は ansible_ssh_common_args='-o StrictHostKeyChecking=no'
を指定することで回避できる、という情報提供[4]があったため、対応する設定について調べたところ、ansible.cfg
に host_key_checking=False
を指定することで同等の効果が得られることがわかりました。ansible.cfg
に関しては Ansible の動作の制御をご覧ください。
すぐにでも実行したいところですが、一度
$ ssh 192.168.1.11
を実行して
yes
を入力してください。Ansible は SSH 接続先を勝手に登録する権限を持っ> ていないのでこれを実行しないとエラーを起こします。一度フィンガープリントを登録してしまえ> ばその後は実際にアクセスせずにCtrl+C
などで中断しても大丈夫です。Docker でやってい> る人は、Docker コンテナ上でこれを実行してください(コンテナからexit
すると--rm
> オプションの影響で毎回新しいコンテナが作成され設定がリセットされるので、コンテナに入り直> す度にこの手順が必要です。それが面倒なら--rm
オプションを外した上で、ご自身でコンテナを管理してください)。$ ssh 192.168.1.11 The authenticity of host '192.168.1.11 (192.168.1.11)' can't be established. ECDSA key fingerprint is SHA256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '192.168.1.11' (ECDSA) to the list of known hosts. root@192.168.1.11's password: [Ctrl+C]
playbook の分割
Ansible のタスクは include_tasks
によって他の YAML 形式のファイルをインポートすることができます。また、このとき vars
に変数を指定することが可能で、インクルードした YAML の{{ }}
で書かれたテンプレートに展開されます。これはたとえば複数のユーザーを似たような設定で作成するときに YAML をモジュールとして使い回せるので便利です。
name
タグはタスクに付与可能な名前で、指定しなくてもよいですが、ログが見やすくなるので適宜挿入していきます。
inventory
playbook.yml
tasks
hello_world.yml
---
- name: "Configure VPS"
hosts: server
tasks:
- include_tasks: tasks/hello_world.yml
vars:
message: "Hello, world!"
---
- name: "hello world"
debug: msg={{ message }}
ここから先はぶっちゃけひたすら泥臭い作業になりますし、特に解説もしないので、読み飛ばしてもよいです。
必要なソフトウェアのインストール
Ansible はサーバー上でシェルスクリプトを実行することも可能なのですが、手続き的な操作を行ってしまうと、宣言的であるという Ansible のメリットが失われてしまいます。そのため、Ansible では各操作に対応したモジュールを用います。
Ubuntu では apt モジュールによってソフトウェアのインストールが可能です。
---
- name: "install requirements"
apt:
update_cache: yes
name:
- git
- make
- docker
- docker-compose
ユーザーグループの作成
group モジュールを使います。
---
- name: "create admin group (is written in sudoers file from the first)"
group:
name: admin
gid: 5001
- name: "create common group"
group:
name: common
gid: 5002
- name: "create docker group"
group:
name: docker
gid: 5003
- name: "allow 'sudo make' to common"
lineinfile:
dest: /etc/sudoers
line: "%common ALL=(root) /usr/bin/make"
- name: "allow 'sudo docker' to common"
lineinfile:
dest: /etc/sudoers
line: "%common ALL=(root) /usr/bin/docker"
管理者ユーザーの追加
user モジュールを使います。
---
- name: "create admin user: {{ username }}"
user:
name: "{{ username }}"
uid: "{{ uid }}"
group: admin
groups: docker
create_home: yes
home: "/home/{{ username }}"
password: "{{ password }}"
update_password: on_create
shell: /bin/bash
### SSH config
- include_tasks: M001_ssh_config.yml
vars:
home: "/home/{{ username }}"
以下は SSH の公開鍵をユーザーのホームディレクトリに書き込むための設定です。これが意外に面倒くさいです。
---
- name: "{{ username }}: remove ssh config directory"
file:
path: "{{ home }}/.ssh"
state: absent
- name: "{{ username }}: create ssh config directory"
become_user: "{{ username }}"
file:
path: ~/.ssh
state: directory
- name: "{{ username }}: copy ssh pubkey to server"
become_user: "{{ username }}"
copy:
src: "~/.ssh/{{ ssh_pubkey }}"
dest: "~/.ssh/{{ ssh_pubkey }}"
- name: "{{ username }}: copy ssh pubkey to ansible variable"
become_user: "{{ username }}"
slurp:
path: "~/.ssh/{{ ssh_pubkey }}"
register: var_ssh_pubkey
- name: "{{ username }}: add ssh pubkey to authorized keys"
become_user: "{{ username }}"
lineinfile:
create: yes
dest: ~/.ssh/authorized_keys
line: "{{ var_ssh_pubkey.content | b64decode }}"
mode: 0600
- name: "{{ username }}: change ssh config directory mode"
file:
path: "{{ home }}/.ssh"
state: directory
mode: 0700
呼び出し側(playbook.yml
)のタスクを抜粋します。
- include_tasks: tasks/0021_create_admin_user.yml
vars:
username: admin
uid: 1001
password: "{{ admin_password | password_hash('sha512') }}"
ssh_pubkey: "{{ ssh_admin_pubkey }}"
admin_password
, ssh_admin_pubkey
はinventory
の[server:vars]
に追加します。
[server]
192.168.1.11
[server:vars]
ansible_python_interpreter=/usr/bin/python3
ansible_port=22
ansible_user=root
ansible_ssh_pass=XXXXXX
# クライアント側にある公開鍵の場所です。これが `~/.ssh` からホストにコピーされます。
ssh_admin_pubkey=id_admin.pub
admin_password=XXXXXX
デプロイ用ユーザーの追加
---
- name: "create common user: {{ username }}"
user:
name: "{{ username }}"
uid: "{{ uid }}"
group: common
create_home: yes
home: "/home/{{ username }}"
password: "{{ password }}"
update_password: on_create
shell: /bin/bash
### SSH config
- include_tasks: M001_ssh_config.yml
vars:
home: "/home/{{ username }}"
呼び出し側です。
- include_tasks: tasks/0022_create_common_user.yml
vars:
username: sirius
uid: 2001
password: "{{ sirius_password | password_hash('sha512') }}"
ssh_pubkey: "{{ ssh_common_pubkey }}"
インベントリに以下を追加します。
# クライアント側にある公開鍵の名前です。これが `~/.ssh` からホストにコピーされます。
ssh_common_pubkey=id_common.pub
sirius_password=XXXXXX
他にもユーザーを追加したい場合はいくらでもタスクをコピペして、パスワードを追加すればよいです。Ansible はループも記述できるので、その気になれば機械的に無数のユーザーを追加できます。
SSH の設定
うまいことどうにかする方法が見当たらなかったので、lineinfile モジュールを駆使して力技でやります。ご自身で SSH 設定を変えたい場合はこちらを頑張って変更してください。
---
- name: "disable password authentication"
lineinfile:
dest: /etc/ssh/sshd_config
regexp: "^PasswordAuthentication"
insertafter: "^#PasswordAuthentication"
line: "PasswordAuthentication no"
- name: "disable empty password"
lineinfile:
dest: /etc/ssh/sshd_config
regexp: "^PermitEmptyPasswords"
insertafter: "^#PermitEmptyPasswords"
line: "PermitEmptyPasswords no"
- name: "disable challenge response"
lineinfile:
dest: /etc/ssh/sshd_config
regexp: "^ChallengeResponseAuthentication"
insertafter: "^#ChallengeResponseAuthentication"
line: "ChallengeResponseAuthentication no"
- name: "disable root login"
lineinfile:
dest: /etc/ssh/sshd_config
regexp: "^PermitRootLogin"
insertafter: "^#PermitRootLogin"
line: "PermitRootLogin no"
- name: "enable pubkey authentication"
lineinfile:
dest: /etc/ssh/sshd_config
regexp: "^PubkeyAuthentication"
insertafter: "^#PubkeyAuthentication"
line: "PubkeyAuthentication yes"
- name: "change ssh port to {{ new_ssh_port }}"
lineinfile:
dest: /etc/ssh/sshd_config
regexp: "^Port"
insertafter: "^#Port"
line: "Port {{ new_ssh_port }}"
new_ssh_port
という変数がありますが、これはインベントリに記述されたものから上書きする必要がないので、呼び出し側の playbook.yml
に指定する必要はありません(ユーザー作成のときはパスワードをハッシュ化する必要があったので上書きしていました)。
new_ssh_port=XXXX
ファイアウォールの設定
Ubuntu の ufw ならば ufw モジュールで設定可能です。こちらも用途に合わせてご自身で変更してください。
---
- name: "deny incoming"
ufw:
default: deny
direction: incoming
- name: "allow SSH ({{ new_ssh_port }})"
ufw:
rule: allow
log: yes
port: "{{ new_ssh_port }}"
- name: "allow HTTP (80/TCP)"
ufw:
rule: allow
proto: tcp
port: "80"
- name: "allow HTTPS (443/TCP)"
ufw:
rule: allow
proto: tcp
port: "443"
- name: "enable firewall"
ufw:
state: enabled
サーバーの再起動
最後に SSH とファイアウォールのデーモンを再起動したあとで、念の為サーバーを再起動します。
---
- name: "reload ufw"
ufw:
state: reloaded
- name: "reboot sshd"
service:
name: sshd
state: restarted
- name: "reboot machine"
shell: reboot
async: 1
poll: 0
inventory と playbook.yml の全体
docker-ansible
├── Dockerfile
└── playbook
└── root
├── ansible.cfg
├── inventory
├── playbook.yml
└── tasks
├── 0010_install_requirements.yml
├── 0020_create_groups.yml
├── 0021_create_admin_user.yml
├── 0022_create_common_user.yml
├── 0030_ssh_security_settings.yml
├── 0040_ufw_security_settings.yml
├── 9999_reboot.yml
└── M001_ssh_config.yml
- name: Configure VPS
hosts: server
become: True
tasks:
### Install requirements
- include_tasks: tasks/0010_install_requirements.yml
### create user groups
- include_tasks: tasks/0020_create_groups.yml
### create users
- include_tasks: tasks/0021_create_admin_user.yml
vars:
username: admin
uid: 1001
password: "{{ admin_password | password_hash('sha512') }}"
ssh_pubkey: "{{ ssh_admin_pubkey }}"
- include_tasks: tasks/0022_create_common_user.yml
vars:
username: sirius
uid: 2001
password: "{{ sirius_password | password_hash('sha512') }}"
ssh_pubkey: "{{ ssh_common_pubkey }}"
### SSH security settings
- include_tasks: tasks/0030_ssh_security_settings.yml
### firewall settings
- include_tasks: tasks/0040_ufw_security_settings.yml
### reboot firewall and sshd
- include_tasks: tasks/9999_reboot.yml
[server]
192.168.1.11
[server:vars]
ansible_python_interpreter=/usr/bin/python3
ansible_port=22
ansible_user=root
ansible_ssh_pass=XXXXXX
new_ssh_port=XXXX
# 管理者ユーザー
ssh_admin_pubkey=id_admin.pub
admin_password=XXXXXX
# 一般ユーザー
ssh_common_pubkey=id_common.pub
sirius_password=XXXXXX
あとは実行するだけです。Docker でやっている人は、起動するときにローカルの ~/.ssh
ディレクトリをコンテナ内にコピーするため、以下のように細工します。
$ docker container run -it --rm \
-v $playbook:/playbook \
-v $~/.ssh:/tmp/.ssh \
wsuzume/docker-ansible:latest \
bash -c "cp -r /tmp/.ssh ~/.ssh && bash"
$ ansible-playbook -i inventory playbook.yml --syntax-check
$ ansible-playbook -i inventory playbook.yml
この時点でサーバーは root ユーザーへのログインやパスワードによるログインが不可能になり、
$ ssh admin
$ ssh sirius
によって、それぞれ管理者ユーザー、デプロイ用ユーザーにアクセスできるようになっていると思います。できなければ、私か、あなたか、万象一切を支配する大宇宙の理が間違っています。できるまで運命に抗い続けてください。
2. user: SSH セットアップ完了後用 playbook
今度は Ansible で公開鍵を用いてサーバーにアクセスせねばなりませんが、これはインベントリと実行時のコマンドを少し書き換えるだけで大丈夫です。とりあえずHello world が実行できれば OK です。
---
- hosts: server
tasks:
- debug: msg="Hello, world!"
[server]
192.168.1.11
[server:vars]
ansible_port=XXXX
ansible_sudo_pass=XXXXXX
ansibple_port
は変更後のポートです。ansible_sudo_pass
はユーザーに指定したパスワードで、Ansible で実行するタスクが sudo
で管理者権限に昇格する際に使用されます。
Docker でやっている人は、先程と同じ方法でコンテナを起動します。
$ docker container run -it --rm \
-v $playbook:/playbook \
-v $~/.ssh:/tmp/.ssh \
wsuzume/docker-ansible:latest \
bash -c "cp -r /tmp/.ssh ~/.ssh && bash"
あとは以下のコマンドで Hello world が実行できます。
$ ansible-playbook -i inventory playbook.yml -u sirius --private_key="~/.ssh/id_common"
-u
はタスクを実行するサーバーのユーザー名で、--private_key
には使用する公開鍵を指定します。
GitHub に push したリポジトリについて
GitHub に push してあるリポジトリは、ここまで説明した項目に加えて少しだけ修正が加わっています。ひとつはいくつかの操作を Make で自動化してあることです。Dockerコンテナの中から以下の Makefile を実行することで、ansible-playbook ~~~
のような長ったらしいコマンドを入力しなくてもよいようになっています。また、このコマンドが使いやすいように ansible.cfg
は Makefile
と同じ場所に移動されています。
.PHONY: root
root:
ansible-playbook -i root/inventory/server root/playbook.yml
.PHONY: user
user:
ansible-playbook -i user/inventory/server user/playbook.yml \
-u castor --private-key="~/.ssh/id_common"
もうひとつは間違ってもインベントリに書き込んだ設定が流出しないように、インベントリをリポジトリの外に置いてあることです。リポジトリにはインベントリのテンプレートのみが置いてあり、Docker コンテナ起動時にリポジトリ外部の本当のインベントリをテンプレートがあった場所にマウントしています。デフォルトでは
$ make copy_inventory
コマンドの実行によって ~/config/[appname]
の場所にインベントリのテンプレートがコピーされるので、こちらを編集してください。
一連の手順でサーバー自動セットアップ
それでは Conoha VPS でサーバーに Ubuntu 20.04 LTS をクリーンインストールした状態でよーいドン。
(shell) $ git clone git@github.com:wsuzume/docker-ansible
(shell) $ cd docker-ansible
(shell) $ make copy_inventory
(shell) $ make image
# Docker がイメージをビルドまたはプルしている間にインベントリを適切に編集する
(shell) $ make shell
(docker)$ make root
(docker)$ make user
記録 5 分。いい感じ。
-
Ansible を使えば複数台のサーバーを同時にセットアップすることが可能なので、その場合は1台1台手で SSH Key を登録するよりも Ansible のほうが速いです。 ↩︎
-
イメージ名に
wsuzume/docker-ansible:latest
を使用する限りは、$ docker container run
のときに私のリポジトリからイメージがプルされるのでビルドは省略しても問題ありません。 ↩︎ -
YAML形式のファイルは厳密にはファイルの開始
---
を記述しますが、これは省略しても問題ありません。 ↩︎ -
すぎむら @sugitkさん、ありがとうございます。 ↩︎
Discussion