しばたテックブログ

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

PowerShell Remoting over SSHを試す

PowerShell Core 6.0の正式リリースに伴い、本エントリの内容を新しいバージョンでやり直しました。
こちらのエントリを参照してください。

blog.shibata.tech


PowerShell on Linux(Mac)が公開された際の新機能の一つにSSHによるPSRemoting(PowerShell Remoting over SSH)があります。

本エントリではこの機能を試してみます。

PowerShell Remoting over SSHとは

ドキュメントでの表記がPowerShell Remoting over SSHPowerShell Remoting Protocol(PSRP) over SSHPSRP over OpenSSH等と若干揺れているのですが本エントリでは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の仕組みはJobやPowerShell Directと同類でPSRPは(たぶん)使用しておらずWinRMにも依存していません。
このため、あくまでも私見ですが、PSRP over SSHという表記は正確でないと思われます。

このあたりはもう少し時間が経てば公式な情報も増えてくるかと思います。

PowerShell Remoting over SSHを試す

手順はGitHubDocsで公開されているのでこの手順をベースに行います。

https://github.com/PowerShell/PowerShell/tree/master/demos/SSHRemoting

試験環境

試験環境はVirtualBox上でWindows Server 2012 R2とCentOS 7.2.1511を使い双方向で通信させてみます。
これらのOSにした理由は単純に私が使い慣れているだけです。

他のOSでも細かい部分に違いはあるでしょうが基本的には同じ流れになると思います。

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

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

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

最初にWindowsをクライアント、CentOSをサーバーにした場合の接続を試します。

CentOSにPowerShellをインストールします。  

# CentOS bash
# PowerShellのインストール
sudo yum install https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-alpha.9/powershell-6.0.0_alpha.91.el7.centos.x86_64.rpm -y

SSHdの設定変更を行います。
/etc/ssh/sshd_configに次の設定を追加します。

Subsystem powershell powershell -sshs -NoLogo -NoProfile

追加例)

# /etc/ssh/sshd_config

# override default of no subsystems
Subsystem       sftp    /usr/libexec/openssh/sftp-server
Subsystem powershell powershell -sshs -NoLogo -NoProfile

また、必要に応じてパスワード認証、鍵認証の可否を設定します。
今回は試験環境のデフォルト設定のままとしました。

# Boxのデフォルト設定
PasswordAuthentication yes

#RSAAuthentication yes
#PubkeyAuthentication yes

設定変更後はSSHdを再起動します。

# CentOS bash
sudo systemctl restart sshd.service

これでCentOS側の設定は完了です。

次にWindows側の設定をします。
標準でインストールされているPowerShell 4.0ではPowerShell Remoting over SSHできませんので、こちらにもPowerShell Coreをインストールします。

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

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

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

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

これで準備が整いましたので実際に試してみます。

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

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

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

# Windows PowerShell Core
PS C:\Users\Administrator> $Session = New-PSSession -HostName 192.168.33.209 -UserName vagrant
The authenticity of host '192.168.33.209 (192.168.33.209)' can't be established.

ECDSA key fingerprint is SHA256:*******************************************.
Are you sure you want to continue connecting (yes/no)?
Please type 'yes' or 'no':
vagrant@192.168.33.209's password:

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

# Windows PowerShell Core
PS C:\Users\Administrator> $Session

 Id Name            ComputerName    ComputerType    State         Configuration
                                                                  Name
 -- ----            ------------    ------------    -----         -------------
  1 SSH1            192.168.33.209  RemoteMachine   Opened        DefaultShell

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

# Windows PowerShell Core
Enter-PSSession -Session $Session

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

# Windows PowerShell Core → CentOS PowerShell
PS C:\Users\Administrator> Enter-PSSession -Session $Session
[192.168.33.209]: PS /home/vagrant> cat /etc/redhat-release
CentOS Linux release 7.2.1511 (Core)
[192.168.33.209]: PS /home/vagrant>

ちなみに既知の不具合としてリモートだとClear-Hostが使えません。
これはそのうち直ると思います。

# Windows PowerShell Core → CentOS PowerShell
# Clear-Hostはエラーになる
[192.168.33.209]: PS /home/vagrant> Clear-Host
TERM environment variable not set.

とりあえずエラーメッセージの通りに

$env:TERM="vt100"

等とTERM環境変数を適当に設定しておけば対処は可能です。

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

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

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

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

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

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

# Windows PowerShell 4.0 (Coreでもたぶん良い)
PS C:\> cd C:\OpenSSH-Win64\
# ssh-keygen
PS C:\OpenSSH-Win64> .\ssh-keygen.exe -t rsa -b 4096
Generating public/private rsa key pair.
Enter file in which to save the key (C:\Users\Administrator/.ssh/id_rsa):
Enter passphrase (empty for no passphrase): ********
Enter same passphrase again: ********
Your identification has been saved in C:\Users\Administrator/.ssh/id_rsa.
Your public key has been saved in C:\Users\Administrator/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:******************************************* Administrator@WIN-7JINTK1PS1K

The key's randomart image is:
+---[RSA 4096]----+
|                 |
|                 |
|                 |
|                 |
|                 |
|                 |
|                 |
|                 |
|                 |
+----[SHA256]-----+

あとは作った公開鍵(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-KeyPathパラメーターを使用します。

実行例は以下

# Windows PowerShell Core
# エラーが出なければ成功
PS C:\> $Session = New-PSSession -HostName 192.168.33.209 -UserName vagrant -KeyPath ~\.ssh\id_rsa
Enter passphrase for key 'C:\Users\Administrator\.ssh/id_rsa': ********

# セッション情報を確認
PS C:\> $Session

 Id Name            ComputerName    ComputerType    State         Configuration
                                                                  Name
 -- ----            ------------    ------------    -----         -------------
  1 SSH1            192.168.33.209  RemoteMachine   Opened        DefaultShell

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

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

最後に、あまり需要があるとは思いませんが、CentOSをクライアント、Windowsをサーバーにした場合の接続を試します。
面倒なのでパスワード認証のみとします。

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

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

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

# ホストキーの作成
.\ssh-keygen.exe -A

# Firewall設定
New-NetFirewallRule -Protocol TCP -LocalPort 22 -Direction Inbound -Action Allow -DisplayName SSH

# 鍵認証周りの設定+再起動
.\install-sshlsa.ps1
Restart-Computer

sshd_configの設定はCentOSの場合と同様でサブシステムにPowerShell(Coreの方)を登録します。

Subsystem powershell C:/Program Files/PowerShell/6.0.0.9/powershell.exe -sshs -NoLogo -NoProfile

追加例)

# C:\OpenSSH-Win64\sshd_config

# override default of no subsystems
Subsystem   sftp    C:/Program Files/OpenSSH/sftp-server.exe
Subsystem powershell C:/Program Files/PowerShell/6.0.0.9/powershell.exe -sshs -NoLogo -NoProfile

設定後はサービスを再起動します。

# Windows PowerShell 4.0(Coreでもよい)
Restart-Service sshd

これで準備は完了です。

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

# CentOS PowerShell
PS /home/vagrant> $Session = New-PSSession -HostName 192.168.33.210 -UserName administrator
The authenticity of host '192.168.33.210 (192.168.33.210)' can't be established.
ECDSA key fingerprint is ***********************************************.
Are you sure you want to continue connecting (yes/no)? yes
administrator@192.168.33.210's password: ********

セッション情報はこんな感じです。

# CentOS PowerShell
PS /home/vagrant> $Session

 Id Name            ComputerName    ComputerType    State         ConfigurationName     Availability
 -- ----            ------------    ------------    -----         -----------------     ------------
  1 SSH1            192.168.33.210  RemoteMachine   Opened        DefaultShell             Available

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

# CentOS PowerShell
PS /home/vagrant> Enter-PSSession -Session $Session
[192.168.33.210]: PS C:\Users\Administrator\Documents>

ここでWindows側でPowerShell Coreが実行されていのですが、現時点ではまだ、PowerShell Core→PowerShell 4.0(Desktop)とネストしてPowerShellを対話的に実行できない様で、実際にやってみるとハングしてしまいます。

ただし、powershell.exe -Command {ほげ}の様にコマンド実行であればネストして実行可能ですので、以下の例の様にPowerShell Coreにないコマンドレット(Get-NetFirewallRule)を実行することもできます。

# CentOS PowerShell → Windows PowerShell Core

# コマンドの実行結果に日本語があるのでShift-JISを有効に
[192.168.33.210]: PS C:\> [System.Text.Encoding]::RegisterProvider([System.Text.CodePagesEncodingProvider]::Instance)
[192.168.33.210]: PS C:\> $OutputEncoding = [System.Text.Encoding]::GetEncoding(932)

# CentOS PowerShell → Windows PowerShell Core → Windows PowerShell 4.0
[192.168.33.210]: PS C:\> powershell -Command { Get-NetFirewallRule -DisplayName *ssh* }


Name                    : {b467a636-88e4-4478-a408-e43a87356821}
ID                      : {b467a636-88e4-4478-a408-e43a87356821}
Group                   :
Platform                : {}
LSM                     : False
Profile                 : Any
Caption                 :
Description             :
ElementName             : SSH
InstanceID              : {b467a636-88e4-4478-a408-e43a87356821}
CommonName              :
PolicyKeywords          :
Enabled                 : 1
PolicyDecisionStrategy  : 2
PolicyRoles             :
ConditionListType       : 3
CreationClassName       : MSFT|FW|FirewallRule|{b467a636-88e4-4478-a408-e43a87356821}
ExecutionStrategy       : 2
Mandatory               :
PolicyRuleName          :
Priority                :
RuleUsage               :
SequencedActions        : 3
SystemCreationClassName :
SystemName              :
Action                  : 2
Direction               : 1
DisplayGroup            :
DisplayName             : SSH
EdgeTraversalPolicy     : 0
EnforcementStatus       : {0}
LocalOnlyMapping        : False
LooseSourceMapping      : False
Owner                   :
Platforms               : {}
PolicyStoreSource       : PersistentStore
PolicyStoreSourceType   : 1
PrimaryStatus           : 1
Profiles                : 0
RuleGroup               :
Status                  : 規則は、ストアから正常に解析されました。
StatusCode              : 65536
PSComputerName          :

最後に

とりあえずこんな感じです。
WindowsからLinuxへのPSRemoting、なかなか胸が熱くなります。

実はWindowsからLinuxへPSRPを使用した接続も可能なのですが、まだバギーな部分が多い様で、このあたりについては別の機会にエントリを書こうと思います。

【2016/09/16追記】

続き書きました。

stknohg.hatenablog.jp

【2017/08/15追記】

Windows 10 Creators Updateでも使える様になっていたので別エントリを起こしました。

blog.shibata.tech