日々の仕事のほとんどは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