﻿OpenSSH Guard インストール・更新・確認ガイド
対象：OpenSSH Guard 1.2.3（配布ファイル：OpenSshGuard.zip）／ 作成：2026年11月2日

作　成：有限会社アルゴシステム https://argo-system.co.jp
作成者：伊藤正輝

本ソフトウェアはWindows標準OpenSSH向けの独立したPowerShellスクリプトです。
既存のSSHGuardプロジェクトのWindows移植版・公式関連製品ではありません。
OpenSSH/OpenBSDプロジェクトの公式製品でもありません。
無料で利用・改変・再配布できます。原版の再配布はZIPをそのまま使用してください。
改変版は改変者の氏名／法人名、連絡先、変更内容・日付を明記し、
改変に起因する問題の対応責任は改変者に帰属します。
法令で認められる範囲で無保証・免責とします。詳細はLICENSE.ja.txtを確認してください。
遮断条件は秘密ではありませんが、紹介ページでは積極的に列挙しません。

Windows Server 2019を例に、SSHポート22番で説明しています。
HTTP（80番）へのアクセスは、このGuardの保護対象ではありません。

【使用するマシンに必要な条件】
・Windows標準のOpenSSHサーバー（sshd）が、標準の構成で動作している。
  SSH接続するための「クライアント」だけでは足りません。
・64ビット版のWindows PowerShell 5.1が使用できる。
  PowerShell 7での実行は対象外です。
・Windowsファイアウォールが有効で、ローカル遮断ルールを追加できる。
  組織のポリシーなどで、ローカルルールの使用が禁止されていないこと。
・タスクスケジューラが使用できる。
・OpenSSH/Operationalイベントログが有効で、ログを取得できる。
・管理者権限でインストールできる。

基本は「標準OpenSSHサーバー」と「64ビット版Windows PowerShell 5.1」が
使えるWindowsですが、上のファイアウォール・ログなどの条件も必要です。
開発対象はWindows Server 2019です。他のWindowsへの適用は、
これらの条件を確認し、そのOSでの実機確認を行ってから運用してください。

【読む順番】
・初めて導入する場合：1章の手順1へ。手順2だけ飛ばして進みます。
・旧版から更新する場合：1章の手順1から順に進みます。
・導入・更新が終わったら：2章で確認し、必要なら3章のテストを行います。

用語は、この2つだけ覚えてください。
・Audit   ＝ 観察して記録する。新しい遮断はしない。
・Enforce ＝ 条件に該当した接続元を、実際に一時遮断する。

================================================================
1．インストール
================================================================

【準備】
作業はRDP（リモートデスクトップ）か、サーバーの画面から行います。
SSHの設定時にSSHサービスを再起動するため、SSH接続での作業は避けます。
先に、普段の方法でSSHログインできることを確認してください。

スタートメニューの「Windows PowerShell」を右クリックし、
「管理者として実行」を選びます。「PowerShell 7」は使いません。
次の2行で、Majorが5、Minorが1、最後の表示がTrueであることを確認します。

    $PSVersionTable.PSVersion
    [Environment]::Is64BitProcess

以下の字下げしたコマンドを、PowerShellへ1行ずつ貼り付けます。
画面で折り返されても、1つのコマンドを途中で分けないでください。


【手順1】ZIPをサーバー内に置く
C:\Temp フォルダーがなければ、次で作成します。

    New-Item -ItemType Directory -Path C:\Temp -Force | Out-Null

配布された OpenSshGuard.zip を C:\Temp\OpenSshGuard.zip にコピーし、
次の2行で展開します。

    Expand-Archive -LiteralPath C:\Temp\OpenSshGuard.zip -DestinationPath C:\Temp\OpenSshGuardPackage -Force
    Set-Location C:\Temp\OpenSshGuardPackage

展開先は作業用です。実際の配置先は通常 C:\ProgramData\OpenSshGuard です。


【手順2】旧版を使っている場合は、停止してからAuditへ戻す
初めて入れる場合は、この手順を飛ばします。
アクセス拒否が出ている場合も、次の順序で進めます。

(1) 両タスクを無効にし、Health、監視本体の順に停止する
この間はGuardの新規検知・遮断と期限切れ解除が止まります。
すでにある遮断ルールは残ります。そのまま放置せず、続けて更新を完了します。

    $guardPath = Join-Path ([Environment]::GetFolderPath('CommonApplicationData')) 'OpenSshGuard'
    Get-ScheduledTask -TaskPath '\' -TaskName 'OpenSshGuard.Health' -ErrorAction SilentlyContinue | Disable-ScheduledTask | Out-Null
    Get-ScheduledTask -TaskPath '\' -TaskName 'OpenSshGuard' -ErrorAction SilentlyContinue | Disable-ScheduledTask | Out-Null
    Get-ScheduledTask -TaskPath '\' -TaskName 'OpenSshGuard.Health' -ErrorAction SilentlyContinue | Stop-ScheduledTask
    Get-ScheduledTask -TaskPath '\' -TaskName 'OpenSshGuard' -ErrorAction SilentlyContinue | Stop-ScheduledTask

数秒待ち、次のコマンドで両タスクがDisabledになったことを確認します。
古い版にHealthタスクがない場合は、存在する監視本体だけが対象です。

    Get-ScheduledTask -TaskPath '\' -TaskName 'OpenSshGuard','OpenSshGuard.Health' -ErrorAction SilentlyContinue | Select-Object TaskName,State

手動で「Invoke-OpenSshGuard.ps1 -Watch」を起動した別ウィンドウがあれば、
その手動監視も終了します。

(2) 停止後にAuditへ切り替える
Enforceのままだと更新後も引き継がれるため、先にAuditへ戻します。
    powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$guardPath\Set-OpenSshGuardMode.ps1" -Mode Audit

Audit移行の確認。
    Get-Content -LiteralPath "$guardPath\config.json" -Raw | ConvertFrom-Json | Select-Object Mode,AutoEnforceAfterMinutes

ModeがAudit、かつAutoEnforceAfterMinutesが0なら切り替え設定は完了です。
停止中のstate.jsonには古い表示が残るため、ここではconfig.jsonを確認します。
表示例（次の3行は実行するコマンドではありません）：

  Mode  AutoEnforceAfterMinutes
  ----  -----------------------
  Audit                       0

(3) エラーがなければ、手順3へ進む
共有ロックの「Global\OpenSshGuard.v1」は、通常のファイルのパスではありません。
旧版ではロックの権限が作成ユーザーに依存し、管理者実行でも拒否される場合が
ありました。1.2.2で修正しましたが、残っている旧ロックを先に解放するため、
上記の停止が必要です。ロックは、保持する全プロセスが閉じると消えます。

停止してもアクセス拒否が続く場合は、先へ進まず次を確認します。
・RDPまたはコンソールの「管理者として実行」したPowerShellか。
・両タスクが停止しているか。手動監視や別の管理コマンドが残っていないか。
・まだ解決しなければ、エラー全文とタスク状態を保存して相談する。
  無効化したタスクは自動再開しないので、監視停止中であることも伝えます。

共有ロックの別名への書き換え、Everyoneへの権限追加、設定フォルダーの削除、
ファイアウォール全体の無効化は行いません。

【手順3】変更前の確認をする
次のコマンドは、確認だけで設定を変更しません。

    powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\Install-OpenSshGuard.ps1 -AllowNoTrustedNetwork -AutoEnforceAfterMinutes 1 -ConfigureSsh -WhatIf

「What if: ...」は確認実行の表示で、エラーではありません。
確認コマンドの直後に、次の行を実行して終了結果も確認します。

    $LASTEXITCODE

確認実行が最後まで終了し、赤いエラーがなく、結果が0なら手順4へ進みます。
Ctrl+Cなどで途中中断した場合は、0と表示されても成功扱いにせず再実行します。
0以外、または赤いエラーが出た場合は、次の対処を行います。


【手順3で赤いエラーが出た場合】

(1) 手順4の本実行へ進まず、エラーの全文を保存する
最初のエラー文から最後の行まで、コピーまたは画面撮影で残します。
この段階のエラーはguard.jsonlに残らないことがあるため、
インストール後のログだけでは確認できません。

文字で保存する場合は、同じ確認コマンドを次のように実行できます。

    powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\Install-OpenSshGuard.ps1 -AllowNoTrustedNetwork -AutoEnforceAfterMinutes 1 -ConfigureSsh -WhatIf *> C:\Temp\OpenSshGuard-install-check.log
    $LASTEXITCODE
    notepad C:\Temp\OpenSshGuard-install-check.log

この場合、結果は画面の代わりに.logファイルへ保存されます。
これも確認実行だけで、SSHやGuardの設定は適用しません。

(2) 表示内容に合う項目を確認する

A．「管理者」「Administrator」「elevated」「PowerShell5.1」
  Windows PowerShellを「管理者として実行」で開き直します。
  1章の準備で、バージョン5.1と64ビット（True）を再確認します。
  開き直した場合は、作業場所を次へ戻します。

    Set-Location C:\Temp\OpenSshGuardPackage

B．「ファイルが見つからない」「Missing package file」
  ZIP全体が同じフォルダーへ展開されているか確認します。
  .ps1ファイルだけを抜き出して実行しないでください。
  作業場所を上のフォルダーへ戻し、必要なら手順1で展開し直します。

C．「sshdが見つからない」「service is not installed」
  Windows標準のOpenSSHサーバーが導入されているか確認します。
  クライアントだけでは足りません。次でサービスの有無と状態を確認できます。

    Get-Service -Name sshd | Select-Object Name,Status

  サービスがない場合はOpenSSHサーバーの導入が先です。
  通常のSSHログインができる状態にしてから、確認実行をやり直します。

D．「OpenSSH/Operational」「Enable OpenSSH/Operational」
  イベントビューアーの
  「アプリケーションとサービス ログ」→「OpenSSH」→「Operational」を開きます。
  無効なら右クリックしてログを有効にします。
  OpenSSH自体が見つからない場合は、標準OpenSSHサーバーの導入状態を確認します。

E．「SSH port」「configured Guard TCP port」「managed settings」
  Guardの対象番号とSSH設定のPort/ListenAddressが一致するか確認します。
  更新時：実配置先のssh-port.txt（通常 C:\ProgramData\OpenSshGuard）が優先。
  新規時：展開先 C:\Temp\OpenSshGuardPackage\ssh-port.txt を使用。
  内容は1～65535の番号1個です。この手順なら22です。
  Guardのファイルだけを変えても、SSH本体の待受け番号は変わりません。
  番号の変更が必要ならREADME.ja.mdのポート変更手順に従います。

F．「sshd configuration validation failed」「Include」「Match」
  SSH設定の検証エラーです。次で設定を読み取り検証します。

    & "$env:WINDIR\System32\OpenSSH\sshd.exe" -t
    $LASTEXITCODE

  0以外なら、表示された設定ファイル・行・項目を確認します。
  自分で変更した箇所を見直すか、正しいバックアップと比較してください。
  このGuardでは独自のInclude構成などは自動調整しません。
  設定を適当に消して進めず、判断できなければエラー全文を添えて相談します。

G．「Global\OpenSshGuard.v1」「Guard shared lock」へのアクセス拒否
  1章の手順2へ戻り、両タスクと手動監視が停止しているか確認します。
  ファイルのアクセス権変更だけで直そうとしないでください。

H．「Untrusted」「untrusted identity」「Unrelated task」「installation marker」
  または設定JSONの読み取り・項目のエラー
  保存先の権限、既存タスク、既存設定などの確認が必要です。
  フォルダーやタスクを削除したり、Everyoneへ書き込み権限を追加したりせず、
  エラー全文と対象のパス・タスク名を添えて相談してください。
  config.jsonやstate.jsonの削除、既定値による上書きも行いません。

(3) 対処後、手順3の確認コマンドをもう一度実行する
「赤いエラーがない」「直後の$LASTEXITCODEが0」の両方を確認できたら、
手順4へ進みます。原因不明のまま-WhatIfを外して実行しないでください。

解決しない場合に伝える内容：
・実行したコマンドとエラー全文、終了結果。
・サーバー名、Windowsの種類、PowerShellのバージョン。
・新規導入か更新か。Guardタスクを停止済みか。

旧版の更新で手順2を実施した場合、解決するまではGuardの監視が停止しています。
この確認実行だけでは再開しません。停止中であることも必ず伝え、
対処と更新完了まで確認してください。


【手順4】インストールする

    powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\Install-OpenSshGuard.ps1 -AllowNoTrustedNetwork -AutoEnforceAfterMinutes 1 -ConfigureSsh

「Installed1.2.2: ...」と表示され、エラーがなければ登録完了です。
更新完了後は、停止したGuardタスクもインストーラーが登録・起動します。
この指定では、管理元の固定IPは登録せず、正常な監視が連続1分続くと
AuditからEnforceへ移ります。SSHのログ設定と接続制限も適用します。

SSH設定の変更先は通常 C:\ProgramData\ssh\sshd_config です。
変更前のバックアップも同じフォルダーに作られます。

22番以外を使う場合は、README.ja.mdの「SSH ポートの指定」を参照します。
Guardの設定は通常 C:\ProgramData\OpenSshGuard\ssh-port.txt、初期値は22。
SSH本体のポートは自動変更しません。Guardの編集後には再起動も必要です。

================================================================
2．インストール後の確認
================================================================

この章も、サーバー側の管理者PowerShellで実行します。
PowerShellを開き直していても使えるよう、最初に次の行を実行します。

    $guardPath = Join-Path ([Environment]::GetFolderPath('CommonApplicationData')) 'OpenSshGuard'


【確認1】自動実行が登録されているか

    Get-ScheduledTask -TaskPath '\' -TaskName 'OpenSshGuard','OpenSshGuard.Health' | Select-Object TaskName,State

正常時の目安：
・OpenSshGuard        Running（監視を実行中）
・OpenSshGuard.Health Ready（確認待ち）、または短時間だけRunning

主タスクは常駐するので、Runningのままで正常です。
タスクが見つからない、Disabledになっている場合は確認が必要です。


【確認2】別のPCから通常ログインを1回行う
普段使うSSHソフトで、正常にログインしてください。
Guardが「新しい接続」と「認証結果」を読み取れるか確認するためです。
接続後、数秒待ってから次へ進みます。


【確認3】監視時刻とモードを見る

    Get-Content -LiteralPath "$guardPath\state.json" -Raw | ConvertFrom-Json | Select-Object LastScanUtc,Mode,SshPort,PortReady,HealthReady,ConnectionCoverage,AutoEnforceStatus,AutoHealthMessage

見方：
・Mode               Auditは観察中、Enforceなら実際の遮断が有効。
・SshPort            この手順なら22。
・PortReady          TrueならSSH設定ファイルの実効ポートと一致。
・HealthReady        接続ログを取得でき、正常ならTrueが目安。
・ConnectionCoverage Observedなら対象ポートの接続ログを観測済み。
・LastScanUtc        最後にログを確認した時刻。
・AutoHealthMessage  準備不足などがある場合の説明。

10秒ほど置いて同じコマンドを実行し、LastScanUtcの更新を確認します。
時刻はUTCです。日本時間より9時間前の表示でも異常ではありません。
通常ログイン後、正常な監視が連続1分続いてModeがEnforceになれば完了です。
「インストールから単純に1分後」ではありません。


【確認4】ログを見る

    Get-Content -LiteralPath "$guardPath\guard.jsonl" -Tail 20

Actionの見方：
・Installed    インストールの記録
・AutoEnforced AuditからEnforceへ自動移行した
・WouldBlock   条件には該当したが、Auditなので遮断していない
・Blocked      実際に新しい遮断ルールを作成した
・Unblocked    遮断を解除した
・Alert / Error 注意事項・処理エラー。詳細を確認する

ログがあるだけで正常とは判断せず、タスク・時刻更新・HealthReadyも確認します。
アクセスのない回に、読み取ったログや認証結果が0件になるのは正常です。

================================================================
3．簡単な動作確認
================================================================

通常は「別PCから正常にSSHログインできること」と、
2章の「時刻更新・HealthReady・Enforce」を確認すれば導入確認になります。

必要な場合だけ、以下の方法で認証失敗の検知を試せます。
パスワード認証が使える環境向けです。新しい遮断は行わない状態で試します。


【テスト1】サーバー側でAuditへ戻す
自動移行もいったん停止します。既存の遮断は残ります。

    powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$guardPath\Set-OpenSshGuardMode.ps1" -Mode Audit


【テスト2】別PCから、存在しないユーザー名で接続する
SSHポート22の例です。ssh-server.exampleは説明用の接続先です。
実行前に、対象サーバーのホスト名またはIPアドレスに置き換えてください。
guard_check_test が実在する場合は、別の存在しない名前に置き換えます。

    ssh -p 22 -o PreferredAuthentications=password -o PubkeyAuthentication=no -o NumberOfPasswordPrompts=3 guard_check_test@ssh-server.example

60秒以内に合計3回、適当な文字をパスワードとして入力します。
入力文字が表示されないのは正常です。ログイン失敗が想定どおりの結果です。
サーバー自身や除外登録済みの接続元ではテストになりません。
パスワード入力が求められない場合は中止し、テスト4で通常運用へ戻します。


【テスト3】数秒後、サーバー側で検知結果を見る

    Get-Content -LiteralPath "$guardPath\guard.jsonl" -Tail 50 | ForEach-Object { $_ | ConvertFrom-Json } | Where-Object Action -eq 'WouldBlock' | ForEach-Object { $_.TimestampUtc + ' / ' + $_.Data.Address + ' / ' + $_.Data.Reason }

表示は「日時 / 接続元IP / 理由」の順です。テストPCの新しい記録を確認します。
インターネット経由なら、PCの私設IPではなく外向けIPが表示されます。
これは「検知」の確認で、実際の遮断を確認したことにはなりません。


【テスト4】必ず通常運用へ戻す

    powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$guardPath\Set-OpenSshGuardMode.ps1" -Mode Audit -AutoEnforceAfterMinutes 1

再設定後、別PCから正常なSSHログインをもう1回行います。
正常な監視が連続1分続き、ModeがEnforceに変わったことを確認してください。
実際の遮断が発生した場合は、ログにBlockedが記録されます。

================================================================
困ったとき
================================================================

・赤いエラーが出た：1章の「手順3で赤いエラーが出た場合」で対処します。
・Global\OpenSshGuard.v1へのアクセス拒否：1章の手順2で対処します。
・Enforceへ移らない：AutoEnforceStatusとAutoHealthMessageを確認します。
  初回のAwaitingConnectionは、まだ接続ログを観測していない状態です。
・ファイルがない／時刻が更新されない：2章のタスク状態を確認します。
  C:\Tempの展開先ではなく、実配置先を見ているかも確かめます。

・自分の接続元が遮断された：
  RDPかサーバーの画面から、Auditへ戻した後で全遮断を解除します。
  次の解除対象は、Guardが作成した遮断ルールです。

    powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$guardPath\Set-OpenSshGuardMode.ps1" -Mode Audit
    powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$guardPath\Invoke-OpenSshGuard.ps1" -ClearBlocks

  確認・修正後は、3章のテスト4で通常運用へ戻します。
  設定破損でAuditへ戻せない場合の復旧方法や詳しい設定変更は、
  README.ja.mdを参照してください。


================================================================
4．障害時の復旧（SSHがつながらない／監視が動かない）
================================================================

作業はRDPまたはサーバーのコンソールで行います。
この章の作業中はGuardの新しい遮断が止まります。
Windowsファイアウォール全体を無効にしたり、無関係なルールを削除したりしないでください。

【手順1】実際のインストール先を指定する
64ビット版Windows PowerShell 5.1を「管理者として実行」し、最初に入力します。
実行するカレントフォルダーは任意です。

    $ErrorActionPreference = 'Stop'
    $guardPath = Join-Path ([Environment]::GetFolderPath('CommonApplicationData')) 'OpenSshGuard'

【手順2】通常はAuditへ戻し、Guardの遮断を解除する

    powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$guardPath\Set-OpenSshGuardMode.ps1" -Mode Audit
    if ($LASTEXITCODE -ne 0) { throw 'Auditへの変更に失敗しました。手順3へ進んでください。' }
    powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$guardPath\Invoke-OpenSshGuard.ps1" -ClearBlocks
    if ($LASTEXITCODE -ne 0) { throw '解除に失敗しました。手順3で監視を止め、再確認してください。' }

Auditへの変更は自動Enforce移行の予約も解除します。
Auditへ戻すだけでは、既存の遮断は期限まで残るため、ClearBlocksを実行します。
別PCから通常のSSH接続を確認します。改善したらログ・設定を確認し、手順4へ進みます。

【手順3】設定破損・ロックエラー等で手順2ができない場合
管理者PowerShellへ戻り、両方のタスクを無効化して停止します。
Healthだけが残ると監視本体を再起動するため、両方を止めます。

    foreach ($name in @('OpenSshGuard.Health','OpenSshGuard')) {
        $task = Get-ScheduledTask -TaskPath '\' -TaskName $name -ErrorAction SilentlyContinue
        if ($task) {
            Disable-ScheduledTask -TaskPath '\' -TaskName $name | Out-Null
            Stop-ScheduledTask -TaskPath '\' -TaskName $name
        }
    }
    powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$guardPath\Invoke-OpenSshGuard.ps1" -ClearBlocks
    if ($LASTEXITCODE -ne 0) { throw '遮断解除に失敗しました。エラーを保存し、手動でルールを一括削除しないでください。' }

手動で -Watch を起動している場合も、そのプロセスを終了してから再試行します。
「Global\OpenSshGuard.v1へのアクセス拒否」が続く場合は、1章の手順2を参照します。
タスク停止だけでは既存の遮断ルールは消えません。ClearBlocksの成功を確認してください。

【手順4】原因を確認し、再開する
guard.jsonlの末尾、state.jsonのLastScanUtc・HealthReady・AutoHealthMessageと
OpenSSH/Operationalのイベントを確認します。設定・履歴は調査前に削除しません。
config.jsonが壊れている場合は、サーバー管理者が保存済みの正常なバックアップと比較して
復元します。config.example.jsonで安易に上書きすると、管理元の除外等を失います。
SSH自体が停止・設定不正の場合は、次の「SSH設定を戻す場合」を確認してください。

停止したタスクを戻す場合は、先にAuditへの切り替えが成功することを確認します。

    powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$guardPath\Set-OpenSshGuardMode.ps1" -Mode Audit
    if ($LASTEXITCODE -ne 0) { throw 'Auditにできないため、タスクは再開しません。' }
    Enable-ScheduledTask -TaskPath '\' -TaskName 'OpenSshGuard' | Out-Null
    Enable-ScheduledTask -TaskPath '\' -TaskName 'OpenSshGuard.Health' | Out-Null
    Start-ScheduledTask -TaskPath '\' -TaskName 'OpenSshGuard'
    Start-ScheduledTask -TaskPath '\' -TaskName 'OpenSshGuard.Health'

ファイル破損等で再配置が必要な場合は、1章の更新手順に沿って再インストールします。
別PCから通常ログインし、2章と3章で確認後、自動Enforce移行を設定し直します。

================================================================
5．アンインストール（Guardを使用しなくなった場合）
================================================================

RDPまたはサーバーのコンソールから、管理者PowerShell 5.1で行います。
通常の実行フォルダーは任意です。C:\Tempの展開先ではなく、インストール先の
Uninstall-OpenSshGuard.ps1を指定します。

【手順1】配置先を指定し、アンインストールする

    $ErrorActionPreference = 'Stop'
    $guardPath = Join-Path ([Environment]::GetFolderPath('CommonApplicationData')) 'OpenSshGuard'
    powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$guardPath\Uninstall-OpenSshGuard.ps1"
    if ($LASTEXITCODE -ne 0) { throw 'アンインストール未完了です。赤いエラーを保存して対処してください。' }

Guard所有と確認できる2タスクと遮断ルールを削除します。
「Removed ... owned task(s) ... guard rule(s)」が完了表示です。
設定・履歴・ログ・スクリプト・SSH設定とバックアップは残ります。
SSHサービス、通常のSSH受信許可ルール、他のルールは削除しません。

【手順2】削除できたことを確認する

    Get-ScheduledTask -TaskPath '\' -TaskName 'OpenSshGuard','OpenSshGuard.Health' -ErrorAction SilentlyContinue
    Get-NetFirewallRule -PolicyStore PersistentStore | Where-Object Group -eq 'OpenSshGuard.v1'

両方とも何も表示されないことを確認します。別PCから通常のSSHログインも確認します。
残っている場合や赤いエラーが出た場合は、フォルダーを削除せず、4章で両タスク停止と
遮断解除を確認し、アンインストールを再実行します。
所有確認エラーの場合は、同名の他製品・他用途のタスクやルールを手動で消さないでください。

【手順3】残ったファイルを整理する（任意）
監視タスクと遮断ルールがなくなったことを確認してから、必要なログ・設定を退避します。
Guardを再利用しない場合は、エクスプローラーで実配置先OpenSshGuardフォルダーを
確認して削除できます。ProgramData全体やsshフォルダーは削除しません。
実配置先を削除する前に、SSH設定を戻す必要がないか確認してください。

================================================================
SSH設定を戻す場合（必要な場合だけ）
================================================================

GuardのアンインストールではSSH設定を自動復元しません。
ログ設定や接続制限を元に戻したい場合だけ、管理者がバックアップを選んで復元します。
これによりSSHが再起動されます。RDP／コンソールを使い、SSHだけで作業しないでください。
バックアップ取得後の正当な変更やポート設定を失わないよう、現在との差を確認します。

【手順1】バックアップ候補を表示する

    $sshDir = Join-Path ([Environment]::GetFolderPath('CommonApplicationData')) 'ssh'
    $sshConfig = Join-Path $sshDir 'sshd_config'
    $sshdExe = Join-Path $env:WINDIR 'System32\OpenSSH\sshd.exe'
    Get-ChildItem -Path (Join-Path $sshDir 'sshd_config.OpenSshGuard.*.backup') | Select-Object FullName,LastWriteTime,Length

バックアップがない場合は自動で復元できません。
最新ファイルを無条件で選ばず、対象サーバー・日時・内容を確認してください。

【手順2】復元するファイルを指定する
次の引用符内を、選んだバックアップのFullNameへ置き換えます。

    $restorePath = 'ここに選んだバックアップの絶対パスを入力'
    if (-not (Test-Path -LiteralPath $restorePath -PathType Leaf)) { throw 'バックアップの指定を確認してください。' }
    & $sshdExe -t -f $restorePath
    if ($LASTEXITCODE -ne 0) { throw 'SSH設定の検証に失敗しました。復元しないでください。' }

何も表示されず、赤いエラーもないことを確認します。
メモ帳等で内容を比較し、このバックアップへ戻してよいことを確認してから次へ進みます。

【手順3】現在の設定を保存してから復元する

    $beforeRestore = Join-Path $sshDir ('sshd_config.before-restore.' + [Guid]::NewGuid().ToString('N') + '.backup')
    Copy-Item -LiteralPath $sshConfig -Destination $beforeRestore -ErrorAction Stop
    Copy-Item -LiteralPath $restorePath -Destination $sshConfig -Force -ErrorAction Stop
    & $sshdExe -t -f $sshConfig
    if ($LASTEXITCODE -ne 0) { throw '設定を再検証できません。SSHを再起動せず、保存した設定と権限を確認してください。' }
    Restart-Service -Name sshd -ErrorAction Stop
    Get-Service -Name sshd

StatusがRunningであることと、別PCからのログインを確認します。
失敗した場合はRDP／コンソールを保ったまま、before-restoreの保存先を使って
同じ検証・復元手順で直前の設定に戻し、エラーを調査します。
Guardを継続利用する場合は、SSH・受信許可・ルーター転送とssh-port.txtの番号を合わせ、
Auditから動作確認します。
