しばたテックブログ

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

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 {ほげ}の様にコマンド実行であればネストして実行可能でした。

Windows Subsystem for Linux(Ubuntu)にRe:VIEWをインストールする

github.com

流行りのRe:VIEWを試してみたくなったので手元のWindows 10にインストールしてみました。

Re:VIEW on Windows

Windows上でRe:VIEWを試すには色々な方法があり、公式にはDocker Toolboxを使う方法が紹介されています。
今回私はWindows Subsystem for Linux(WSL)のUbuntu上にRe:VIEWをインストールする方法を採りました。

環境は以下となります。

  • 最新のWindows Updateを適用した64bit版 Windows 10 Pro (1709)
  • WSL上のUbuntu 16.04

Re:VIEWのインストール

本エントリではWSL上のUbuntuを用意する手順は端折ります。
MicrosoftストアからUbuntuをインストールして初期ユーザー設定を済ませた状態をスタート地点とします。

rbenvとRubyのインストール

Re:VIEWはRuby製アプリなので最初にRubyの実行環境を用意します。
以下のドキュメントを参考にしてrbenvとRubyをインストールします。

WSLのUbuntu(bash.exe)を起動し以下のコマンドを順に実行します。
今回はRuby 2.5.0をインストールしました。

# 前準備の apt-get update
sudo apt-get update -y
# rbenvのコンパイルに必要
sudo apt-get install -y gcc make
# Rubyのビルドに必要。バージョンによって変わるやも?
sudo apt-get install -y libssl-dev libreadline-dev zlib1g-dev

# rbenvのインストール
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
chmod go-w -R ~/.rbenv
cd ~/.rbenv && src/configure && make -C src
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
source ~/.bash_profile

# ruby-buildのインストール (rbenv installコマンドに必要)
mkdir -p "$(rbenv root)"/plugins
git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build

# Rubyのインストール
#   今回は Ruby 2.5.0をインストール
rbenv install 2.5.0

# システム全体でのデフォルトバージョンを指定
rbenv global 2.5.0

Re:VIEWインストールの前準備

WSL側から見たWindows(/mnt/c/配下のディレクトリ)の任意の場所にRe:VIEW用のディレクトリを作ります。
今回は/mnt/c/Temp/review(C:\Temp\review)を対象します。

# Windows側のディレクトリにファイルを作る
mkdir /mnt/c/Temp/review

Re:VIEWのインストール

前項で作成したディレクトリに移動してrbenvの設定とRe:VIEWのインストールを行います。
インストール自体はgem installでサクッと行けます。

# 移動
cd /mnt/c/Temp/review/

# このディレクトリで利用するRubyのバージョンを設定
rbenv local 2.5.0
# Re:VIEWのインストール
rbenv exec gem install review

(オプション)TeX Liveのインストール

Re:VIEWでPDFのドキュメントを生成する場合システムにTeXがインストールされている必要があります。
(PDFを出力しない場合は不要です)

本エントリでは以下のサイトの手順を参考にTeX Liveをインストールします。

TeX Liveのインストールはapt-get installでも可能ですが、最新のバージョンを使おうと思いネットワークインストーラを使用したインストールにしました。

# TeX Liveのインストール
cd ~
# 今回はJAISTを選んでいますが、ダウンロード先のアドレスはミラーサイトから適当に選んでください
curl -O http://ftp.jaist.ac.jp/pub/CTAN/systems/texlive/tlnet/install-tl-unx.tar.gz
tar xvf install-tl-unx.tar.gz
cd install-tl*
sudo ./install-tl --repository http://ftp.jaist.ac.jp/pub/CTAN/systems/texlive/tlnet/

# あとはインストーラーの指示に従う。
# 今回はデフォルト設定のままインストール続行。

# インストール後パス追加
sudo /usr/local/texlive/????/bin/*/tlmgr path add

また、現時点のRe:VIEW 2.5.0でソースコードのリストを使う場合、PDF生成時にjlisting.styが不足している旨のエラーが出ます。
こちらは以下のIssueでも認識されておりRe:VIEW 3.0で対処する見込みの様です。

とりあえず現状は公開されているサイトからjlisting.styをダウンロードして追加する必要があるので、以下のコマンドで追加しておきます。

# ソースコードのリストを使う場合はjlisting.styを追加
curl -OL https://ja.osdn.net/projects/mytexpert/downloads/26068/jlisting.sty.bz2
bzip2 -d ./jlisting.sty.bz2
sudo mv ./jlisting.sty /usr/local/texlive/2017/texmf-dist/tex/latex/listings
sudo chmod 644 /usr/local/texlive/2017/texmf-dist/tex/latex/listings/jlisting.sty
sudo mktexlsr

これで一通りのインストールは完了です。

Re:VIEWを試す

最後にインストールしたRe:VIEWを試してみます。

雛形作成

review-initコマンドでドキュメントの雛形を作成できますので適当な名前で作成します。
(今回はsample-docにしました)

cd /mnt/c/Temp/review/

# サンプル作成
review-init sample-doc

Windows側のディレクトリにサンプルを作っているのでVisual Studio Codeから内容を確認することができます。
Re:VIEW拡張をインストールしてればプレビュー表示も可能です。

f:id:stknohg:20180309174342p:plain

(上図ではクイックスタートガイドにあるテキストを試しています)

ドキュメント生成

前項で作成した雛形のルートフォルダでreview-epubmakerreview-pdfmakerコマンドを実行すればEPUBおよびPDFドキュメントを生成できます。
雛形にはRakeの設定も含まれているのでrakeコマンドから生成することも可能です。

cd /mnt/c/Temp/review/sample-doc/

# EPUB生成
review-epubmaker config.yml
# rake epub でも良い

# PDF生成
review-pdfmaker config.yml
# rake pdf でも良い

生成されたEPUBとPDFは以下の様な感じになりました。

f:id:stknohg:20180309174416p:plain

f:id:stknohg:20180309174425p:plain

とても良い感じですね。

PowerShellの三項演算子について

あまりにも今更なはなしなのですが、根強い需要があるみたいなのでざっくりブログに書いておきます。

【2019/09/20追記】PowerShell 7の三項演算子

本日PowerShell 7 Preview.4がリリースされ、このバージョンの新機能の一つとして三項演算子が導入されました。
詳細を別エントリーにまとめていますのでこちらも併せてご覧ください。

dev.classmethod.jp

PowerShellの三項演算子

はじめに結論を書いておくと、現在、PowerShellに三項演算子はありません。
PowerShell 1.0 ~ PowerShell Core 6.0に至るまでどのバージョンでもサポートされていません。

ただ、三項演算子に対する要望はPowerShellがリリースされた当初からあった様で、オープンソース化した現在も以下のIssueで要望と提案がなされています。

github.com

私自身は三項演算子に対する思い入れは全くないので気にしていませんが、気になる方はこちらのIssueに積極的に参加していくと良いでしょう。

三項演算子の代わりになるもの

で、これで終わってしまうとあまりにも芸がないので三項演算子の代わりになる方法をいくつか紹介します。

1. if文

一般的なプログラミング言語に慣れている方には受け入れ難い動作かもしれませんが、PowerShell 2.0からif文が値を返す動作をします。

このため、

$result = if($condition){ Write-Output "True" }else{ Write-Output "False" }

の様な記述を三項演算子の代わりに使うことができます。
なお、式の中でこれを使いたい場合は、

"Result is " + $(if($condition){ Write-Output "True" }else{ Write-Output "False" })

の様に部分式($())にしてやればOKです。

イメージとしてはVB.NETのif演算子が近いでしょうか。


余談ですが、PowerShellにおける文と式の曖昧さ、そしてパイプライン文については牟田口先生のこちらの考察をご覧ください。必見です。

winscript.jp

2. 2値配列

ショートコーディングのテクニックの一つなのですが配列で三項演算子を代用することができます。

スクリプト中で使うことはあまりお勧めしませんがちょっとしたワンライナーを書くときのテクニックとして覚えておいて損はないかと思います。

以下の様に2値の配列にTrue、Falseごとの処理を記述し、配列の要素指定に条件を記述します。

$result = &({ Write-Output "True" }, { Write-Output "False" })[!$condition]

[int]$false = 0[int]$true = 1と暗黙的に型変換されることを利用してそれぞれの条件を実行するテクニックです。

3. Invoke-Ternary

最初に触れたPowerShell Team Blogの記事からの引用です。

# 定義
filter Invoke-Ternary ([scriptblock]$decider, [scriptblock]$ifTrue, [scriptblock]$ifFalse) 
{
    if (& $decider) { 
        & $ifTrue
    } else { 
        & $ifFalse 
    }
}
Set-Alias ?: Invoke-Ternary -Option AllScope -Description "PSCX filter alias"

# 使用例
1..10 | ?: {$_ -gt 5} {"Greater than 5";$_} {"Not greater than 5";$_}

関数やフィルターを作ってしまうならコレにこだわらず自分の好きな様に作ってしまうのが良いでしょう。

最後に

ほかにも色々なやり方で三項演算子を代用することができると思いますが本エントリではこのくらいにしておきます。