0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Cloudflare Tunnel経由でMacからWindowsに公開鍵認証SSH接続する

0
Last updated at Posted at 2026-05-08

Cloudflare Tunnel経由でMacからWindowsに公開鍵認証SSH接続する

はじめに

mac を client として持っておき、Windows にアクセスすることを前提として組んでいます。

そのため mac についている ssh-keygen などの mac 特有の SSH キー発行で話を進めるので、windows -> mac にアクセスするということは想定せずに今回は進めていきます。

最終的に達成したいのは、Cloudflare Tunnel を使って ssh 化した安全な通信経路を利用した remote cli 接続の実現です。VPN を使わずに、世界中どこからでも Mac のターミナルから Windows の CLI を叩ける状態を目指します。

想定環境

  • リモート(接続先): Windows 11 (Build 26100)
  • クライアント(接続元): Mac (Apple Silicon 想定)
  • 通信経路: Cloudflare Zero Trust Tunnel

通信フローのイメージ

Mac
 └─ cloudflared access ssh
     └─ Cloudflare Edge
         └─ Tunnel (cloudflared on Windows)
             └─ localhost:22 (OpenSSH Server)

前提: ドメインが必須

Cloudflare Tunnel を使うには Cloudflare 上で管理されているドメイン(ゾーン)が必須です。win.あなたのドメイン のような形でアクセスするので、その「あなたのドメイン」の部分が Cloudflare の管理下にないと Public Hostname を割り当てられません。

ドメインの状況によって、最初にやることが変わります。

パターン A: Cloudflare Registrar でドメインを買う/既に持っている

一番楽です。何もしなくて OK です。Cloudflare で取得したドメインは自動的に Cloudflare のネームサーバーを使うようになっているので、そのまま「2/4 Cloudflare での設定」に進めます。

パターン B: 他社レジストラ(お名前.com、Google Domains、ムームードメインなど)でドメインを買っている

Cloudflare に そのドメインのゾーンを移管する作業(Add a site) が必要です。これは「ドメイン自体の所有権を移す」のではなく、「DNS の管理だけを Cloudflare にやらせる」設定で、無料プランでも可能です。

他社レジストラのドメインを Cloudflare に追加する手順
  1. Cloudflare ダッシュボード にログイン
  2. Domains > Onboard a domain をクリック
  3. apex ドメイン(例: example.org)を入力。プランは Free で OK
  4. Cloudflare が既存の DNS レコードをスキャンしてくれるので、レコードに漏れがないか確認
  5. Cloudflare から 2 つのネームサーバー名(例: ada.ns.cloudflare.commicah.ns.cloudflare.com)が割り当てられる
  6. 元のレジストラ(お名前.com など)の管理画面にログインし、ネームサーバー設定を Cloudflare から指定された 2 つに書き換える
  7. 反映には最大 24 時間かかります(実際は数分〜数時間で済むことが多い)
  8. Cloudflare のダッシュボードでドメインのステータスが Active になったら準備完了

[!WARNING]
元のレジストラで DNSSEC を有効にしている場合は、ネームサーバー変更に DNSSEC をオフにしてください。有効なまま切り替えるとドメインが一時的に到達不能になります。Cloudflare 側で再度有効化できます。

[!NOTE]
既にそのドメインで Web サイトやメールを運用している場合、ネームサーバー変更時に既存の DNS レコード(A、MX、TXT など)が全部 Cloudflare 側にも登録されているかを必ず確認してください。スキャンで拾えないレコードがあると、サイトやメールが止まります。

パターン C: ドメインを持っていない

新規取得が必要です。Cloudflare Registrar で買ってしまうのが一番手間がかかりません(パターン A になります)。年間 $10 前後で .com などが取れます。


全体像

基本的にすることは

  1. インストール
  2. Cloudflare での設定
  3. 鍵作成
  4. 接続

という流れになります。

各セクションで Windows 側で行う作業か Mac 側で行う作業かが混在するため、コードブロックの言語タグと見出しで「どちら側の作業か」を明示しています。

タグ 実行する場所
```powershell Windows の PowerShell
```cmd Windows のコマンドプロンプト
```bash Mac のターミナル

1/4 インストール

このセクションでは、Windows 側に「OpenSSH Server」と「cloudflared」を、Mac 側に「cloudflared」をインストールします。

1-1. Windows: OpenSSH Server のインストール

Windows 10/11 には標準で OpenSSH の機能自体は含まれていますが、初期状態だと クライアント(外に接続する機能)のみが入っており、サーバー(外から接続される機能)は手動で追加する必要があります。

インストール状態の確認

まず、PowerShell を 管理者として実行 で開きます(一般ユーザー権限だと 要求された操作には管理者特権が必要です と弾かれます)。

以下のコマンドで状態を確認します。

Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH.Server*'

State : NotPresent と表示された場合は未インストールなので次に進みます。State : Installed だったらインストール作業はスキップして問題ありません。

インストール実行

管理者 PowerShell で以下を実行します。

Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0

完了すると Online : True RestartNeeded : False と表示されます。

[!NOTE]
もしここで コンポーネント ストアが壊れています というエラーが出たら、もう一度同じコマンドを叩くと通ることがあります。それでも駄目な場合は Dism /Online /Cleanup-Image /RestoreHealth でシステム修復を試してください。

サービスの起動と自動化

インストールしただけではサービスは止まっているので起動させます。

# サービスを今すぐ開始
Start-Service sshd

# Windows 起動時に自動で開始されるように設定
Set-Service -Name sshd -StartupType 'Automatic'

動作確認

Get-Service sshd

Status : Running になっていれば Windows 側の SSH 受け入れ態勢は完成です。

1-2. Windows: cloudflared のインストール

Cloudflare の管理画面(Zero Trust ダッシュボード)から「Tunnel」を作成すると、Windows 用のインストーラ(.msi ファイル)が自動で生成されてダウンロードできます。これを実行するだけで cloudflared 本体のインストールとトンネル接続情報の登録が同時に終わります。

具体的な作成手順は次セクション「2/4 Cloudflare での設定」で扱います。

1-3. Mac: cloudflared のインストール

Mac 側でも、Cloudflare のトンネルを通すための cloudflared が必要です。Homebrew で入れるのが一番楽です。

Homebrew がまだ入っていない場合

Mac のターミナルで以下を実行すれば Homebrew 自体をインストールできます。

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

インストール完了後、ターミナル上に Next steps: として PATH を通すコマンド(echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile のようなもの)が表示されるので、それも忘れずに実行してください。

詳細な手順や M1 Mac 特有の注意点については Homebrew をインストールする (Qiita) が参考になります。

brew install cloudflared

インストールできたか確認します。

cloudflared --version

バイナリのパスは後で SSH config に書く必要があるので、ここで控えておきます。

which cloudflared
  • Apple Silicon (M1/M2/M3): /opt/homebrew/bin/cloudflared
  • Intel Mac: /usr/local/bin/cloudflared

のどちらかになっているはずです。


2/4 Cloudflare での設定

このセクションは全部 ブラウザ上の Cloudflare Zero Trust ダッシュボード での操作です。コマンドはありません。

2-1. Tunnel の作成

  1. Cloudflare Zero Trust Dashboard にログイン
  2. 左メニューから Networks > Tunnels を開く
  3. Create a tunnel をクリック
  4. コネクタの種類は Cloudflared を選択
  5. 任意の名前(例: windows-ssh)を付ける
  6. Windows 用のコネクタ(.msi)をダウンロード・インストール

インストール後、しばらくするとダッシュボード上でトンネルのステータスが HEALTHY になります。

2-2. Public Hostname の設定

トンネルを作っただけでは「ただの土管」が通っただけで、どのドメインでアクセスしたら何に繋ぐかが決まっていません。これを設定します。

  1. 作成したトンネルの Edit をクリック
  2. Public Hostname タブを選択
  3. Add a public hostname をクリック
  4. 以下のように入力
項目
Subdomain win(任意)
Domain 自分が Cloudflare に登録しているドメインを選択
Path 空欄
Service Type SSH
Service URL ssh://localhost:22

[!IMPORTANT]
Service URL は ssh:// プレフィックスが必須です。localhost:22 だけだと「サービス URL の形式が無効です(https://、tcp:// などのプロトコルで始める必要があります)」というエラーになります。

Cloudflare Tunnel は SSH 以外にも HTTP やデータベース通信など色々運べるため、「この通信は ssh プロトコルで Windows 内の localhost の 22 番に届けてね」と明示的に指定する必要があります。

入力したら Save hostname で保存します。

これで win.あなたのドメイン のような完全なホスト名で、Windows の SSH ポートに到達できる経路が完成しました。

[!NOTE]
設定画面の項目名が「Public Hostname」(公開アプリケーション/公開ホスト名) なので「これって全世界に公開するの?」と不安になりますが、ここでの「Public」は「インターネット側からそのアドレスにアクセスできる」という意味です。

アクセスを自分だけに絞るには、別途 Access > Applications でポリシーを設定することで、SSH 接続前にブラウザでの本人認証(Google ログインなど)を強制できます。これが本来のゼロトラスト構成ですが、本記事のスコープ外なので別途設定してください。


3/4 鍵作成

ここまでで「パスワード認証での SSH 接続」は技術的には可能な状態になっています。実際にこの段階で ssh ユーザー名@win.あなたのドメイン を叩けば、Windows のログインパスワードを入力してログインできます。

ただし、毎回 Microsoft アカウントの長いパスワードを打つのは面倒なうえ、セキュリティ的にも公開鍵認証に切り替えるのが定石なので、これを設定します。

3-1. Mac: 鍵ペアの作成

Mac のターミナルで、今回の Windows 接続用の鍵ペアを作成します。

ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_win

パスフレーズはお好みで設定してください(空 Enter でなしでも可)。

これで以下の 2 つのファイルが作られます。

  • ~/.ssh/id_ed25519_win(秘密鍵 / 絶対に外に出さない)
  • ~/.ssh/id_ed25519_win.pub(公開鍵 / これを Windows に渡す)

秘密鍵の権限を念のため確認しておきます。

ls -al ~/.ssh/id_ed25519_win

-rw------- (600) になっていれば OK です。緩い権限だと SSH クライアントが安全のために鍵の使用を拒否します。もし違う場合は以下で修正してください。

chmod 600 ~/.ssh/id_ed25519_win

3-2. Mac: ~/.ssh/config の設定

接続情報を設定ファイルにまとめます。これがないと毎回 ProxyCommand を手で書く羽目になります。

nano ~/.ssh/config

以下を追記します(HostNameUser は自分の環境に合わせて書き換え)。

Host win-cli
    HostName win.あなたのドメイン
    User あなたのWindowsユーザー名
    IdentityFile ~/.ssh/id_ed25519_win
    IdentitiesOnly yes
    ProxyCommand /opt/homebrew/bin/cloudflared access ssh --hostname %h

各項目の意味:

項目 説明
Host win-cli Mac でコマンドを打つ時の「あだ名」。ssh win-cli で接続できる
HostName 実際のドメイン名。Cloudflare の Public Hostname で設定したもの
User Windows のユーザー名(whoami で確認したもの)
IdentityFile 使用する秘密鍵のパス
IdentitiesOnly yes ここが重要。指定した鍵以外を試させない
ProxyCommand SSH 通信を cloudflared 経由に流す
%h HostName の値が自動展開される変数

[!IMPORTANT]
IdentitiesOnly yes を入れる理由は、SSH エージェントに既に登録されている他の鍵(id_ed25519 など)を SSH クライアントが先に試してしまい、Windows 側に登録した鍵に辿り着く前に認証が失敗するケースがあるためです。これを入れないと「設定は完璧なはずなのにパスワードを聞かれる」現象でハマります。

[!NOTE]
cloudflared のパスは which cloudflared で確認した値に書き換えてください。Apple Silicon なら /opt/homebrew/bin/cloudflared、Intel Mac なら /usr/local/bin/cloudflared です。

3-3. Windows: ユーザー名を確認

Mac の config に書く User の正しい値を確認しておきます。Windows 側でパスワード認証で一度ログインするか、実機で PowerShell を開いて以下を実行します。

whoami

pcname\username のように表示されますが、このうち \ の右側(username の部分)だけを Mac の config の User に書きます。

[!NOTE]
Microsoft アカウントでサインインしている場合、ユーザー名がメールアドレスの先頭 5 文字に自動設定されていることがあります(例: nikeri4649@gmail.comnikeri)。あくまで whoami で出てくる文字列を正として使ってください。

3-4. Windows: 公開鍵を authorized_keys に登録

ここが一番ハマりポイントが多い箇所です。順番に進めます。

公開鍵を Windows 側に書き込む

Mac で公開鍵をコピーします。

cat ~/.ssh/id_ed25519_win.pub

ssh-ed25519 AAAA... で始まる 1 行の文字列を全部コピーします。

次に、一度パスワード認証で Windows に SSH ログインします(鍵認証はまだ動かないのでパスワードで入る)。

ssh あなたのWindowsユーザー名@win.あなたのドメイン

初回は The authenticity of host ... can't be established. というフィンガープリント確認が出ますが、これは SSH の標準的な「初めての相手なので信頼するか確認」のメッセージです。yes と入力して進めます。続けて Windows のログインパスワード(Microsoft アカウントを使っている場合は PIN ではなく Microsoft アカウントのフルパスワード)を入力すれば入れます。

ログイン後、Windows 側の PowerShell で .ssh ディレクトリと authorized_keys ファイルを作成し、公開鍵を書き込みます。

# .ssh ディレクトリを作成(既にあってもエラーにならない)
New-Item -ItemType Directory -Force -Path $HOME\.ssh

# 公開鍵を書き込む("ssh-ed25519 ..." の部分を貼り付け)
Set-Content -Path "$HOME\.ssh\authorized_keys" -Value "ssh-ed25519 AAAA...貼り付け..." -NoNewline

[!WARNING]
Mac 側から cat ... | ssh ... でパイプ経由で書き込もうとすると、PowerShell の引数解釈の都合で内容が壊れて System.Management.Automation.Runspaces.PipelineReader... のような型情報がファイルに書き込まれてしまうことがあります。素直に SSH ログインしてから Set-Content で書く方が確実です。

書き込めたら確認します。

Get-Content "$HOME\.ssh\authorized_keys"

ssh-ed25519 AAAA... という自分の公開鍵が表示されれば OK です。

authorized_keys の権限(ACL)を絞る

Windows の OpenSSH は アクセス権限に非常に厳格で、authorized_keys に他のユーザーの書き込み権限が付いていると「セキュリティリスクあり」と判断して鍵を完全に無視します。これを修正します。

Windows の PowerShell で以下を順に実行します(管理者権限不要)。

# 権限の継承を無効化(親フォルダから引き継いだ権限を全部切る)
icacls "$HOME\.ssh\authorized_keys" /inheritance:r

# 自分(現在のユーザー)に読み書き権限を付与
icacls "$HOME\.ssh\authorized_keys" /grant:r "${env:USERNAME}:(R,W)"

# SYSTEM に読み書き権限を付与
icacls "$HOME\.ssh\authorized_keys" /grant:r "SYSTEM:(R,W)"

[!NOTE]
ここで使っている $path のような PowerShell 変数は、システム環境変数の $PATH とは別物です。スクリプト内のローカル変数なので、システムには一切影響しません。

もしコマンドプロンプト(cmd)でやる場合は、構文が違うので以下になります(%USERPROFILE%, %USERNAME% を使う):

icacls "%USERPROFILE%\.ssh\authorized_keys" /inheritance:r
icacls "%USERPROFILE%\.ssh\authorized_keys" /grant %USERNAME%:(R,W)
icacls "%USERPROFILE%\.ssh\authorized_keys" /grant SYSTEM:(R,W)

3-5. Windows: 管理者ユーザー特有の罠を回避する

ここが最後の関門です。Windows の OpenSSH は、管理者グループに所属するユーザーがログインする場合、デフォルトで $HOME\.ssh\authorized_keys ではなく、システム共通の C:\ProgramData\ssh\administrators_authorized_keys という別ファイルを参照するという仕様になっています。

このため、自分のホームディレクトリにいくら鍵を置いても無視され続けます。これを無効化して、ユーザー個別の authorized_keys を見るように設定します。

sshd_config の編集

C:\ProgramData\ssh\sshd_config を編集する必要がありますが、このファイルは管理者権限がないと書き換えられません。

現在の SSH セッションが管理者権限ではない場合(通常はそう)、Linux の sudo のような「セッション内で昇格する」コマンドは Windows 標準では存在しません。以下のいずれかで対応します。

方法 A: 実機 or リモートデスクトップから一発で済ませる

Windows を直接触れる状況なら、管理者として PowerShell を開いて以下を一気に実行するのが最速です。

$path = "C:\ProgramData\ssh\sshd_config"
(Get-Content $path) | ForEach-Object {
    if ($_ -match "Match Group administrators" -or $_ -match "AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys") {
        "# " + $_
    } else {
        $_
    }
} | Set-Content $path

Restart-Service sshd

このスクリプトは、sshd_config の末尾にある以下 2 行を自動でコメントアウトしてサービスを再起動します。

Match Group administrators
       AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys

方法 B: gsudo を導入してセッション内で昇格する

完全に Mac からの SSH 越しだけで完結させたい場合は、gsudo(Windows 版 sudo)を入れておくと便利です。Microsoft 公式のパッケージマネージャ winget でインストールできます。

winget install gsudo
winget が使えない/コマンドが見つからない場合

wingetWindows 11 では標準で利用可能ですが、Windows 10 の古いビルドや、まれに Windows 11 でも「App Installer」が古いと使えないことがあります。

winget --version を実行して、コマンドが認識されない・バージョンが極端に古い場合は以下のいずれかで対処します。

対処法 1: Microsoft Store から「アプリ インストーラー(App Installer)」を更新する

一番楽です。Microsoft Store を開き、「アプリ インストーラー」または「App Installer」で検索して更新してください。これで winget も最新版になります。

対処法 2: GitHub から直接インストール

Store が使えない環境では、Microsoft/winget-cli の Releases ページ から最新の Microsoft.DesktopAppInstaller_*.msixbundle をダウンロードして、PowerShell(管理者)で以下のように手動インストールします。

Add-AppxPackage -Path "ダウンロードしたファイルのパス.msixbundle"

対処法 3: winget を使わずに gsudo を直接入れる

そもそも winget を入れるのが面倒なら、gerardog/gsudo の Releases ページ から gsudo.portable.zip を落として、解凍したフォルダにパスを通すだけでも使えます。

[!NOTE]
winget の基本コマンドや、よくあるハマりポイント(管理者権限が要るケース、同名パッケージの曖昧さ、英語版/日本語版の取り違えなど)については Wingetについて (Qiita) がコンパクトにまとまっていて参考になります。

導入後は、SSH セッションの中から以下のように使えます。

sudo notepad C:\ProgramData\ssh\sshd_config

メモ帳で末尾の以下 2 行を # でコメントアウトして保存します。

# Match Group administrators
#       AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys

その後、サービスを再起動します。

sudo Restart-Service sshd

[!NOTE]
なぜ Linux のように sudo 一発で昇格できないかというと、Windows の権限モデルでは プロセス起動時に権限が固定されるためです。一度「一般ユーザーとして開始された sshd の子プロセス(あなたのシェル)」を、後から「管理者権限のプロセス」に化けさせることが構造的にできない設計になっています。


4/4 接続

ここまで来たら、あとはテストするだけです。

4-1. Mac から接続

Mac のターミナルで設定したあだ名を使って接続します。

ssh win-cli

成功すると、パスワード入力なしで以下のように Windows のプロンプトが返ってきます。

Microsoft Windows [Version 10.0.26100.4061]
(c) Microsoft Corporation. All rights reserved.

ユーザー名@PC名 C:\Users\ユーザー名>

これで完成です。世界中どこからでも安全に Windows の CLI を Mac から叩ける環境ができあがりました。

4-2. うまく繋がらない時のチェックリスト

万が一パスワードを聞かれてしまう場合は、以下を順に確認します。

デバッグログを見る

一番情報量が多いので、まずはこれです。

ssh -v win-cli

出力の中で以下のような行を探します。

  • debug1: Offering public key: ~/.ssh/id_ed25519_win → 鍵を提示できている
  • debug1: Will attempt key: ... の一覧に id_ed25519_win が入っているか
  • debug1: Authentications that can continue: publickey,password,keyboard-interactive の後の挙動

もし id_ed25519_winOffering されていない場合、Mac の ~/.ssh/config の設定が読まれていないか、Host 名のミスマッチです。ssh win-cli(あだ名)で繋いでいることを確認してください。ssh win.あなたのドメイン だと Host win-cli ブロックが適用されません。

各レイヤーの確認項目

レイヤー 確認項目
Windows Get-Service sshdRunning
Cloudflare ダッシュボード上で Tunnel が HEALTHY
Windows Get-Content $HOME\.ssh\authorized_keys で公開鍵が正しく入っているか(ssh-ed25519 AAAA... で始まる文字列が 1 行で入っているか)
Windows icacls $HOME\.ssh\authorized_keys で自分と SYSTEM 以外に権限がないか
Windows sshd_configMatch Group administrators の 2 行がコメントアウトされているか
Windows sshd_config 編集後に Restart-Service sshd したか
Mac 秘密鍵が -rw------- (600) になっているか
Mac ~/.ssh/configIdentityFileIdentitiesOnly yes が入っているか

公開鍵の余計な改行を削る

Set-Content でうっかり改行を含めて書き込んでしまった場合、以下で末尾の余計な空白を削れます。

$content = (Get-Content "$HOME\.ssh\authorized_keys").Trim()
Set-Content -Path "$HOME\.ssh\authorized_keys" -Value $content -NoNewline

以上で完了となります。
お疲れ様でした。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?