【完全版】sudo なしでヘッドレスサーバの codex login を成功させる実戦ガイド
はじめに
本記事では、sudo権限なしの環境で、ヘッドレスなリモートサーバ上での codex login を高確率で成功させるための実戦的なガイドを提供します。
SSH ローカルフォワードを用いたトンネリング、ポート競合時の回避策(ローカル側ポート変更・URL書き換え・デバイスコード方式など)を網羅的に解説します。
0. TL;DR(最短手順)
前提条件
- ローカルマシンにブラウザがある
- リモートホスト名は
golgi(~/.ssh/configで設定済み) -
codexはリモートで実行
最短3ステップ
ステップ1: ローカルでSSHトンネルを作成
ssh -fN -o ExitOnForwardFailure=yes \
-L 127.0.0.1:1455:127.0.0.1:1455 golgi
ステップ2: リモートで codex を起動
ssh golgi
codex login
ステップ3: 表示されたURLをローカルブラウザで開く
端末に表示された長いURLをローカルのブラウザで開きます。
1. 仕組み:なぜSSHトンネルが必要なのか
OAuth認証フローの課題
-
codex loginは一時的なHTTPサーバを リモート側の127.0.0.1:1455に立ち上げる - ブラウザはローカルにあるため、そのままではコールバックがローカルに落ちてしまう
- OAuth認証のコールバックがリモートサーバに届かず、認証が完了しない
SSHローカルフォワードによる解決
SSH の -L オプションでトンネルを作成することで、以下の通信経路を確立します:
ローカルブラウザ → ローカル:1455 → [SSHトンネル] → リモート:1455 → codex
これにより、ローカルブラウザからのコールバックがリモートの codex プロセスに正しく届きます。
2. 用語の整理
本記事での用語の定義を明確にしておきます。
| 用語 | 説明 |
|---|---|
| ローカル | あなたのPC(ブラウザがある側) |
| リモート | サーバ(codex login を実行する側) |
| トンネル | SSH ローカルフォワード(-L オプション) |
3. 標準手順(再現性重視)
3.1 前提確認
以下を事前に確認してください:
-
ローカルから
ssh golgiでログインできる - ローカルマシンにブラウザがある
- sudo権限は不要(一般ユーザー権限のみで完結)
3.2 実行手順
(1) ローカルでSSHトンネルを作成
ssh -fN -o ExitOnForwardFailure=yes \
-L 127.0.0.1:1455:127.0.0.1:1455 golgi
オプションの説明:
-
-f: バックグラウンドで実行 -
-N: リモートコマンドを実行しない(トンネル専用) -
-o ExitOnForwardFailure=yes: ポートフォワードに失敗したら即座に終了
(2) ローカルでLISTEN状態を確認
Linux:
ss -ltn 'sport = :1455' 2>/dev/null || true
macOS:
lsof -iTCP:1455 -sTCP:LISTEN -n -P || true
(3) リモートで codex を実行
ssh golgi
codex login
端末に表示されたURLをローカルのブラウザで開きます。
(4) 終了後、トンネルをクローズ
pkill -f 'ssh -N -L .*1455:127\.0\.0\.1:1455' || true
4. エラーの切り分け(一次診断)
ローカルで ssh -L が失敗する場合
エラーメッセージ:
bind [127.0.0.1]:1455: Address already in use
原因: ローカルのポート1455が既に使用されている
確認方法: 5.1節 を参照
リモートで codex login が失敗する場合
エラーメッセージ:
Port 127.0.0.1:1455 is already in use
原因: リモートのポート1455が占有されている(過去の codex プロセスや他ユーザーのプロセス)
確認方法: 5.2節 を参照
ブラウザ認証後も完了しない場合
症状: ブラウザで認証したのに codex が反応しない
原因:
- トンネルが張られていない
- URLが
localhostのままでIPv6に流れている - ローカル側で別ポートを使用しているのにURL未調整
5. トラブルシューティング
5.1 ローカル側のポート競合
症状
ssh -L 実行時に以下のエラーが表示される:
bind [127.0.0.1]:1455: Address already in use
確認コマンド
Linux:
ss -ltn 'sport = :1455' 2>/dev/null || true
macOS:
lsof -iTCP:1455 -sTCP:LISTEN -n -P 2>/dev/null || true
プロセス確認:
pgrep -af 'ssh -N -L .*1455:127\.0\.0\.1:1455|codex' || true
対処法A: 既存プロセスの終了
自分のSSH/codexプロセスを終了させます:
pkill -f 'ssh -N -L .*1455:127\.0\.0\.1:1455' || true
pkill -f '^codex .*login' || true
対処法B: ローカルで別ポートを使用
ローカル側のみ別ポート(例: 21455)を使用します:
LPORT=21455
ssh -fN -o ExitOnForwardFailure=yes \
-L 127.0.0.1:$LPORT:127.0.0.1:1455 golgi
LISTEN確認:
ss -ltn "sport = :$LPORT" 2>/dev/null || true
対処法C: ポート自動割当て
ローカルポートを自動割当て(:0)にする方法:
ssh -vfN -o ExitOnForwardFailure=yes \
-L 127.0.0.1:0:127.0.0.1:1455 golgi 2> /tmp/ssh-tunnel.log
LPORT=$(grep -oE 'Local forwarding listening on 127\.0\.0\.1 port [0-9]+' \
/tmp/ssh-tunnel.log | awk '{print $NF}')
echo "Local port = $LPORT"
その後、コールバックURLの :1455 を :$LPORT に書き換えて開きます。
5.2 リモート側のポート競合
症状
codex login 実行時に以下のエラーが表示される:
Port 127.0.0.1:1455 is already in use
確認コマンド(リモートで実行)
# ポート使用状況
ss -ltn 'sport = :1455' || true
# 自分のcodex/sshプロセス
pgrep -af 'codex|ssh.* -R .*1455' || true
# 詳細表示
ps -u "$USER" -f | egrep 'codex|ssh.*-R|ssh.*-L' | grep -v egrep || true
対処法A: 自分のプロセスを終了
pkill -f '^codex .*login' || true
pkill -f 'ssh.* -R .*1455' || true
対処法B: codex のポート変更機能を利用
他ユーザーがポートを使用している場合、kill できません(sudo不要縛りの制約)。
codex がコールバックポートの変更に対応している場合:
# まず確認
codex help login
# 例(実際のオプションはヘルプを確認)
codex login --port 31455
この場合、トンネルも新しいポートに合わせます:
RPORT=31455 # リモート側の新ポート
ssh -fN -o ExitOnForwardFailure=yes \
-L 127.0.0.1:1455:127.0.0.1:$RPORT golgi
対処法C: デバイスコード方式(推奨)
codex がデバイスコード方式をサポートしている場合、これが最も安定します(ポートを使用しません)。
手順:
- リモートでコマンド実行
- 端末にURLとコードが表示される
- ローカルのブラウザでURLを開き、コードを入力・承認
- リモートの
codexが完了表示
# 例(実際のコマンドは codex help login で確認)
codex login --device
5.3 IPv6による接続失敗
症状
ブラウザで認証したのに codex が完了しない、または接続失敗する。
原因
URLの localhost が環境によってIPv6(::1)に解決され、トンネルがIPv4(127.0.0.1)を待ち受けているため。
対処
認証URLの localhost を 127.0.0.1 に置換して開きます。
ローカルで別ポートを使用している場合は、:1455 も :LPORT に書き換えます。
5.4 URL書き換えの具体例
シナリオ
- ローカルポートを
LPORT=21455でトンネル作成済み - 認証後のリダイレクト先が以下のURL:
http://localhost:1455/auth/callback?code=ABC123&state=XYZ789
対処
以下のように書き換えて開きます:
http://127.0.0.1:21455/auth/callback?code=ABC123&state=XYZ789
ワンライナーでの変換
手作業が面倒な場合は以下のように変換できます:
URL='http://localhost:1455/auth/callback?code=ABC123&state=XYZ789'
LPORT=21455
echo "$URL" | sed -E "s#localhost(:1455)?#127.0.0.1:${LPORT}#"
5.5 多段SSH(JumpHost経由)
~/.ssh/config での設定
Host golgi
HostName 192.168.1.100
ProxyJump bastion
User myuser
トンネル作成コマンド
ProxyJump を設定済みの場合、-J オプションは不要です:
ssh -fN -o ExitOnForwardFailure=yes \
-L 127.0.0.1:1455:127.0.0.1:1455 golgi
明示的に指定する場合:
ssh -fN -o ExitOnForwardFailure=yes \
-J bastion \
-L 127.0.0.1:1455:127.0.0.1:1455 golgi
5.6 バックグラウンドプロセスの罠
注意点
tmux/screen や autossh などで、背景に残ったトンネルが無自覚に生きていることがあります。
確認と終了
# トンネルプロセスを終了
pkill -f 'ssh -N -L .*1455:127\.0\.0\.1:1455' || true
# 親プロセスの確認(autossh等で自動復活していないか)
ps -o pid,ppid,cmd --forest -p <PID>
6. 代替フロー(高確率な別手段)
6.1 デバイスコード方式(推奨)
特徴
- ポートを一切使用しない
- 最も安定した方式
-
codexがサポートしている必要あり
手順
# 対応しているか確認
codex help login
# デバイスコード方式で実行(例)
codex login --device
- リモートでコマンド実行 → 端末にURLとコードが表示
- ローカルのブラウザでURLを開き、コードを入力・承認
- リモートの
codexが完了表示
6.2 トンネルなしの非常手段
手順
1. リモートで codex を起動
codex login
2. ローカルブラウザで認証
表示されたURLをローカルブラウザで開く
3. コールバックURLをコピー
ブラウザが最終的に開こうとするURL:
http://localhost:1455/auth/callback?code=...&state=...
を丸ごとコピー
4. リモートで curl を実行
curl -iL "http://127.0.0.1:1455/auth/callback?code=...&state=..."
6.3 固定別ポート運用
社内端末でポート1455が恒常的に埋まっている場合、固定で別ポートを使用する運用も有効です。
設定例
# ローカル側を固定で21455にする
LPORT=21455
ssh -fN -o ExitOnForwardFailure=yes \
-L 127.0.0.1:$LPORT:127.0.0.1:1455 golgi
URL変換スクリプト
# ブラウザで表示された失敗URLをコピー → 端末で変換
URL='http://localhost:1455/auth/callback?...'
LPORT=21455
echo "$URL" | sed -E "s#localhost(:1455)?#127.0.0.1:${LPORT}#"
変換後のURLをブラウザで開きます。
7. 疎通確認とデバッグ
ローカルでの確認
Linux:
ss -ltn 'sport = :1455' 2>/dev/null || true
macOS:
lsof -iTCP:1455 -sTCP:LISTEN -n -P 2>/dev/null || true
リモートでの確認
ss -ltn 'sport = :1455' || true
トンネル越し到達確認
ローカルから実行:
# リモートにcodexのHTTPサーバが立っていれば 200/302/404 等が返る
curl -i http://127.0.0.1:1455/ || true
ローカルで別ポートを使用している場合:
curl -i http://127.0.0.1:$LPORT/ || true
プロセス確認
自分のプロセスのみ確認:
pgrep -u "$USER" -af 'codex|ssh.*-L|ssh.*-R' || true
自分のプロセスのみ終了:
pkill -u "$USER" -f '^codex .*login' || true
pkill -u "$USER" -f 'ssh -N -L .*1455:127\.0\.0\.1:1455' || true
8. ワンライナー集
一撃ログイン(標準)
ターミナルA(ローカル):
ssh -fN -o ExitOnForwardFailure=yes \
-L 127.0.0.1:1455:127.0.0.1:1455 golgi
ターミナルB(リモート):
ssh golgi && codex login
ローカル別ポート版
LPORT=21455
ssh -fN -o ExitOnForwardFailure=yes \
-L 127.0.0.1:$LPORT:127.0.0.1:1455 golgi
# 以後、ブラウザの最終URL :1455 → :$LPORT に置換して開く
残骸掃除(ローカル)
( ss -ltn 'sport = :1455' 2>/dev/null || true ); \
( lsof -iTCP:1455 -sTCP:LISTEN -n -P 2>/dev/null || true ); \
( pkill -f 'ssh -N -L .*1455:127\.0\.0\.1:1455' || true ); \
( pkill -f '^codex .*login' || true )
残骸掃除(リモート、自分のプロセスのみ)
pkill -u "$USER" -f '^codex .*login' || true
pkill -u "$USER" -f 'ssh.* -R .*1455' || true
9. セキュリティ上の注意事項
バインドアドレスの制限
# ✅ 良い例(127.0.0.1に限定)
ssh -L 127.0.0.1:1455:127.0.0.1:1455 golgi
# ❌ 悪い例(0.0.0.0に晒される)
ssh -L 1455:127.0.0.1:1455 golgi
認可情報の取り扱い
- URLに含まれる
codeやstateパラメータは第三者と共有しない - 平文でログファイルや共有チャットに残さない
- ブラウザの履歴に残ることに注意
トンネルのクリーンアップ
作業後は必ずトンネルを閉じます:
pkill -f 'ssh -N -L .*1455:127\.0\.0\.1:1455' || true
使い回しを避けるため、成功直後に不要なトンネルは落とすことを推奨します。
10. エラーメッセージ早見表
| エラーメッセージ | 発生場所 | 原因 | 対処法 |
|---|---|---|---|
bind [127.0.0.1]:1455: Address already in use |
ローカル | ローカルの1455が占有されている | 5.1節参照 |
Port 127.0.0.1:1455 is already in use |
リモート | リモートの1455が占有されている | 5.2節参照 |
| 認証後も完了しない(無反応) | ブラウザ | コールバックがリモートに届いていない | 5.3節、5.4節参照 |
11. まとめ:sudo なしで安定運用するコツ
基本原理
ローカル→リモートのSSHローカルフォワードでOAuthコールバックを橋渡しする。
推奨アプローチ
-
まずは標準ポート(1455)を使用
- 最もシンプルで成功率が高い
-
失敗したらローカル別ポートに切り替え
- ローカル側のみポート番号を変更
- コールバックURLのポート番号を書き換えて開く
-
リモート側が埋まっている場合
- 自分のプロセスのみ掃除
- それでもダメなら
codexのポート変更機能を使用 - デバイスコード方式が利用可能ならそれを使う
sudo なし環境の制約
- 自分のプロセスしか kill できない
- 他ユーザーのプロセスがポートを占有している場合は調整が必要
- ランダム高番ポート活用とURL書き換え運用が高確度
補足事項
codex のバージョン依存
codex がポート変更やデバイスコードをサポートしているかはバージョンによります。
必ず以下を確認してください:
codex help login
# または公式ドキュメントを参照
URL書き換え運用の互換性
本記事で紹介したURL書き換え運用は、多くのCLI/OAuth実装で問題なく動作しますが、プロダクト固有の検証が厳しい場合は挙動が異なることがあります。
その際はデバイスコード方式が最も堅牢です。
おわりに
本記事では、sudo権限なしでヘッドレスサーバ上の codex login を成功させるための包括的なガイドを提供しました。
基本的なSSHトンネリングから、ポート競合時の回避策、デバッグ方法まで網羅していますので、環境に応じて適切な方法を選択してください。
不明点があれば、お使いの環境(OS、sshのバージョン、codexのバージョン)を添えてコメントいただければ幸いです。
Discussion