EC2 Windows ServerにSSH接続する構成をPackerとTerraformに落とし込む
要約
主に以下の事項について、どのようにやったかという点について記載しています。
- Packerを使用して、AWS EC2 Windows Server 2019, 2022を構築、WinRMではなく、OpenSSHサーバ(Win OpenSSH)に接続にいく
- Userdata(powershell)内でOpenSSHの設定(鍵の登録、パスワード認証切るとか)、ユーザー作る、その他諸々する
- Terraformで確認用のインスタンスを立てる
GitHub repo
フォルダ構成についてはREADME.mdにも記載していますが、主に以下の構成となります。
- packer ディレクトリ - Windows ServerのAMI作る為の構成
- terraform ディレクトリ - 確認用のEC2インスタンス、及びアタッチするセキュリティグループやキーペア関連の構成
- Makefile, .env - コマンドショートカット(make ami, make ec2 とか)用、使用する場合
.env
にVPC_IDとSUBNET_IDを書いておく前提
動機
検証用のEC2 Windows Server動かしたいから, PackerでAMI作れるようにして、Terraformで構成を書いておこうと思ったのがきっかけです。
- PackerでのAMI作成時に、WinRM接続ではなく,SSH接続にしたかった
Packer公式のチュートリアルだとWinRMを使用した方法が記載されています。
ここでは,WinRMの設定を実行していますが、「基本認証」「暗号化無効」の設定をしているので文字通り
やり取りされるデータは暗号化されません。
「手元(ローカルマシン)からWinRM接続で,このままインスタンスの設定をするにはセキュリティ的に不安」
かつ
「当然設定を行う事で暗号化された通信はWinRMでも可能だが、WinRMの設定のチューニングの経験等があまりない」
という状態だったの、しばらくほかの方法を探していました。
そこで、下記のページにたどりつきました。
記載されている通り、Windows Server2019から OpenSSH(クライアント/サーバ)がFODを介してインストールできます。
ということなので、「これはssh接続もいけそうだな」と思って試してみたというのが事の経緯です。
AMIのビルドはpacker build .
で完結するように(正確にはVPC_ID等の環境向けの変数入力が必要ですが)
確認用のインスタンス立てるのとSSHのテストはterraform apply
で完結させる為に、この構成にしました。
以下構成別に分けて見ていきたいと思います。
構成毎のポイント
Packer
鍵はプラグイン「sshkey」のお力を借りて生成
最初はssh-keygen叩いて、出力した鍵を同じディレクトリ内に設置していましたが、鍵を一緒に管理してくれるプラグインがあったのでPackerの構成に含めています。PACKER_CACHE_DIR
の環境変数のパスに鍵を生成してくれるので、これはそのままカレントディレクトリを指定しています。
OpenSSHサーバの起動や、公開鍵の登録、ユーザーの作成諸々は全部userdata内のpowershellで実行する
- OpenSSHの設定
OpenSSH、インストール、サーバの起動(サービス起動)、WindowsDefenderのルールでポート空ける等の基本設定は下記のドキュメントに記載されています。
この内容をベースにuserdataで実行されるPowershellとして記載し、肉付けしたものが
userdata.tpl
です。
userdata.tpl(PowerShell内での設定内容)
authorized_keysに関しての以下の説明を参考にし
authorized_keys(administrators_authorized_keys)ファイルを設置し、権限を設定しています。パスワード認証はOFFに設定しています。
#Disable PasswordAuthentication
(Get-Content -Encoding Ascii $env:programdata\ssh\sshd_config) | foreach { $_ -replace "#PasswordAuthentication yes","PasswordAuthentication no" } | Set-Content $env:programdata\ssh\sshd_config
- ユーザーを作成してみて、そちらのユーザーでもSSHログインできるようにする
ユーザープロファイル(C:\Users\ユーザー)を作成する為に Win32 API CreateProfileを呼び出します。
以下が参考になります
要約すると
- PowerShell Add-Typeを介して.NET(C#)のクラス定義を読み込み
- 上記クラス定義(System.Runtime.InteropServices)を介して, userenv.dllで定義されているCreateProfileの定義を読み込み
- 最終的にCreateProfileに引数(ユーザー名)を与えて、呼び出し
というような処理が行われています。
これを見たとき、PowerShellでこんな事もできるんだなと感心しました。
userdataをインスタンスに設定する
あとはuserdata.tplをテンプレートとし、Packerのbuiltin function「templatefile」を使用して
必要な箇所をPackerの変数に置換した最終的な文字列をインスタンスのuserdataとして、設定します。
source "amazon-ebs" "windows_ssh" {
~~~~~~~~~
user_data = templatefile(var.userdata_template_path,
{
admin_key = data.sshkey.admin_key.public_key,
user_name = var.normal_user_account,
user_key = data.sshkey.normal_user_key.public_key
}
)
~~~~~~~~~~
この状態でpacker buildを実行する事で
AMI作成開始
↓
OpenSSHが立ち上がるまで待機
↓
ユーザー:Administratorでssh接続
↓
powershellのprovisionerが走る(Administratorのパスワードを設定しています)
という形の動作をする事が確認できます。
Terraform
terraform側は確認用のインスタンスを生成,及びssh接続をテストする為の構成です。
- 確認用インスタンス生成
module ec2_instanceのお力を借ります
インスタンスの設定に関しては、デフォルトでは
-
キーペアは
resource "aws_key_pair" "instance_key"
の記述にしたがって
key-windows-ssh
というキーペアとして登録されます。
登録される公開鍵はAdministrator用に生成されたキーです。
Makefile:ADMIN_KEY
をご参照ください。 -
セキュリティグループでグローバルからのSSH(22) Inboundトラフィックが許可されます。
今回検証した、Windows Server2019, 2022のインスタンスはデフォルトでSSM Agentがインストールされているので、
適切なポリシーを割り振ったIAM Roleのインスタンスプロファイルを割り当てる事でSSM経由でSSH接続が可能です。
インスタンスのInbound 22番ポートを0.0.0.0/0
であけずに、SSH接続が手元からできます。
ですが、後述するsshの接続確認も.tfの構成内に含めて置きたかった点、及びやりたい事の本筋ではなかった為上記セキュリティグループをアタッチしています。
- ssh接続確認
以下の箇所でAdministrator、及び作成したユーザー(デフォルトだとuser_a)のssh接続確認をしています
resource "null_resource" "check_ssh_connectivity_admin"{
~~~
}
resource "null_resource" "check_ssh_connectivity_normal"{
~~~
}
- variable
with_ssh_check
が有効な場合のみ、リソースが作成され接続確認が行われます - connectionブロック内の
target_platform
はwindows
で指定が必要です
https://www.terraform.io/language/resources/provisioners/connection#argument-reference
If the platform is set to windows, the default script_path is c:\windows\temp\terraform_%RAND%.cmd, assuming the SSH default shell is cmd.exe. If the SSH default shell is PowerShell, set script_path to "c:/windows/temp/terraform_%RAND%.ps1"
と記載がある通り、もしsshログイン時のデフォルトログインShellをcmdからPowershellに変更した場合
script_path
もc:/windows/temp/terraform_%RAND%.ps1
と指定が必要です。
今回はcmdのまま動かしているので、target_platformのみ指定しています。
New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -PropertyType String -Force
- ssh経由でコマンド実行する
単に"echo connected using ssh ${アカウント名}"を実行しています
所感
冷静になって考えてみると「素直にWinRMの暗号化通信設定をする」とか
「手元でPacker動かさず、PVC内で動作する踏み台サーバでPacker走らせる」とか
他の手段でも良かったなという気がします。
Discussion