注意
自己責任でお願いします。これら記事の内容を実行して何か不都合を被ったとしても筆者は責任を負いません。
性質上セキュリティホールを自分で開けているようなものなので、実装は実験目的かつ仮想環境を推奨します。また当たり前ですが実装自体には管理者権限が必要です。また自分の所有する端末のみで行ってください。
もし sudo を使いたいなら、Windows 標準 か Grignoli 氏の gsudo を使えばよいと思います。
構成
目次
1. 変数の宣言と代入
2. 接続設定
3. 構成ファイル作成・登録
4. 権限設定
5. 証明書作成
6. 証明書紐付け
7. 実行・関数作成
8. 後始末
説明
これまで mock(模造品・似非・真似)を使ってきましたが、最後はせっかくなので sudo にしてそれっぽくしたいと思います。また filter 関数の中の引数について _ から始まっているのはコマンドレットのオプション名と被らないようにするためです。
変数に対して $ の代わりに @ をつけるとスプラッティングが作動し、パラメーターに展開されてコマンドに渡されます。これを使うとスクリプトブロック内で任意の位置に引数を渡すことができるようになります。
内容
使用する変数:
$PhrasePath
$ModDirName = "JEA"
$EntirePath = ($Env:PSModulePath -split ";" -like "$Env:ProgramFiles*")[-1]
$ModulePath = Join-Path $EntirePath $ModDirName
$RoleInPath = Join-Path $ModulePath RoleCapabilities
$PhrasePath = Join-Path $RoleInPath "phrase.uid"
↓管理者権限で実行します↓
Restart-Service WinRM -Force
$ThumbUser = (Get-ChildItem "Cert:\CurrentUser\My").Where{$_.FriendlyName -like "My Client Cert - UserDef"}[-1]
Invoke-Command -ComputerName 127.0.0.1 `
-ConfigurationName "MockEndpoint" `
-CertificateThumbprint $ThumbUser.Thumbprint `
-ScriptBlock { Get-Location }
# コマンド実行用のフレーズを表示しておく(後で使う)
Get-Content $PhrasePath -Encoding UTF8
↓昇格させず、一般ユーザー権限で実行します↓
filter sudo {
param(
[object]$__MainCommand__
)
if (-not $PSBoundParameters.ContainsKey("__MainCommand__") -or $null -eq $__MainCommand__) {
Write-Error "パラメーター '__MainCommand__' に引数がありません。"
return
}
$WorkFolder = Get-Location
if (-not ($WorkFolder.Provider.Name -eq "FileSystem")) {
Write-Error "現在の場所が FileSystem Provider ではありません。"
return
}
if ($__MainCommand__ -is [ScriptBlock]) {
$Switch = $true
$Executive = $__MainCommand__.ToString()
} else {
$Switch = $false
$Executive = $__MainCommand__
}
$IsArray = if ($null -eq $_){ $false
} elseif ($_ -is [string]){ $false
} elseif ($_ -is [System.Array]){ $true
} elseif ($_ -is [System.Collections.IEnumerable]){ $true
} else { $false }
$ScriptBlock = {
param(
[string]$PWDPATH,
[bool]$SBFLAG,
[string]$PHRASE,
[object]$CMD,
[object]$PIPE,
[object]$REST
)
Mock -__Mock_WorkFolder__ $PWDPATH `
-__Mock_ScriptBlock__:$SBFLAG `
-__Mock_Phrase__ $PHRASE `
-__Mock_Command__ $CMD `
-__Mock_Pipeline__ $PIPE `
-__Mock_Arguments__ $REST
}
$Thumbprint = (Get-ChildItem "Cert:\CurrentUser\My").Where{$_.FriendlyName -like "My Client Cert - UserDef"}[-1].Thumbprint
if ($IsArray) {
Invoke-Command -ComputerName 127.0.0.1 `
-ConfigurationName "MockEndpoint" `
-CertificateThumbprint $Thumbprint `
-ScriptBlock $ScriptBlock `
-ArgumentList $WorkFolder.Path, $Switch, $PhraseGUID, $Executive, (,$_), @{Args = $args}
} else {
Invoke-Command -ComputerName 127.0.0.1 `
-ConfigurationName "MockEndpoint" `
-CertificateThumbprint $Thumbprint `
-ScriptBlock $ScriptBlock `
-ArgumentList $WorkFolder.Path, $Switch, $PhraseGUID, $Executive, $_, @{Args = $args}
}
return
}
# フレーズの入力は残さない(若干の安全策・GUID が漏れなければコマンドの実行が阻止されるため)
Set-PSReadlineOption -HistorySaveStyle SaveNothing
# PowerShell の関数における変数は、子スコープで初期化するまで親スコープのときの値が入っている(ただし子スコープで変更しても親スコープには反映されない)
$PhraseGUID = ""
# 権限表示
sudo whoami /priv
# 引数の渡し方(参考)
sudo {echo @args} -InputObject "Argument Test"
# パイプを使ってオブジェクトを渡す(ただし [ScriptBlock] は [string] になって渡されるため注意)
"Pipeline Test" | sudo {$input | echo}
# 複数行のパターン
sudo {
echo "Start: $(($S = [DateTime]::Now).ToString().Split()[1])`n"
[Threading.Thread]::Sleep(3000)
echo "Close: $(($E = [DateTime]::Now).ToString().Split()[1])`n"
"Sum Time: $(($E - $S).TotalMilliseconds)"
}
備考
https://learn.microsoft.com/ja-jp/powershell/module/psreadline/set-psreadlineoption?view=powershell-7.5#-historysavestyle によると
HistorySaveStyle を SaveNothing に設定し、同じセッションで後でSaveIncrementallyに設定すると、PSReadLine はセッションで以前に実行したすべてのコマンドを保存します。
らしく、同セッション内では SaveIncrementally を実行しないほうが良いと思います(セッション終了時に HistorySaveStyle の値は自動で元に戻ります)。