しばたテックブログ

気分で書いている技術ブログです。

PowerShell Remoting over SSHを試す - 再び

以前に試した

blog.shibata.tech

ですが、PowerShell Core 6.0が正式リリースされ当時の内容では正しく動作しなくなっていたので改めてやり直しました。

PowerShell Remoting over SSHとは

従来のPowerShell Remoting Protocol(PSRP)では通信の下回りにHTTP/HTTPSを使いますが、PowerShell Remoting over SSHでは名前の通りSSHを使った通信を行います。

基本的な仕組みとしては、

  1. クライアント側はpowershell(.exe)がリモートセッション生成時にssh(.exe)を呼び出し、powershell(.exe)→ssh(.exe)のプロセス間通信を経由してサーバーへの通信を行う。

  2. サーバー側はSSHdのサブシステムにPowerShellを登録。
    クライアントからの接続があった場合はsshd(.exe)→powershell(.exe)とリモートシェルが実行される。

となっています。

PowerShell Remoting over SSHを試す

手順はGitHubで公開されているのでこの手順をベースに行います。
https://github.com/PowerShell/PowerShell/tree/master/demos/SSHRemotinggithub.com

【2018/05/20更新】
いつのまにかGitHubの手順が削除されてしまいました... (#6628)
手順は一致しないかもしれませんが、Docsのこちらの手順が参考になりますので代わりにご覧ください。

もしくは削除直前のこちらのバージョンをご確認ください。

試験環境

試験環境はVirtualBox上でWindows Server 2012 R2とCentOS 7.4.1708を使い双方向で通信させてみます。
他のOSでも細かい部分に違いはあるでしょうが基本的には同じ流れになると思います。

その他細かい条件は以下。

項目 Windows Server 2012 R2 CentOS 7.4.1708
基本設定 インストール直後の状態+最新のWindows Updateを実施 bento/centos7.4のBoxにyum updateを実施
IP 192.168.33.210 192.168.33.209
ユーザー vagrant, administrator vagrant, root

Windows → CentOSへの通信(パスワード認証)

CentOS側の設定

CentOSにPowerShell Coreをインストールして、sshd_configの設定を変更します。
パスワード認証、鍵認証の可否については試験環境のデフォルト設定のままとしました。

# CentOS PowerShell for Linuxのインストール
curl https://packages.microsoft.com/config/rhel/7/prod.repo | sudo tee /etc/yum.repos.d/microsoft.repo
sudo yum install -y powershell

# /etc/ssh/sshd_config の設定
# '# override default of no subsystems'のコメント行の下に設定を追加
# ※位置指定をかなり決め打ちにしているので注意
sudo sed -i.orig -e "/# override default of no subsystems/a Subsystem powershell /usr/bin/pwsh -sshs -NoLogo -NoProfile" /etc/ssh/sshd_config

# sshdの再起動
sudo systemctl restart sshd.service

Windows側の設定

標準でインストールされているPowerShell 4.0ではPowerShell Remoting over SSHできませんので、こちらにもPowerShell Coreをインストールします。
前回と比べてGitHubへの接続にTLS 1.2が必須となったため、Windows PowerShell側の設定を若干変更する必要があります。

# Windows PowerShell 4.0
# GitHubへ接続するためにTLS 1.2を有効化
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [System.Net.SecurityProtocolType]::Tls12

# PowerShell Coreのインストール
Invoke-WebRequest -Uri "https://github.com/PowerShell/PowerShell/releases/download/v6.0.1/PowerShell-6.0.1-win-x64.msi" -OutFile "PowerShell-6.0.1-win-x64.msi"
Start-Process -FilePath msiexec.exe -ArgumentList @("/i", "$($pwd.Path)\PowerShell-6.0.1-win-x64.msi", "/passive") -Wait -PassThru

次にSSHをインストールします。 Zipの解凍をしたいので先ほどインストールたPowerShell Coreから以下の手順でインストールします。

# PowerShell Core 6.0.1
# ssh.exeのインストール 
Invoke-WebRequest -Uri "https://github.com/PowerShell/Win32-OpenSSH/releases/download/v7.6.0.0p1-Beta/OpenSSH-Win64.zip" -OutFile "OpenSSH-Win64.zip"
Expand-Archive -Path ".\OpenSSH-Win64.zip" -DestinationPath "C:\"
[Environment]::SetEnvironmentVariable('PATH', [Environment]::GetEnvironmentVariable('PATH') + ';C:\OpenSSH-Win64', 'Machine')

インストール先はC:\OpenSSH-Win64としましたが、こちらについては必要に応じて変更しても構いません。
また、PowerShell Remoting over SSHではssh.exeに対してPATHが通っていること(正確にはssh.exeだけでコマンド呼び出しができること)が必須であるためPATHは必ず通す様にしてください。

テスト接続

ここから実際に試してみます。

まずはNew-PSSessionでPSセッションを作成します。 -HostName(-ComputerNameではない)、-UserNameパラメーターを指定するとPowerShell Remoting over SSHで通信することになります。

# PowerShell Core
$Session = New-PSSession -HostName 192.168.33.209 -UserName vagrant

実行すると以下の様にssh.exeを使ってサーバーに接続する際のあれやこれやを聞かれるので適切な情報を入力していきます。

f:id:stknohg:20180312193552p:plain

入力後、エラーが出なければ成功です。
$Sessionの中身を確認すると以下の様になり、名前がSSHになっていることがわかります。
このセッションがssh.exeと紐づいています。

f:id:stknohg:20180312193616p:plain

この$sessionを使いEnter-PSSessionしてリモート接続します。

# PowerShell Core
Enter-PSSession -Session $Session

実行結果は以下の様になり、無事CentOSにリモート接続できました。

f:id:stknohg:20180312193640p:plain

リモートからClear-Hostが使えないのは相変わらずでした...
TERM環境変数を適当に設定してみましたが、Clear-Hostしてもエスケープシーケンスが表示されるだけなのでOSが対応しないとダメそうな雰囲気を感じます。
(Windows10やWindows Server 2016だと上手くいくやも?)

最後にRemove-PSSessionしてセッション情報を削除すると紐づいていたssh.exeも終了します。

# PowerShell Core
Remove-PSSession -Session $Session
  
# 個別に消すのが面倒なら以下で一気に消しても良い
Get-PSSession | Remove-PSSession

Windows → CentOSへの通信(鍵認証)

鍵認証で通信する場合、基本的にSSHの鍵認証の方法と同一です。

先ずはWindows側でssh-keygen.exeで鍵を作ります。
鍵の種類やパスフレーズの有無は適当にしてください。

以下の実行例ではRSA2 4096Byte、パスフレーズ有りにしています。

# Windows PowerShell 4.0 / PowerShell Core どちらでも可
#  作成する鍵の設定は適当に
cd C:\OpenSSH-Win64\
.\ssh-keygen.exe -t rsa -b 4096

f:id:stknohg:20180312193705p:plain

あとは作った公開鍵(id_rsa.pub)をCentOS側に転送し~/.ssh/authorized_keysに追加するだけです。 以下の例では/vagrant/id_rsa.pubに鍵を転送しています。

# CentOS bash
# ユーザーはvagrantユーザー
cat /vagrant/id_rsa.pub >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

これで鍵認証の準備は完了です。
鍵認証で接続する場合はNew-PSSession-KeyFilePathパラメーターを使用します。

実行例は以下

# PowerShell Core
$Session = New-PSSession -HostName 192.168.33.209 -UserName vagrant -KeyFilePath ~\.ssh\id_rsa

f:id:stknohg:20180312193737p:plain

セッションが生成された後はパスワード認証の場合と同様です。

CentOS → Windowsへの通信(パスワード認証)

最後に、CentOSをクライアント、Windowsをサーバーにした場合の接続を試します。
前回同様パスワード認証のみとします。

まずはWindows上でsshdを動作させます。 基本的にはGitHub上のインストール手順に従うだけなので問題ないかと思います。

# Windows PowerShell 4.0
cd C:\OpenSSH-Win64\

# sshdのインストール
.\install-sshd.ps1

# Firewall設定
New-NetFirewallRule -Name sshd -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22

# サービス起動
Start-Service sshd

sshd_configの設定はCentOSの場合と同様でサブシステムにPowerShell(Coreの方)を登録します。
なお、sshd_configC:\ProgramData\ssh\sshd_configを修正してください。(これはVer.1.0.0.0-betaからの変更になります)

追加例 )

# C:\ProgramData\ssh\sshd_config

# override default of no subsystems の欄に追記
Subsystem   sftp    sftp-server.exe
Subsystem powershell c:/program files/powershell/6.0.1/pwsh.exe -sshs -NoLogo -NoProfile

あとパスワード認証も許可しておきます。

# コメント解除
PasswordAuthentication yes

サービスを再起動して設定を反映させます。

Restart-Service sshd

これで準備は完了です。

CentOSからWindowsへ接続してみます。

# CentOS PowerShell Core
$Session = New-PSSession -HostName 192.168.33.210 -UserName administrator

セッション情報はこんな感じになります。

f:id:stknohg:20180312193850p:plain

Enter-PSSessionでWindowsへ接続できます。

# CentOS PowerShell Core
Enter-PSSession -Session $Session

f:id:stknohg:20180312193909p:plain

前回同様にPowerShell Core→PowerShell 4.0(Desktop)とネストしてPowerShellを対話的に実行できないままで、powershell.exe -Command {ほげ}の様にコマンド実行であればネストして実行可能でした。