linuxユーザーがssh経由でWindows PowerShellにアクセスできるようにした際の覚書

# 背景

Microsoft (MS) が嫌いなユーザーではあるものの，業務上の理由でPowerShellを使わなければならくなった．手元にWindowsがないので，常設のWindows 11 Proにsshdを入れてリモートからwindows 11にアクセスしPowerShellを使えるようにした個人的な覚書．sshの操作などbashなどのterminal操作は慣れているものとする．

`>`, `$` はPowerSell，bashそれぞれのコマンドの接頭を表し入力する必要はない．

# windows にssh serverをインストールする

PowerShellのversionが5系の場合は7系に先にupdateしておいた方が良いかもしれません．
Updateの方法は追記に記述しています．

以下は
```:PowerShell
> $PSVersionTable.PSVersion

Major  Minor  Build  Revision
-----  -----  -----  --------
5      1      22621  3958
```
での作業内容．

## windows 11 GUI環境での作業

対象が異なるが公式の [Windows server 2022](https://learn.microsoft.com/ja-jp/windows-server/administration/openssh/openssh_install_firstuse?tabs=powershell&pivots=windows-server-2022)を参照して（windows server 2025はデフォルトでsshdがインストールされているので参考ならない），*前提条件の確認* と *OpenSSH for Windows Server* (PowerShell版)を実行する．記憶が曖昧だが前提条件はWinsows 11 Pro だったら問題なかったはず．

ほぼ，コピペになるが

1. PowerShell を管理者として実行します。
2. OpenSSH が使用可能になっていることを確認するには、次のコマンドレットを実行します。

    ```:PowerShell
    > Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH*'
    ```

    SSH client と server いずれもインストールされていなければ

    ```:PowerShell
    Name  : OpenSSH.Client~~~~0.0.1.0
    State : NotPresent
    
    Name  : OpenSSH.Server~~~~0.0.1.0
    State : NotPresent
    ```

    となる．（version 0.0.1.0ってまともにversion管理するつもりないなと嫌な気分になる）

3. 今回はSSHクライアントは不要なのでopenssh Serverのみインストールする（5分くらいかかったので気長に待つ）．

    ```:PowerShell
    # Install the OpenSSH Server
    > Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
    ```

4. Serviceの起動

    ```:PowerShell
    # Start the sshd service
    > Start-Service sshd
    ```

5. Service自動起動(永続化)
    
    ```:PowerShell
    > Set-Service -Name sshd -StartupType 'Automatic'
    ```

6. Firewallのport 22番の穴あけ

    ```:PowerShell    
    # Confirm the Firewall rule is configured. It should be created automatically by setup. Run the following to verify
    if (!(Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -ErrorAction SilentlyContinue | Select-Object Name, Enabled)) {
        Write-Output "Firewall Rule 'OpenSSH-Server-In-TCP' does not exist, creating it..."
        New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
    } else {
        Write-Output "Firewall rule 'OpenSSH-Server-In-TCP' has been created and exists."
    }
    ```

    おそらく `TCP -Action Allow -LocalPort 22` の部分を22以外にすれば任意のポートで穴あけできると思われる（未調査）

## ssh client -> ssh server

1. ssh server （Windows 11）にssh clientが入っている端末から接続してみる

    適当なSSH clientから
    
    ```:bash
    $ ssh username@ip # or ssh username@hostname.local.
    ```
    
    で接続すればいい．fingerprintを確認して接続するとパスワード認証を要求されるので入力する．私の場合はMS Accountだったので，MSアカウントのパスワードを入力する．
    
    後者のホスト名+.local.方法でも同じサブネットにある端末なので接続できたが，パブリックネットーワークの設定であるにも関わらず見えるのは不思議なような気がする．avahi-daemon的なものはネットワークの探索と無関係なんだろうか．ちなみに，ssh server側（Windows）のhostnameやipはPowerShellから

    ```:PowerShell
    > whoami
        hostname\username
    
    > ipconfig # != ifconfig
        ...(省略)...
    ```

    などで調べれば良い．

## ssh serverの設定ファイル (sshd_conf)

1. ssh_confのPATH

    [sshd_config 内の Windows 構成](https://learn.microsoft.com/ja-jp/windows-server/administration/openssh/openssh_server_configuration#windows-configurations-in-sshd_config) を参照すると，Windows 11 にSSH Serverをinstallすると
    *Windows では、sshd は既定で、%programdata%\ssh\sshd_config から構成を読み取ります*
    と記述されているが，

    ```:PowerShell
    > cd %programdata%\ssh\sshd_config
    > cd : パス 'C:\Users\hanaata\%programdata%\ssh\sshd_config' が存在しないため検出できません。
    ```

    となる．どうやら *%programdata%* とは root directory 直下に配置されている隠しファイルのよう．GUIから[エクスプローラーの表示設定](https://www.ubackup.com/jp/data-recovery-disk/cant-find-programdata-folder-windows-10-7668-tc.html) から隠しファイルを表示させると，`\ProgramData\ssh\sshd_conf` にアクセスできる．注意しなけばならいことは，エクスプローラーから管理者権限で `sshd_conf` を編集する権限がない（ro）ので，管理者権限で立ち上げたPowerShellで

    ```:PowerShell
    > notepad C:\ProgramData\ssh\sshd_config
    ```

    としてメモ帳を開き編集する必要がある．設定ファイル自体はOpenSSH serverの設定ファイルそのものなので，ようやく生きた心地がする．

2. 公開鍵認証

    個人的にパスワード認証は嫌いなので公開鍵認証に制限する．

    ```:conf_sshd
    PubkeyAuthentication yes
    PasswordAuthentication no #PasswordAuthentication yes
    ```

    もしかするとデフォルトで `# PubkeyAuthentication yes` となっていたので，OpenSSHの設定ファイルから考えると，デフォルト値で公開鍵認証はyesなので，コメントアウトは不要だったかもしれない．

    
3. 謎のwindows 固有のsshdの設定の無効化

    conf_sshdの最下部２行に次のような設定がある．
    
    ```:conf_sshd
    Match Group administrators
           AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys
    ```

    linuxのsshd.confには無い設定なので不穏な空気を感じつつ，ChatGPTくんに恐る恐る聞いてみると，管理者権限を持つユーザーの公開鍵のファイルの場所を`__PROGRAMDATA__/ssh/administrators_authorized_keys` に強制するみたい．

    Windowsのお作法に従っても良いかもしれないが，個人的に公開鍵は `~/.ssh/authorized_keys` に配置したいので上記２行はコメントアウトする．

4. sshdの再起動

    ```:PowerShell
    > Restart-Service sshd
    ```

5. 公開鍵の設置

    3. で言及したように ssh serverのユーザーホームディレクトに，` ~/.ssh/authorized_keys` に公開鍵を置けば，公開鍵認証でリモートにアクセスできる．

6. ssh接続次にPowerShellを起動する

    デフォルトの場合，sshでログインするとコマンドプロンプトが表示されるのでsshで起動後powershellを起動したいので下記設定をsshd_confに追記しsshdを再起動する

    ```:conf_sshd
    ForceCommand powershell.exe
    ```
    (PowerShell v7のがinstallされていたv7を起動する場合は `ForceCommand pwsh.exe` にする必要があります．追記参照)
    

    ```:PowerShell
    > Restart-Service sshd
    ```

    おそらくこれでsshでログインした際にpowershellが起動するはず．


7. 残課題


    上記の設定でssh経由でpowershellを起動すると，私のアカウントが管理者権限を持っているせいかログインした直後から管理者モードになっている．

    ```:PowerShell
    > ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
    True
    ```

    linuxユーザからするとpermit root loginを許可しているようで非常に居心地が悪い．ただ，WindowsのGUIでPowerShellを管理者権限で開く場合にroot パスワードを聞かれるわけでも無いのでこれで良いのか？ただ非常に気持ちわくモヤモヤする．
    
# Linux terminalの知識で対応できるものできないもの

とりあえず

```:PowerShell
> man 

トピック
    Windows PowerShell のヘルプ システム
(省略)

> Get-Help

  コマンドレットのヘルプを表示するには、次のように入力します。

        Get-Help <コマンドレット名>
```

などでヘルプが見られる．出力が違うので`man`は`Get-Help`のエイリアスではないかもだが，`man <command>`は基本的に `Get-Help` を参照していそう．

## 共通のコマンド

個人的によく使いそうな次のコマンドは

```
ls，cd, pwd，rm，cp，cat，echo，wget，curl，ping，date，host，history
```

は使える．但しパラメーターがbash系と異なるので注意が必要かもしれない．

```:PowerShell
$ cd ~
$ cd ../
```

などはbashと共通だが， `cd -` はNGっぽいし，引数なしで `cd` 単体でもhome directoryに戻れない．
Windowsのディレクトリは，`/`ではなく`\`で切られているが，tab補完すれば自動でWindows用のフォーマットに整形してくれるのでそこまで不便ではない（但しWindowsのTab補完は使いずらうえ，Ctrl-aやCtrl-eなどのemacs likeなショートキーが使えないのでクソ不便）．

## 共通でないコマンド

個人的にlinuxでよく使うがPowerShellで使えないコマンドは

```
touch, chown, where，find，ln，
```

最も残念なことはcliで動くeditorが見つからないこと．ググってもChatGPTに聞いてもそれらしい記事が出てこない．x11経由でnotepadを起動できない（未調査）のでそのため，~~wsl経由でemacsなりvimなりnanoなりEditorを起動するしかなさそう．~~ wingetコマンドでvimなどのeditorをinstallしてPowerShell上で動かせそう（後述）．

```:PowerShell
> wlc emacs -nw test.txt
```

まあ私の環境ではwlcが使えるので，これが一番かな．

パスの表示がlinuxとwindowsで違うので中一が必要である．
例えば，カレントディレクトリに存在しないファイルの中身を

```:PowerShell
> wsl nano C:\pass\to\somwhere\test.txt
```

と参照することは，wsl上で
```:bash
$ nano C:\pass\to\somwhere\test.txt
```
としているに等しい．

従ってPowerShellからwlcを経由してwindows上のファイルにアクセスするには

```:PowerShell
> wsl nano /mnt/c/pass/to/somwhere/test.txt
```

とする必要がある．この場合PathのTab補完が機能しないので，参照したいファイルがあるdirectry まで `cd` して（current dirctoryにファイルがあるようにして）開くか，wslにログインしてファイルにアクセスした方が良いであろう．

## 後述(wingetを使ってみる)

ChatGPTくんにPowerShellでパッケージマネージャが使えないか聞いたところwingetでアプリ系ーションをダウンロードできるみたい

```:PowerShell
> winget search vim
名前                          ID                          バージョン  一致                 ソース
--------------------------------------------------------------------------------------------------
Vim Cheat Sheet               9WZDNCRDMCWR                Unknown                          msstore
Vimar By-me App List Exporter 9PFSSC3DTHK8                Unknown                          msstore
Vimar VIEW Pro                9NNG9QXL8W3Z                Unknown                          msstore
Vim                           vim.vim                     9.1.0618                         winget
Vim                           vim.vim.nightly             9.1.0703                         winget
Helix                         Helix.Helix                 24.07       Tag: vim             winget
Vieb                          Jelmerro.Vieb               12.0.0      Tag: vim             winget
Neovim                        Neovim.Neovim               0.10.1      Tag: vim             winget
Neovim Nightly                Neovim.Neovim.Nightly       0.11.0      Tag: vim             winget
spotify_player                aome510.spotify-player      0.19.1      Tag: vim             winget
lf                            gokcehan.lf                 r32         Tag: vim             winget
win-vind                      pit-ray.win-vind            5.13.2      Tag: vim             winget
qutebrowser                   qutebrowser.qutebrowser     3.2.1       Tag: vim             winget
Yazi                          sxyazi.yazi                 0.3.2       Tag: vim             winget
neovim-qt                     equalsraf.neovim-qt         0.2.18                           winget
marksman                      Artempyanykh.Marksman       2023.12.09  Tag: neovim          winget
Kaku                          Chia-Lung.Kaku              2.0.2       Tag: vimeo           winget
4K Video Downloader           OpenMedia.4KVideoDownloader 4.32.5.0116 Tag: vimeo           winget
Replit                        Replit.Replit               1.0.12      Tag: desenvolvimento winget
```

お，vimあるじゃんと思いinstall

```
> PS C:\Users\hanaata> winget install vim
複数のパッケージが入力条件に一致しました。入力内容を修正してください。
名前            ID              ソース
---------------------------------------
Vim Cheat Sheet 9WZDNCRDMCWR    msstore
Vim             vim.vim         winget
Vim             vim.vim.nightly winget
PS C:\Users\hanaata> winget install vim.vim
見つかりました Vim [vim.vim] バージョン 9.1.0618
このアプリケーションは所有者からライセンス供与されます。
Microsoft はサードパーティのパッケージに対して責任を負わず、ライセンスも付与しません。
ダウンロード中 https://github.com/vim/vim-win32-installer/releases/download/v9.1.0618/gvim_9.1.0618_x64.exe
  ██████████████████████████████  10.8 MB / 10.8 MB
インストーラーハッシュが正常に検証されました
パッケージのインストールを開始しています...
インストールが完了しました
> PS C:\Users\hanaata> vim
vim : 用語 'vim' は、コマンドレット、関数、スクリプト ファイル、または操作可能なプログラムの名前として認識されません。名前が正しく記述されていることを確認し、パスが含まれている場合はそのパスが正しいことを確認し  てから、再試行してください。
発生場所 行:1 文字:1
+ vim
+ ~~~
    + CategoryInfo          : ObjectNotFound: (vim:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

```

installはできるけどPATHが通ってないみたいので絶対pathで実行すればPowerShell上でeditorを起動できる．

```:PowerShell
> C:'\Program Files\Vim\vim91\vim.exe'
```

ただ，アプリケーション入れる度に環境変数でPATHの設定するのか？めんどくさ過ぎるぞ．emacsもinstallできるけどGUIを開こうとするので

```:PowerShell
& 'C:\Program Files\Emacs\emacs-29.4\bin\emacs-29.4.exe' -nw
```

としなきゃならん．これらは使わないかな・・・


## wsl

wlsのコマンドヘルプは

```:PowerShell
> wls --help
```

で確認できる．

wslにubuntu 20.04が入ってたけど古いので削除して最新のdebianを入れる．

```:PowerShell
> wsl -l -v
  NAME      STATE           VERSION
* Ubuntu    Stopped         2
> # Status runningなら一度terminateした方がお行儀が良いかも
> # wsl -t Ubuntu
> wsl --unregister Ubuntu
```

ちなみに `wsl --shutdown` は仮想マシンをシャットダウンするコマンド．
長年Ubuntuを使っていたけどaptのリポジトリがtestingブランチでアップデートが多いので，Debianのstable版を使う．

```:PowerShell
> wsl --list --online
インストールできる有効なディストリビューションの一覧を次に示します。
'wsl.exe --install <Distro>' を使用してインストールします。

NAME                            FRIENDLY NAME
Ubuntu                          Ubuntu
Debian                          Debian GNU/Linux
kali-linux                      Kali Linux Rolling
Ubuntu-18.04                    Ubuntu 18.04 LTS
Ubuntu-20.04                    Ubuntu 20.04 LTS
Ubuntu-22.04                    Ubuntu 22.04 LTS
Ubuntu-24.04                    Ubuntu 24.04 LTS
OracleLinux_7_9                 Oracle Linux 7.9
OracleLinux_8_7                 Oracle Linux 8.7
OracleLinux_9_1                 Oracle Linux 9.1
openSUSE-Leap-15.6              openSUSE Leap 15.6
SUSE-Linux-Enterprise-15-SP5    SUSE Linux Enterprise 15 SP5
SUSE-Linux-Enterprise-15-SP6    SUSE Linux Enterprise 15 SP6
openSUSE-Tumbleweed             openSUSE Tumbleweed

> wsl --install Debian
```

仮想マシンのデフォルトがDebianになっていれば，wlcへのログインは

```:PowerShell
> wlc
```

でできるのであとは好きなパッケージでも入れればよろしい．昔はVcXsrvを入れてx11を動かしていたが確か，WSLgで不要になった気がする．

設定不要でx11 forwardingできるかと思ったが

```
> wsl --version
WSL バージョン: 2.2.4.0
カーネル バージョン: 5.15.153.1-2
WSLg バージョン: 1.0.61
MSRDC バージョン: 1.2.5326
Direct3D バージョン: 1.611.1-81528511
DXCore バージョン: 10.0.26091.1-240325-1447.ge-release
Windows バージョン: 10.0.22631.4037

> wsl xeyes
X connection to :0 broken (explicit kill or server shutdown).
```

となりそのままでは実行できない．多分server側のsshd_configで許可されてない．
ダメもとでssh serverに

```:sshd_config
X11Forwarding yes
X11DisplayOffset 10
X11UseLocalhost yes
```

を追記すると，

```
X connection to :0 broken (explicit kill or server shutdown).
```

のエラーは出なくなったがx11はforwardingされない．まあ使わないからこれ以上は探らない．
とりあえずリモートからPowerShellを起動できるので，これでよしとする．

# 追記

## PowerShellのupgrade

PowerShellにログインすると

```
新機能と改善のために最新の PowerShell をインストールしてください!https://aka.ms/PSWindows
```

と表示される．Versionを調べてみると

```:PowerShell
> $PSVersionTable.PSVersion

Major  Minor  Build  Revision
-----  -----  -----  --------
5      1      22621  3958

```

となり，version 5で現行はvesion 7なので古い様子．[ここの記事](https://qiita.com/probabilityhill/items/f56c9b2882936c296c0e#コマンドを実行する方法)を参考にコマンドラインからupgradeを試みる．

```
> Invoke-Expression "& { $(Invoke-RestMethod https://aka.ms/install-powershell.ps1) } -UseMSI"
```

あ，ssh経由だとGUIが開かないからセットアップに進めない．ChatGPUくんに方法ないか聞いたらCUIで完結できそうではあるものの，RemoteDesktopなどでアクセスしてupdateした方が早いので，リモード経由でPowerShellの7系をインストールする．どうやらPowerShell5とPowerShell7はソフトウェアとして異なるみたいで，Windowsに共存して（やが）る．

```:PowerShell
> $PSVersionTable.PSVersion

Major  Minor  Patch  PreReleaseLabel BuildLabel
-----  -----  -----  --------------- ----------
7      4      5
```

sshd_configのPowerShell設定だとPowerShell v5 が起動するので

```:sshd_conf
    #ForceCommand powershell.exe #v5
    ForceCommand pwsh.exe   #v7
```

に変更し，sshdを再起動し，再度sshでログインすればpowershell v7が起動する．
ちなみにコマンドプロンプトでpwshと打てばPowerShell v7が起動する．

ちょっとだけ使ってみた印象として，`ls` のディレクトリ名の背景がblueにになっている．
ただ，黒背景だと見えない．[Gighub issue](https://github.com/PowerShell/PowerShell/issues/18550) に解決策があり

```:PowerShell
$PSStyle.FileInfo.Directory = "`e[38;2;255;255;255m"
```

とすれば解決できた．ただ設定はtemporalなので設定ファイルを作成する．
PowerShellの設定ファイルのPATHは

```:PowerShell
echo $Profile.AllUsersAllHosts
echo $Profile.AllUsersCurrentHost
echo $Profile.CurrentUserAllHosts
echo $Profile.CurrentUserCurrentHost
```

から調べることができて，実際にPATHを調べてみるとクソみたいな場所にあるようなので，

```:PowerShell
> echo $Profile.CurrentUserCurrentHost
C:\Users\hanaata\Documents\PowerShell\Microsoft.PowerShell_profile.ps1
```

該当のPATHに設定ファイルを作成する．

Tab補完がクソなので改善してるかなと思ったが改善していなかったのが残念である．