TL;DR
- Windows
- WinRMはHTTP(S)でLISTENしている
- 踏み台サーバにHTTP proxyを設置し、WinRMのリクエストを中継することができる
- Linux
- オプションを適切に設定すると多段SSHができるようになる
- AnsibleでlocalhostのIPアドレスを取得する方法
-
{{ hostvars.localhost.ansible_default_ipv4.address }}
を使うhosts: localhost
のTaskを1回以上実行しておくこと
-
{{ ansible_fact.ens224.ipv4.address }}
を使う- ens224は環境によって変わる
-
0. ことの始まり
Ansibleを使った環境構築の設定中にNW構成の変更が入り、踏み台経由で各種サーバにログインできるようにする必要が生じた。
その環境にはLinuxとWindowsがそれぞれ存在するような構成だったので、SSHとWinRMをそれぞれ踏み台経由で実行できなければならない。
問題のNW構成図
1. 設計
1.1. SOCKS Proxy案
SOCKS Proxyを経由すれば実現できるのではという初期案。
https://www.bloggingforlogging.com/2018/10/14/windows-host-through-ssh-bastion-on-ansible/
SOCKSの通信用にクライアントモジュールを増やさなければならないのでAnsibleホストの環境構築の手間が増えそう。
1.2. HTTP Proxy案
WinRMが裏でHTTPSを使っていることを利用し、HTTP Proxyを経由させる案
踏み台にSquidを入れるだけなのですべてAnsibleのPlaybook内で完結し、事前の準備が難しくない。
Squidの設定も参考になりそうなものを見つけたので、こちらの案を試すことにする。
2. 実装
2.1. 踏み台にSquidインストールする
- name: Install squid yum: name: squid disable_gpg_check: no state: installed - name: Update squid.conf copy: src: ./squid.conf dest: /etc/squid/squid.conf owner: root group: root mode: 0644 - name: Open tcp/3128 iptables: chain: INPUT protocol: tcp match: tcp destination_port: '3128' jump: ACCEPT - name: Restart squid service: name: squid enabled: yes state: restarted
2.2. Proxy経由でSSH/WinRMを実行する
ここではinventoryで指定する同一グループ内にWindows/Linuxが混在しない構成を想定している。
(childrenを使ってグループの階層構造を作ることは構わないが、その場合はPlaybookの作り方を考える必要がある)
inventory/group_vars/all.yml
を設定する
ansible_winrm_proxy: "http://{{ vars.groups.bastion[0] }}:3128" ansible_ssh_common_args: '-o StrictHostKeyChecking=no -o ProxyCommand="sshpass -p {{ bastion_pass }} ssh -W %h:%p -q {{ bastion_user }}@{{ vars.groups.bastion[0] }}"'
inventory/group_vars/bastion.yml
を設定する
ansible_ssh_common_args: '-o StrictHostKeyChecking=no'
- Ansibleを流すターゲットOSに応じて以下の設定をPlaybookに入れておく
2.3. Ansible実行ホストを自動的に許可する
これまでの実装では、SquidのallowリストにAnsibleの実行ホストのIPアドレスがハードコードされているため、移植性に難がある。
そこで、Squidのallowリストに実行ホストのIPアドレスを自動的に挿入する方法を考える。
2.3.1. Ansibleに実行ホストのIPアドレスを知る手段はあるのか
その謎を解明するため、我々はAnsibleの奥地へと向かったーー。
まずは変数をダンプ
hosts: bastion tasks: - debug: msg: "{{ vars }}"
vars
の出力は非常に長いので割愛するが、ここでは特に何も見つからない。
外部変数で自分のIPアドレスを設定してから変数ダンプ
- hosts: localhost vars: ansible_host_ipv4_address: '192.168.0.2' tasks: - debug: var: ansible_host_ipv4_address register: test_var hosts: bastion tasks: - debug: var: test_var - debug: msg: "{{ vars }}"
別の hosts
で指定したPlayの変数を参照することができた。
しかし、実行ホストのIPアドレスが自動で設定されている様子はない。
vars以外に変数を見る手段は無いのか?
- hosts: localhost tasks: - debug: msg: "test" - hosts: bastion tasks: - debug: var: hostvars
まだ見つからない。
が、hostvars
はIPアドレスをkeyにした連想配列になっているらしい。
hostvarsにAnsibleホストのkeyがあるかどうか確かめる
- hosts: localhost tasks: - debug: msg: "test" - hosts: bastion tasks: - debug: var: hostvars.localhost
意外とあっさり見つかった。
IPアドレスを取得してみる
- hosts: localhost tasks: - debug: msg: "test" - hosts: bastion tasks: - debug: var: hostvars.localhost.ansible_default_ipv4.address
意図したとおりIPアドレスを取得できた。
綺麗に整理する
- hosts: bastion tasks: - debug: var: hostvars.localhost.ansible_default_ipv4.address
???
2.3.2. 結論
Ansible実行ホストのIPアドレスを取得するには以下のように、初めに hosts: localhost
のPlayを実行しておく必要がある。
- hosts: localhost tasks: - debug: msg: "test" - hosts: bastion tasks: - debug: var: hostvars.localhost.ansible_default_ipv4.address
2.3.3. Squidの設定に反映させる
roles/bastion/files/squid.conf
をroles/bastion/templates/squid.conf.j2
に移動する- IPアドレスをハードコードしていた場所を変数化する
# アクセスを許可するIPアドレスを設定して下さい acl myacl src xxx.xxx.xxx.xxx http_access allow myacl
↓
# アクセスを許可するIPアドレスを設定して下さい acl myacl src {{ hostvars.localhost.ansible_default_ipv4.address }} http_access allow myacl
- squidを設定するタスクで
copy
モジュールを使っている箇所を差し替える
- name: Update squid.conf template: src: ./squid.conf.j2 dest: /etc/squid/squid.conf owner: root group: root mode: 0644
- 踏み台の設定を行う前に
hosts: localhost
のPlayを実行しておく
- hosts: localhost tasks: - debug: msg: "touch"
2.3.4. 余談
{{ ansible_fact.ensXXX.ipv4.address }}
とすれば hosts: localhost
のPlayを実行する必要はない。
ただし、ensXXX
は環境によってNICの番号が変わるデメリットがあるため、状況に応じて使い分けられると良い。
例えば hostvars.localhost.ansible_default_ipv4.address
にはdefault routeに設定されているNICのIPアドレスが設定されるため、踏み台サーバにそのNICからアクセスできない場合には、NICを直接指定する本手法が有効である。
3. おわりに
特定のサーバ経由でしかアクセスできないインスタンス(Windows/Linux問わず)に対してAnsibleを実行できるようになった。