@kyanny's blog

My thoughts, my life. Views/opinions are my own.

SSH: ControlMaster, ControlPath, ControlPersist によってコネクションが永続化されていると ProxyCommand が無視される

SSH over HTTPS でプロキシを利用する場合の動作検証用に、ローカルで HTTP プロキシを立てて Connect with SSH through a proxy - Stack Overflow を試していたら、一向にうまくいかず苦戦した。

❯ ssh -o 'ProxyCommand nc -X connect -x localhost:8080 %h %p' -p 443 -T git@ssh.github.com

こんなやつ。なお nc は macOS の /usr/bin/nc である。

mitmproxy / mitmdump で HTTP プロキシを立てていると ssh 接続はエラーになってしまうとか(未解決)、proxy.py がデフォルトで IPv6 アドレスを bind するが nc はデフォルトで IPv4 アドレスを解決するためにプロキシサーバに接続できていなかったとか(解決済み)、他の要素も絡んでいて余計に苦労したが、最終的に表題の件にたどり着いた。

ControlMaster, ControlPath, ControlPersist については以下の記事が詳しい。要するに、ssh 接続を使い回すための設定が ControlMaster, ControlPath で、リモートホストからログアウトしたあともバックグラウンドで接続を維持するための設定が ControlPersist である。

今回ハマったのは、 ~/.ssh/config に、どこかからコピペしてきた設定が書いてあることに気付いていなかったのが敗因だった。

Host *
  ControlMaster auto
  ControlPath ~/.ssh/mux-%r@%h:%p
  ControlPersist yes  

ControlPersist yes が問題で、一度あるホストに ssh 接続すると、以後そのホストへの接続時には最初に確立したコネクションが(半永久的に)使いまわされる。ProxyCommand で HTTP プロキシを通すような ssh コマンドを発行しても、既存のコネクションの再利用が優先されるので、結果的に ProxyCommand が無視される。

-F /dev/null オプションで ssh config を読ませなくするか、 -o オプションでこれらのオプションの値を上書きしてやれば解決する。

❯ ssh -F /dev/null -o 'ProxyCommand nc -X connect -x localhost:8080 %h %p' -p 443 -T git@ssh.github.com

手元で試したところ、ControlMaster と ControlPersist を -o で上書きしても効果はなく、ControlPath を別のパスに上書きするのは効果があった。

# ダメ
❯ ssh -o 'ControlMaster no' -o 'ProxyCommand nc -X connect -x localhost:8080 %h %p' -p 443 -T git@ssh.github.com

# ダメ
❯ ssh -o 'ControlPersist no' -o 'ProxyCommand nc -X connect -x localhost:8080 %h %p' -p 443 -T git@ssh.github.com

# うまくいく
❯ ssh -o 'ControlPath /dev/null' -o 'ProxyCommand nc -X connect -x localhost:8080 %h %p' -p 443 -T git@ssh.github.com

なお、バックグラウンドで維持されている既存のコネクションを切断するには ssh -O オプションを使う。接続先のホスト・ポート・ユーザ名などを一致させる必要がある。

# コネクションが存在するか確認する
ssh -O check -p 443 -T git@ssh.github.com

# コネクションを切断する
ssh -O exit -p 443 -T git@ssh.github.com

proxy.py で動作検証用の HTTP プロキシサーバを立てる

pypi.org

Python 製の HTTP プロキシ。インストールすると proxy コマンドが使えるようになる。

venv 環境内へのインストール手順。

python3 -m venv venv
source venv/bin/activate
pip install proxy.py

注意点として、デフォルトで IPv6 のポートを listen する。IPv4 のポートは listen しない。curl のように IPv4/IPv6 をよしなに処理してくれる HTTP クライアントでは問題にならず、気付きづらいが、ssh の ProxyCommand + nc でプロキシを通すテストの検証時にはまった。

proxy.py -h

  --hostname HOSTNAME   Default: ::1. Server IP address.
  --port PORT           Default: 8899. Server port.
❯ proxy --port 8080
2021-07-18 02:35:29,204 - pid:74959 [I] load_plugins:334 - Loaded plugin proxy.http.proxy.HttpProxyPlugin
2021-07-18 02:35:29,205 - pid:74959 [I] listen:113 - Listening on ::1:8080
2021-07-18 02:35:29,228 - pid:74959 [I] start_workers:136 - Started 4 workers

curl に IPv4 アドレスのみを解決させる -4, --ipv4 オプションを指定すると、ローカルのプロキシサーバに接続できずエラーになることがわかる。

❯ curl -x localhost:8080 ifconfig.me/ua
curl/7.64.1

❯ curl -4 -x localhost:8080 ifconfig.me/ua
curl: (7) Failed to connect to localhost port 8080: Connection refused

❯ curl -6 -x localhost:8080 ifconfig.me/ua
curl/7.64.1

ProxyCommand を指定するとエラーになる。おそらく nc がデフォルトで IPv4 アドレスのみを解決するから。

❯ ssh -o ProxyCommand='nc -X connect -x localhost:8080 %h %p' -p 443 -T git@ssh.github.com
nc: Proxy error: "HTTP/1.1 200 Connection established"
kex_exchange_identification: Connection closed by remote host
❯ proxy --port 8080
2021-07-18 02:38:21,728 - pid:75222 [I] load_plugins:334 - Loaded plugin proxy.http.proxy.HttpProxyPlugin
2021-07-18 02:38:21,729 - pid:75222 [I] listen:113 - Listening on ::1:8080
2021-07-18 02:38:21,750 - pid:75222 [I] start_workers:136 - Started 4 workers
ConnectionResetError(54, 'Connection reset by peer')

proxy.py に IPv4 アドレスを bind すると解決する・・・はずなのだが、いま手元で試したらうまくいかない。昨日はうまくいったのに・・・。

❯ proxy --hostname 0.0.0.0 --port 8080

Evernote iOS の「ノートの編集保護」が便利

編集保護を有効にする – Evernote ヘルプ&参考情報

ノートを表示してる状態で、ダブルタップしないと編集モードに移行しないようにできる。ノートをスクロールする操作がシングルタップと解釈されて編集モードに入ってしまう、というささやかなストレスとおさらばできる。

そうそう、こういうのが欲しかったんだよずっと、言われるまで気づかなかったけど、みたいな感じ。地味だけど、使い勝手に配慮した良い機能だと思う。プロダクトにこういう改善が行われるのは、良い兆候だと思う。

iPhone Safari のコンテンツブロッカーを Ad バスターから 280blocker に変更

‎「280blocker : コンテンツブロッカー280」をApp Storeで

‎「Adバスター」をApp Storeで

たしか Safari にコンテンツブロッカー機能がついたときに(iOS 9 かららしい、2015 年 9 月リリース、、)たしかこの二つは有料コンテンツブロッカーアプリとして二強っぽくランキング上位で、たまたま Ad バスターのほうを選んだような記憶がうっすらある。

ウェブブラウジングはもっぱら Google アプリでやるようになり、 Safari の広告ブロック具合など気にもとめなくなっていたが、数年経ってかたや 280blocker は現在も最も売れてる有料アプリの一つで、かたや Ad バスターはすっかり不人気になってしまったようだ。

  • みずほシステム障害の調査報告書
    • データベースのインデックスファイルの容量が超過した、ってどういうこと?と思って先を読んだら、処理速度向上のためにメモリ内にインデックスファイルを配置する構成にしてたとのこと。それならたしかに容量不足は致命傷になりうる。
    • 前々職で年に一度の恒例深夜メンテの事前準備で計画を立ててたときのことや、当日にトラブルが発生してヒヤヒヤしながら急遽対応してなんとか乗り切ったときのことを思い出した。
  • 知識整理のためのノートのとり方
  • 思考力を磨くためにライティングを始めよう
    • 「安心してなんでも書ける場所」
  • ブログを書いている私がブログを書かなくて良いと皆に伝えたい理由
    • おれは逆に、自分しか読まない場に雑に書いた文章は質が低過ぎて自分が読み返しても役に立たない、という経験を経て、ブログをそういう場として使うのが板についたなぁ。ストック型のメモツールをあとで整理する習慣がまったく定着しない、というのも一因だが。
  • Readwise
    • iBooks のハイライトも抽出できるのは良さそう。SQLite 直叩きしてるとかなんだっけ?
    • これは別途書きたいが、やっぱり Kindle より iBooks で読む方がおれには合ってるので、iPad mini の新しいのが出たら買い増したい。