👻

windowsでsshクライアント側の~/.ssh/のパーミッションの設定

2023/03/02に公開

windows 10以降ではsshはosのコマンド、アプリケーションとしてプリインストールされています。
MS公式自体がサポートしている事もあり、Tera termや、Puttyを使うより安全性や
不具合の面では不安は少ないと思われるので、特に理由が無い限りはwindowsについているsshを使いましょう。

ただ、ここで問題になってくるのがパーミッションです。
英語で見ても分かりづらい説明が多く、結局どの設定が効いてるねん!とツッコミたい人は多いと思います。

なので、ここではchmod 700 ~/.sshに相当する処理を書きます。

必要な設定

やることとしては~/.sshに
SYSTEM GroupとAdministrators Groupとユーザーディレクトリのユーザーのみ、
Full Controllのパーミッションを与え、
それ以外のユーザーはすべての権限を削除します。

そのうえで~/.sshに新規作成されたファイルも~/.sshと同じ権限であったら、
sshを実行することができます。

なお処理は分けてSet-Aclすることはできなくて、
オブジェクトの継承を無効にする処理と、
指定のユーザー、グループ以外、触れなくするのと2回実行する必要があります。

権限の継承を無効にする

windowsのファイルシステムにある変わった特徴として、継承があります。
これはパーミッションを変更したいファイル(例えば、C:\User\username.sshなら)の上のフォルダ(C:\User\username)のパーミッションを
引き継ぐ設定です。

権限の変更を行いたいファイル(C:\User\username.ssh)の継承を無効にしないと、
手動で行う権限の設定より、権限の継承の方が優先度が高いため自分がしたい権限が反映されません

このため、~/.sshに対して権限の継承を無効にしたのちに指定のユーザーの権限を削除する必要があります
なお、この設定を行った場合でも~/.ssh/配下に新規ファイルを作成した場合はそのファイルは~/.sshの権限を継承します
(なので、~/.sshの権限と同じになる。~/.sshがsshを行う上で適切な権限だった場合は配下のファイルは適切な権限になる。)。

また、権限の継承を無効にしたうえで、現在の権限の設定を保持しないと、
~/.sshのユーザー本人の権限さえもすべて取られるため、管理者権限でしかそのファイルを操作できなくなります
気をつけましょう。

# ~/.sshの権限を取得する
$acl = Get-Acl ~/.ssh

# 権限の継承を無効にしたうえで、現在の権限は保持する。
# [system.security.accesscontrol.objectsecurity.setaccessruleprotection](https://learn.microsoft.com/ja-jp/dotnet/api/system.security.accesscontrol.objectsecurity.setaccessruleprotection?view=net-7.0)
$acl.SetAccessRuleProtection($True, $True)

# 権限の反映を行う
Set-Acl -Path ~/.ssh -AclObject $acl

sshを使うユーザーの権限のみにする

SYSTEM GroupとAdministrators GroupとユーザーディレクトリのユーザーのみFull Controllの権限を与えます。
sshを使うユーザーだけでも良いですが、Administrators GroupとSystem Groupにも権限を与えても動きます。

# ユーザーのホスト名と名前を取得する。
$hostname = $env:USERDOMAIN
$username = $env:USERNAME

$acl = Get-Acl -Path ~/.ssh

# 指定のユーザー以外のアクセス権限をすべて削除する。
# Remove-Aclコマンドとかないので、ループで回して消す必要がある。
$acl.Access |
    Select-Object -ExpandProperty IdentityReference | 
    ?{$_ -notin ("$hostname\$username", "NT AUTHORITY\SYSTEM", "BUILTIN\Administrators")} |
    %{$acl.PurgeAccessRules($_)}

# ユーザーから権限を無くすときはSet-Aclは使えない。なので、下記のクラスメソッドを使う必要がある。
# [system.io.filesystemaclextensions.setaccesscontrol](https://learn.microsoft.com/ja-jp/dotnet/api/system.io.filesystemaclextensions.setaccesscontrol?view=net-7.0)
[System.IO.FileSystemAclExtensions]::SetAccessControl((Get-Item ~/.ssh), $acl)

権限の設定を行うときにSet-Aclを使いたくなりますが、
Set-Aclではユーザーから権限を無くす事は管理者権限で起動しないとできません。
これはSet-Aclのバグです。
2018年3月の時点で日本語の掲示板に書かれているが、2023年3月現在直ってない。teratail
英語で情報探すともっと前の日付も見つかるのでずっと放置されているっぽいですね...

# こうやりたくなるが、Set-Aclにはバグがあり、下記のエラーが出力される。
# >> Set-Acl: The process does not possess the 'SeSecurityPrivilege' privilege which is required for this operation.
Set-Acl -Path ~/.ssh -AclObject $acl

まとめ

どうですか?多分ネットで調べても正しい手順を理解するのも探すのも、難しかったと思います。
正直、sshとパーミッションについてはMS公式もちゃんと説明のページ作るべきでしょうが、作りませんね...

Set-Aclのバグは致命的なもので、Powershellチームがずっとこれ放置してるのはうーん...powershellの責任者の降格妥当!としか。
というか、ファイルシステムのパーミッションの操作のバグがずっと放置されているシェルってなんやねん!

Remove-Aclコマンド欲しいよね。
Poweshell公式が作る気無さそうなので、手が空いたら作る。

Discussion