LinuxとWindowsが混在するシステムをリモートから管理する場合、前者はSSH、後者はリモートデスクトップかWinRMと、使い分けているのではないだろうか。これは意外と面倒だと感じていると思う。実はWindows ServerでもSSHに接続することは可能だ。Azureの仮想マシン(VM)をデプロイする際に、公開鍵認証でSSH接続ができるようにする方法を紹介する。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
対象:Azure Virtual Machine(仮想マシン)、Windows Server 2019/Windows Server 2022、Bicep
LinuxやWindows Serverをリモートから管理する場合、LinuxにはSSHで、Windows ServerにはリモートデスクトップやWinRMでそれぞれ接続して管理することが多いだろう。
しかし、Windows ServerとLinuxが混在しているシステム、それもLinuxの方が多い場合は、Windows Serverだけ例外としてSSH以外の方法で管理するのが面倒だと感じないだろうか?
実は、Windows Server 2019以降のWindows Server OSには、OpenSSHのクライアント/サーバ機能が標準装備されている。ちょっとした作業でOpenSSHサーバをインストールして、リモートからSSHで接続できるのだ。
そこで本Tech TIPSでは、Windows Server 2019/2022をAzureの仮想マシン(VM)にインストールするという前提で、デプロイ中に自動でOpenSSHサーバをセットアップして、リモートから公開鍵認証を経てSSHで接続できるようにする方法を紹介する。
管理が主目的なので、SSHで接続できるようにするのは、Azure VMのデプロイ時に生成される管理アカウントとする(管理権限を持たない一般ユーザーは対象外とする)。
まずは、Windows ServerのVMにSSHで接続するための管理アカウント向け公開鍵/秘密鍵のペアを生成する。後述するVMのデプロイ時に公開鍵が必要なので、事前に用意しておく必要がある。
公開鍵/秘密鍵は「ssh-keygen」コマンドで生成するのが簡単だ(Windows 10/11の場合、SSHクライアントをインストールすると、ssh-keygenコマンドが使えるようになる)。
ssh-keygen -t ed25519 -f <鍵ファイル名> -C "<コメント>"
これでed25519署名の秘密鍵が<鍵ファイル名>というファイルに、同様に公開鍵が<鍵ファイル名>.pubにそれぞれ格納される。いずれもOpenSSHでそのまま使える形式で出力される。
次にWindows ServerのVMを生成しよう。以下、SSHと公開鍵認証に関係する部分に絞って説明していく。その他の部分は適宜補完していただきたい。またデプロイには、AzureリソースマネージャーとBicepを利用している。
param location string = resourceGroup().location
param vmName string // 仮想マシンの名前
param computerName string // コンピュータ名
param adminUsername string // 管理アカウントのユーザー名
@secure()
param adminPassword string // 管理アカウントのパスワード
param adminPublicKey string = loadTextContent('id_ed25519.pub')
// デフォルトでは「id_ed25519.pub」に記載の公開鍵が用いられる
// loadTextContent()では変数でファイル名を指定できないので注意
// リソース生成: 仮想マシン
resource vm 'Microsoft.Compute/virtualMachines@2023-03-01' = {
name: vmName
location: location
properties: {
osProfile: {
computerName: computerName
adminUsername: adminUsername
adminPassword: adminPassword
windowsConfiguration: { /* …… 省略 ……*/ } // Windows OS特有の設定
}
hardwareProfile: { /* …… 省略 ……*/ }
storageProfile: { /* …… 省略 ……*/ }
networkProfile: { /* …… 省略 ……*/ }
}
}
// OpenSSHサーバの設定ファイルなどのパス
var winSSHAdminPath = 'C:/ProgramData/ssh'
var winSSHAdminAuthKeys = '${winSSHAdminPath}/administrators_authorized_keys'
var winSSHHostKeys = '${winSSHAdminPath}/ssh_host_*_key.pub'
// 実行するコマンドライン(PowerShell)
var cmdLineSetupSSHServer = 'Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0; while(!(Get-Service sshd -ErrorAction SilentlyContinue)) { Start-Sleep 1; } Start-Service sshd; Set-Service sshd -StartupType Automatic; Write-Output \'${adminPublicKey}\' | Out-File ${winSSHAdminAuthKeys} -Encoding ASCII; icacls.exe ${winSSHAdminAuthKeys} /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F"; Get-Content ${winSSHHostKeys}'
// リソース生成: OpenSSHサーバをインストールして公開鍵認証をセットアップする
resource runCmdSetupSSHServer 'Microsoft.Compute/virtualMachines/runCommands@2023-03-01' = {
parent: vm // コマンドを実行する仮想マシンのリソース
name: 'SetupSSHServer' // このコマンドに付ける名前
location: location
properties: {
source: {
script: cmdLineSetupSSHServer // sshdのインストールとセットアップ
}
timeoutInSeconds: 1200 // 最長20分まで待つ
}
}
上記リストの変数「cmdLineSetupSSHServer」に格納しているのが、OpenSSHサーバ(以下、「sshdサービス」)のインストールや公開鍵の設定をするためのコマンドラインである。Windows OSの場合、これはPowerShellで記述する必要がある。各コマンドレットの目的は以下の通りだ。
# 変数
$adminPublicKey = Get-Content "id_ed25519.pub" -Encoding ASCII
$winSSHAdminPath = "C:/ProgramData/ssh"
$winSSHAdminAuthKeys = "${winSSHAdminPath}/administrators_authorized_keys"
$winSSHHostKeys = "${winSSHAdminPath}/ssh_host_*_key.pub"
# Windows Serverの標準機能であるOpenSSHサーバ(sshdサービス)をインストール
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
#サービスとして認識されるまで待つ
while(!(Get-Service sshd -ErrorAction SilentlyContinue)) { Start-Sleep 1; }
# sshdサービスを起動
Start-Service sshd
# sshdサービスが自動的に起動するように設定を変更
Set-Service sshd -StartupType Automatic
# 管理アカウント用の公開鍵を所定のパスに保存
Write-Output ${adminPublicKey} | Out-File ${winSSHAdminAuthKeys} -Encoding ASCII
# 公開鍵ファイルを管理者とシステムアカウントだけがアクセスできるように制限
icacls.exe ${winSSHAdminAuthKeys} /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F"
# C:\ProgramData\sshフォルダにあるホストキーを表示
Write-Output "----- SSH Host Keys -----"
Get-Content ${winSSHHostKeys}
VM以外のリソースについても、注意すべき点を簡単に説明しておく。
ネットワークインタフェース(NIC)に割り当てる「ネットワークセキュリティグループ」については、最低限、SSHの受信を許可するためのセキュリティルール(受信ポートの規則)を追加する必要がある。
// リソース生成: ネットワークセキュリティグループ
resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2023-04-01' = {
name: 'nsg-${vmName}'
location: location
properties: {
securityRules: [
{
name: 'allow-ssh'
properties: {
description: 'Allow the SSH Connection'
protocol: 'TCP'
sourcePortRange: '*'
destinationPortRange: '22' // SSHのデフォルトの着信ポート番号
destinationAddressPrefix: '*'
access: 'Allow' // 許可
priority: 1000
direction: 'Inbound' // 内向き
sourceAddressPrefixes: [] // 接続を許可するソースIPアドレスを列挙
}
}
]
}
}
必要なら、RDPやWinRMについても、許可するセキュリティルールをネットワークセキュリティグループに追記していただきたい。
インターネット経由でWindows Server VMに接続するなら、パブリックIPアドレスも生成してNICにひも付ける必要がある。
// ↓ホスト名を指定。デフォルトは「<仮想マシン名>-<ユニークな英数字>」
param hostName string = toLower('${vmName}-${uniqueString(resourceGroup().id)}')
@allowed([
'Basic' // 動的IPアドレスが必要ならこちらを選択
'Standard' // IPアドレスは固定のみ
])
param pipSKU string = 'Basic'
@allowed([
'Dynamic' // 動的IPアドレス
'Static' // 固定IPアドレス
])
param pipAllocationMethod string = pipSKU == 'Standard' ? 'Static' : 'Dynamic'
param pipIdleTimeoutInMinutes int // アイドル時のタイムアウト秒数を指定
// リソース生成: パブリックIPアドレス
resource publicIPAddress 'Microsoft.Network/publicIPAddresses@2023-02-01' = {
name: 'pip-${vmName}'
location: location
sku: {
name: pipSKU
}
properties: {
publicIPAllocationMethod: pipAllocationMethod
publicIPAddressVersion: 'IPv4'
dnsSettings: {
domainNameLabel: hostName // FQDNは「<ホスト名>.<Azureのドメイン>」になる
}
idleTimeoutInMinutes: pipIdleTimeoutInMinutes
}
}
デプロイ後に必要となる情報については、以下のような「output」の行を加えて、デプロイ完了時に表示される出力に追加しておくとよい。
// コンピュータ名
output computerName string = vm.properties.osProfile.computerName
// 管理アカウント名
output adminUsername string = vm.properties.osProfile.adminUsername
// SSHクライアントのコマンドライン
output sshCommand string = 'ssh ${adminUsername}@${publicIPAddress.properties.dnsSettings.fqdn} -i id_ed25519'
デプロイ時に実行したコマンドの標準出力やエラーの有無を確認するには、以下のAzure CLIコマンドを実行する。
az vm run-command show -o jsonc -g <VMのリソースグループ名> --vm-name <VM名> --run-command-name SetupSSHServer --instance-view
コマンドが正しく実行できていたら、以下の画面のように、「instanceView.output」にホストキーなどが出力されているはずだ。一方、エラーが生じた場合は、「instanceView.error」の値で詳細を確認できる(エラーが発生しなかった場合はnullが入る)。
「instanceView.output」にあるホストキーは、Tech TIPS「【Azure】Linux VMのSSHサーバ(sshd)のホストキー(公開鍵)を安全に確認するには」で説明している手順で、SSHクライアントのknown_hostsに登録しておく。
VMのデプロイが正しく完了したら、SSHで接続してみよう。それには、前述のOutputで出力したSSHクライアントのコマンドラインをコピーして実行する。
ssh <ユーザー名>@<VMのホスト名(FQDN)> -i <秘密鍵ファイル>
秘密鍵に設定したパスフレーズを入力して認証に成功すると、SSH接続が確立し、コマンドプロンプトが表示されるはずだ。
■関連リンク
Copyright© Digital Advantage Corp. All Rights Reserved.