日々の仕事のほとんどはUbuntu Linux(サーバーサイドはAmazon LinuxなどRHEL系が多いが)やmacOSで過ごしているのでWindowsは苦手で、リモートでの作業もSplashtopやリモートデスクトップ接続でGUIを使って済ませてしまうことが多いのだけど、担当する業務の都合上、遠隔地のWindows端末をリモートでメンテナンスする必要に迫られて、でも予算の関係でSplashtopやTeam Viewerのライセンスは買ってもらえないということで「じゃあ、仕方がないからWindowsにSSH経由でログインしてPowerShellのCLIでメンテしよう!」と思い立ったのでその記録
でも、結論としてはやっぱり「Windows上の環境をCLIだけで何とかするのはとても難しい」だったのだけど…
OpenSSHサーバーのインストールと有効化
PowerShell(Windows Terminal)を管理者モードで起動
※この後の作業も全てはWindowsでAdministrators(管理者)グループに所属するユーザーでターミナルを管理者モードで起動して実施する前提で記述されています。

OpenSSHサーバーの有効化
OpenSSHサーバーはWindowsの機能の一部としてMicrosoftより提供されていますが、デフォルトでは機能が有効となっていないので、まずはこれをインストールして有効化します。
OpenSSHサーバーのインストール状態を確認
すでにOpenSSHサーバーが有効化されているかどうかはGet-WindowsCapabilityコマンドによって確認が可能です。
Get-WindowsCapabilityコマンドではレスポンスとして利用可能な機能の一覧が返されるのでWhere-Objectコマンドでフィルタリングすることで出力を絞り込んでいます。
Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH*'
以下のようにデフォルトで有効化されているOpenSSH.ClientについてはStateがInstalledとなっていて有効であることが判ります。
Name : OpenSSH.Client~~~~0.0.1.0
State : Installed
Name : OpenSSH.Server~~~~0.0.1.0
State : NotPresent
上記のようにOpenSSH.ServerのStateがNotPresentになっていたらOpenSSHサーバーはまだインストールされていないので追加でインストールします。
OpenSSHサーバーのインストール
PowerShellでWindowsのオプション機能をインストールして有効化するにはAdd-WindowsCapabilityコマンドを使います。
Add-WindowsCapabilityコマンドの-Nameオプションとして先ほどGet-WindowsCapabilityコマンドで一覧されたOpenSSHサーバーの名称とバージョン(OpenSSH.Server~~~~0.0.1.0)を指定して機能を有効化します。
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
Microsoftのサーバーから追加機能(OpenSSHサーバー)のダウンロードが始まると、以下のようにWindows Terminal上に進捗が表示されるので暫し(我が家の環境ではだいぶ…)待ちましょう。

機能の追加が終わると以下のように状態が表示されます。
今回は調べていないけど、Pathにはサービス以外ではコマンドのパスとかが表示されるのでしょうか?
Onlineはてっきり「もう使えるよ!」なのかと思ったら、そうではなくて「インストールはされているから色々とすれば使えるようになるよ」な状態のようです。
RestartNeededは「追加した機能を使うにはWindowsの再起動が必要」だと思われますが、今回はそれは必要ないようです。
Path :
Online : True
RestartNeeded : False
OpenSSHサービスの起動と有効化
そんな訳でPowerShellのGet-ServiceコマンドでOpenSSHサーバーのサービスの状態を確認してみます。
どうやらWindowsでもOpenSSHサーバーの実態はsshdのようなので、オプションとして-Name sshdでそれを指定します。
Get-Service -Name sshd
このようにStatusがStoppedなのでサービスはまだ動いていないことがわかりました。
Status Name DisplayName
------ ---- -----------
Stopped sshd OpenSSH SSH Server
Start-Serviceコマンドでsshdサービスを起動します。
Start-Service -Name sshd
自分の場合は常にOpenSSHサーバーを使える状態にしておきたいので、ついでにSet-Serviceコマンドを使いサービスがWindowsの起動時に自動的にスタートされるように設定しました。
Set-Service -Name sshd -StartupType 'Automatic'
Get-Service -Name sshd
Status Name DisplayName
------ ---- -----------
Running sshd OpenSSH SSH Server
他のホストからSSHクライアントでログインしてみる
他のホストからSSHクライアントを使いOpenSSHサーバーを起動したWindowsホストへとログインしてみます。
ssh my.host.name
初回の接続時には以下のように接続先のホストキーが未登録なので、これを受け入れるかを聞かれるので接続先の情報に間違いがなければAre you sure you want to continue connecting (yes/no/[fingerprint])?の問いに対してyesを入力してホストキーを受け入れます。
The authenticity of host 'my.host.name (nnn.nnn.nnn.nnn)' can't be established.
ED25519 key fingerprint is SHA256:VC1Kosfqmrr86YBac2nN6Vu14hEBmGDCOPQXhiBitUs.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'my.host.name' (ED25519) to the list of known hosts.
myusername@my.host.name's password:
以下のようにWindowsコマンドシェルのプロンプトが表示されれば接続は成功です。
Microsoft Windows [Version 10.0.22635.4371]
(c) Microsoft Corporation. All rights reserved.
myusername@my.host.name C:\Users\myusername>
WindowsとLinuxをデュアルブートにしている場合
WindowsとLinuxをデュアルブートにしている場合には過去にデュアルブートのLinuxにログインしたことがあるクライアントからSSH経由でログインしたことがある場合に、以下のようにログインしようとしているホストの証明書が変更されてしまっていることを警告するメッセージが表示され、ログインできないことがあります。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ED25519 key sent by the remote host is
SHA256:8xeVm+HZeHmBf1RHK2+hrEqDHcuVpbGszffgrlwAjdY.
Please contact your system administrator.
Add correct host key in /Users/myusername/.ssh/known_hosts to get rid of this message.
Offending ED25519 key in /Users/myusername/.ssh/known_hosts:nnn
Host key for my.host.name has changed and you have requested strict checking.
Host key verification failed.
このような場合には警告文にあるようにssh-keygen -R <ホスト名>などで~/.ssh/known_hosts内の該当のエントリーを消去すれば新しいホストキーを登録し直してログインは可能ですが、それだとWindowsとLinuxを切り替えるたびに同様の操作が必要となってしまい、とても面倒です。
このような場合にはWindowsまたはLinuxのどちらかのSSHホストキーをもう一方へとコピーしてしまえば両方のOSでクライアントからの接続時に同じホストキーが提示されるので齟齬が起こらずに済みます。
SSHホストキーはLinux OSでは/etc/sshディレクトリ以下に格納されています。
SSHホストキーのファイルは以下の6つ
ssh_host_ecdsa_key ssh_host_ed25519_key ssh_host_rsa_key
ssh_host_ecdsa_key.pub ssh_host_ed25519_key.pub ssh_host_rsa_key.pub
Linux OSからWindows(NTFS)にファイルをコピーするにはfuse3とntfs-3gを使うことで実現できます。
例えばUbuntu Linuxの場合には以下のようにaptコマンドでfuse3とntfs-3gをインストールすることができます。
sudo apt install ntfs-3g fuse3
fuse3とntfs-3gをインストールしたなら、WindowsがインストールされたパーティションをLinux OSでマウントして利用可能な状態にします。
partedコマンドでパーティションの状態を確認
sudo parted -l
例えば、自分が利用しているホストではパーティションの状態は以下のようになっていました。
デバイス/dev/nvme0n1の3番目のパーティションがntfsとなっているのでWindowsのパーティションと推測されます。
モデル: XXXXXXXXXX (nvme)
ディスク /dev/nvme0n1: 2000GB
セクタサイズ (論理/物理): 512B/512B
パーティションテーブル: gpt
ディスクフラグ:
番号 開始 終了 サイズ ファイルシステム 名前 フラグ
1 1049kB 316MB 315MB fat32 EFI system partition boot, esp
2 316MB 450MB 134MB Microsoft reserved partition msftres
3 450MB 958GB 957GB ntfs Basic data partition msftdata
4 958GB 2000GB 1043GB ext4
mountコマンドでマウントポイント/mnt/ntfsにWindowsのパーティションを読み書き可能な状態(オプション-o rw)でマウントします。
sudo mount -t ntfs -o rw /dev/nvme0n1p3 /mnt/ntfs
Linux OSのファイルシステム/etc/ssh/に格納されている6つのSSHホストキーファイルをWindowsファイルシステムの所定の場所¥ProgramData¥ssh¥へとコピーします。 (毎度思うのですが、なんでMicrosoftはパスの区切り文字をバックスラッシュにしちゃったかな?)
sudo cp /etc/ssh/{ssh_host_ecdsa_key,ssh_host_ecdsa_key.pub,ssh_host_ed25519_key,ssh_host_ed25519_key.pub,ssh_host_rsa_key,ssh_host_rsa_key.pub} /mnt/ntfs/ProgramData/ssh/
これでWindowsとLinuxを切り替えてもSSHログインの度に面倒なことにならずに済みます。
デフォルトシェルをPowerShellに変更
デフォルトではOpenSSHサーバー経由でWindowsにログインするとPowerShellではなく旧来のコマンドシェルが起動されてしまい使い勝手が悪かったのでOpenSSHサーバー経由でのログイン後のデフォルトシェルをPowerShellへと変更します。
まずはコマンドシェルだと色々とやりずらいのでPowerShellを起動します。
powershell
PowerShellに切り替われば以下のようなグリーティングが表示されるはずです。
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
新機能と改善のために最新の PowerShell をインストールしてください!https://aka.ms/PSWindows
デフォルトシェルをPowerShellに変更
New-ItemPropertyコマンドでOpenSSHサーバーのデフォルトシェルをPowerShellに切り替えます。
New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -PropertyType String -Force
DefaultShell : C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\
OpenSSH
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE
PSChildName : OpenSSH
PSDrive : HKLM
PSProvider : Microsoft.PowerShell.Core\Registry
一旦Windowsからexitコマンドでログアウトして(今は旧来のコマンドシェルとPowerShellが二重に起動されているので2回exitする必要がありはずです)接続を一旦切り、もう一度WindowsにSSHクライアントで接続してみます。
結果、以下のようにグリーティングが表示されれば切り替えは成功です。
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
新機能と改善のために最新の PowerShell をインストールしてください!https://aka.ms/PSWindows
sshクライアントからの接続に公開鍵認証を使えるように設定
OpenSSHサーバーの公開鍵認証を有効化
SSHクライアントからの接続認証にパスワードではなくSSHホストキーによる公開鍵認証を行うように設定しました。
※インストールされているWindowsのバージョンやアーキテクチャによるのか?初期状態で公開鍵認証による認証が利用可能でしたが、環境によってはSSHホストキーによる認証が無効化されているかもしれないので、その場合には設定の変更とOpenSSHサーバーの再起動が必要です。
OpenSSHサーバーの公開鍵認証を有効化するにはWindowsのAdministrators(管理者)グループに所属するユーザーの場合向けにはC:¥ProgramData¥ssh¥administrators_authorized_keysにクライアントのSSH公開鍵を追加します。
Administrators(管理者)グループに所属していない一般のユーザーの場合にはLinuxなどの他の環境と同様に各々のユーザーのホームディレクトリに.ssh/authorized_keysファイルを用意し、ここにのSSH公開鍵を追加します。
PowerShellが扱うファイルへの出力文字コードをascii(ASCIIコード)に変更する
※これを事前に行っておかないと次のリダイレクトでの公開鍵の書き込みがデフォルトではUTF16でファイルに出力されてしまい、OpenSSHサーバーが読み込めなくなってしまう。
$PSDefaultParameterValues['Out-File:Encoding'] = 'ascii'
クライアントのSSH公開鍵をadministrators_authorized_keysファイルへと書き込む
ここでは新しくadministrators_authorized_keysファイルを作成する前提で作業をしているが、安全を期すにはリダイレクトには>ではなく>>を使うべきかもしれない。
echo "ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBABDRjRndygXmZdQvY7Hv0nXC2keN357yHGcSMnxrFifV34dHaE8JzkSquVFhE4dPQaPdLYo5gIJAsnv3PaQ9ddNMQAGVomKiHDJuLNLP8mF8dxegFok/pDOSz7SiA+OJJ1KdQUGjEyPk3NBl3lZuDlao9xDYkbwBUoRIuKf64dhQiBVSA== myusername@my.host.name" > C:\ProgramData\ssh\administrators_authorized_keys
念のためadministrators_authorized_keysファイルのEveryone(誰でも)へのアクセス許可を削除しておきます。
icacls C:\ProgramData\ssh\administrators_authorized_keys /remove Everyone
これで再びexitコマンドでWindowsとの接続を切り、SSHクライアントから公開鍵認証を使いWindowsに接続した際にパスワードの入力が不要になっていれば設定は成功です。
###その他もろもろ…
vimのインストール
これは全くの好みの問題で、何をインストールするかは人次第(宗教?)ではありますが、まずはCLI環境でもテキストエディターを使いたかったので自分の場合は vim(https://www.vim.org/download.php) をインストールしました。
これからはWindowsでもGUIではなくCUI・CLIで生きていくぞ!と心に誓ったので、ここはGUIインストーラーではなくMicrosoftt謹製のPowerShell用パッケージマネージャー WinGet(https://learn.microsoft.com/ja-jp/windows/package-manager/winget/) でインストールしてみます。
winget install --id vim.vim -e --source winget
Gitのインストール
自分はプログラムを書いたり色々なサーバーの類をメンテナンスするのが生業なのでGitなども入れておきましょう。
winget install --id Git.Git -e --source winget
未署名のPowerShellのスクリプト実行を有効化
WIndowsデフォルトのセキュリティポリシーではPowerShellのCLIでは正式な署名付きのPowerShellスクリプトしか実行できないようになっているので、これを現在ログイン中のユーザーに限っては(-Scope CurrentUserオプション)署名なしのスクリプトも実行できるように変更しました。
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
sudoコマンドの有効化
sudoコマンドは「Windows 11 2024 Update」(バージョン 24H2)から利用が可能で、これ以前のWindowsを使っている場合には使えません。 (MicrosoftによればそのうちWindows 10とかでも使えるかも?とのこと)
Windows 11の現在のバージョンはwinverコマンドで調べることができます。
※Windowsのタスクバーの検索ボックスにwinverと入力し、winverコマンドを実行

Windowsの 11のバージョンが22H2以上であればsudoコマンドが実行可能なはず

もし、winverコマンドで調べてWindows 11のバージョンが22H2より古いバージョンだった場合にはWindows Updateのオプションの「利用可能になったらすぐに最新の更新プログラムを入手する」をオンにしてWindows Updateを実行すれば最新のバージョンへとWindowsがアップデートされるはずです。

「利用可能になったらすぐに最新の更新プログラムを入手する」をオンにする
※GoogleなどでキーワードWindows11 24H2などで検索をすると判るとおり、Windows 11のバージョン24H2にはいろいろと問題も多いようなので、このバージョンへのアップデートにはそれなりの危険性と覚悟が必要なのを忘れないでください!
sudoコマンドを有効にするにはWindowsの「設定」で「システム」の「開発者向け」を選び

UACのダイアログでsudoの実行を許可すればPowerShellでのsudoコマンドの実行が可能になります

と、ここまでいろいろとやっておいて何なのですが、デフォルトの状態ではリモートホストからSSH経由でWindowsのPowerShellにログインするとそもそも管理者権限で動いている状態だったのでsudo要らなかったw
PowerShellが管理者権限で実行されているか調べてみる
([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
True
