それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

kvm-qemu/virsh でクローンの作成

KVM/QEMU仮想マシンを別のマシンへコピーしたい

マイグレーションだと「移転・移動」してしまうので、「コピーして移動したい」

全体の流れ

という2段階でやろうと思う。

コピー/クローンの作成について

クローン作成コマンド

クローン作成には、virt-clone を使うと楽

virt-clone --original AdguardHome --name AdguardHome02 --auto-clone

virt-clone の準備

virt-clone コマンドは、virsh とは別のパッケージなため未導入の場合がある。

sudo apt install virtinst

未停止の場合

clone にはシャットダウンが要求されるためシャットダウンしておく

virsh shutdown xxx

シャットダウンしてないとエラーになる。

$ virt-clone --original AdguardHome --name AdguardHome02 --auto-clone
ERROR    Domain with devices to clone must be paused or shutoff.

clone 時の注意

クローンは、丸コピーとは違ってクローン作成です。同じものを「起動できる状態」にしてくれる

何を言ってるのかというと、MACアドレスは変わる。MACアドレスが変わるということは ubuntu など cloud-init でnetplanしているとMACが変わってIP割当が変わる。

コピーの確認

シャットダウン状態でコピーされるので、

virsh list --all 

または、virt-managerから

必要な時間

qcow2 などボリュームをコピーするので、SSDの書き込み速度とqcow2 の容量に依存する。8GB で5分程度かかった

できないこと

clone 時に、SSH経由でCloneができればよかったんだけど

一気呵成にできない。

virt-clone \
   --original qemu:///system/FromVM  \
   --name qemu+ssh://takuya@another/system/ToVM` 

のようなURI+パスで仮想マシン指定でができれば強かったんだけど。できないですね。

マイグレーション

移転の方法は、2つある。

今回は、rsync 移転にする。

手作業転送とライブマイグレーション

ライブマイグレーション機能はストレージを共有しているホスト間で仮想マシンを移動することが前提だったと思う。

ライブマイグレーションは、「ストレージを共有しているホスト間でメモリや書き込みを転送しつつ」うまい感じに起動したままホストを移動する機能なので、オフラインで転送するようなコピーには全く向かない。

設定を取り出して転送

dumpxml で取り出して ssh 経由でサクッと転送。

virsh dumpxml DomA | ssh srv02  dd of=DomA.xml

設定をロード

virsh define DomA.xml

XML ファイル名がドメイン名(仮想ゲスト名)になる。

ストレージを転送

ストレージを転送

rsync -avz --progress --partial DomA-clone.qcow2 takuya@srv02:~

起動する

最後に起動しして終了

ssh srv02 virsh start DomA

sslh で 443 ポートのUDP/TCP を振り分ける(openvpn/udpとwireguard/ udp を使う)

443 ポートを活用する

443 ポートで https / ssh / openvpn / wireguard を全部の待受ができたらいいなと思った。

フリーWiFiが443 / 53 /80 以外のポートを使わせてくれない事が非常に多い。公衆無線LANでポート監視やるのは流石にまずいと思うんだけど。実際やられてるんだから仕方ない。あまり監視をしちゃうと、某国みたいにVPNのイタチごっこが始まるので本当に勘弁してほしい。

よろしい、ならば抵抗してみよう。

sslh 最新版が UDP に対応

sslh の最新版(1.22)がUDPに対応していた

最新版コードを持ってきて ビルドする

git clone https://github.com/yrutschle/sslh.git
cd sslh
make 

UDP にも対応させる

UDPはfork でパケットを流せないらしいので、select コールで使うらしい

./sslh-select -F /etc/sslh.conf

/etc/sslh.conf

verbose: 0;
foreground: true;
inetd: false;
numeric: false;
transparent: true;
timeout: 1;
user: "sslh";
pidfile: "/var/run/sslh/sslh-select.pid";



# Change hostname with your external address name.
listen:
(
    {
      host: "192.168.1.222";
      is_udp: false;
      port: "443";
    },
    {
      host: "192.168.1.222";
      is_udp: true;
      port: "443";
    }
);

protocols:
(
    { name: "ssh";
          service: "ssh";
          host: "127.0.0.1";
          port: "8022";
          is_udp: false;
          fork: true;
    },
    { name: "openvpn"; host: "localhost"; port: "1194"; },
    { name: "tls";
        host: "127.0.0.1";
        port: "443";
        alpn_protocols: [ "h2", "http/1.1", "spdy/1", "spdy/2", "spdy/3" ];
        is_udp: false;
        fork: true;
        log_level: 0;
        tfo_ok: true },
    # wireguard
    { name: "regex";
        host: "192.168.1.21";
        log_level: 2;
        is_udp: true;
        fork: false;
        port: "11945";
        regex_patterns: [ "\x01\x00\x00\x00" ];
      }
);

wiregaurd は regex

regex_patterns: [ "\x01\x00\x00\x00" ];

これで、wireguardのパケットを識別できるらしい。識別できるってことはブロックされちゃうってことでもあるけど、そこまで流行ってないwireguardだ、これでしばらく通信できそうな気はする。

UDPTCP を同時リッスンするポイント

リッスンをudp/tcp と2つかくことが設定ポイントになる。

listen:
(
    {
      host: "192.168.1.222";
      is_udp: false;
      port: "443";
    },
    {
      host: "192.168.1.222";
      is_udp: true;
      port: "443";
    }
);

UDP サポートについて

UDPは本家のマニュアルに記載が追加されていた。

https://github.com/yrutschle/sslh/blob/master/doc/config.md#udp

UDPがサポートされたのは2021/08 の 1.22 リリース前後のようです。

443 ポートで隠す?

暗号化通信をしていたとしても、SNIでドメイン名は丸見えだし、IPアドレスは隠せない。

そうなると、必然的にVPNを使うことになる。TLSでトンネルを掘るとか、HTTPSで Proxy Connectをすることになる。

となると、TLS通信を監視する必要が出てくる。そのためにカスペルスキーのようなソフトでTLSの監視を行うことになる。

となると、すべての通信はLocalhost経由になり、監視されることになり、証明書を信用できなくなる。本当にそれは望んだ自由な社会なのだろうか。

IPv6でIP範囲指定が難しくなり、すべての端末がグローバルアドレスになりつつある現代でローカルネットやPC管理を前提としたセキュリティは崩壊寸前かもしれない。

IPv6 での rsync のIPアドレス指定

v6 の場合、rsyncIPアドレス指定はめんどくさい

rsync -avz sample.xml 'takuya@[2001::1:34]:~'

ポイント

v6 アドレスは[ ] で 囲む

rsync -avz sample.xml takuya@[2001::1:34]:~

[ ]bash に解釈されるので、クォートする

rsync -avz sample.xml 'takuya@[2001::1:34]:~'

めんどくさい。。。

Explorer で Sambaを見るとファイル名がチルダの文字になる

チルダの文字なる

Samba経由でLinuxのファイルを見ると、英数文字で5文字、チルダ、英数字のファイルになる。

再現方法

次のようなファイルを作ると、再現できる。

touch aaaa?

原因

Linuxのファイル文字とWindowsの使用禁止文字が異なるためらしい。

対応

Windowsで禁止されているファイル文字を使わないようにする

化ける文字

次の文字が化ける。

> : " / \ | ? * < 

クエスチョンマーク

! は問題ないが、?マークで化けるので ? を何とかする

たとえば、!?と連続して使われる場合、!?の代わりに絵文字の⁉を使うとか。

rename 's/!\?/⁉/g'

単体の? は全角に変換しておけばいいでしょう。

rename 's/\?/?/g'

!? のように使われると全角にすると読みにくくなる。なので、⁉の絵文字を使った。

よくあるパターン

* アスタリスクが化ける。

アスタリスクなんて使わないと思うんだけど、コマンド入力ミスでファイルが作られてしまったりする。

rename 's/\*/\*/g'

... ピリオドの三点リーダ

ファイル名に ... があると化ける。三点リーダーにする

再現方法

mkdir aa....

対症方法

rename 's/.\.\.\/\…/g'

その他に、ピリオドが原因で起きるもの

調べてみると、末尾に、3つ以上で表示がおかしくなりました。

touch い.  # 表示可
touch ああ.. # 表示可
touch ううう...      # 起きる
touch ええええ....  # 起きる
touch お...おおお  # 表示可

bbb \aaa 末尾の空白

ファイル名の末尾に空白があると化けます。

再現方法

touch 'Xaaa '
rename 's/ +$//' 

末尾の空白は、1つ以上でダメです。複数スペースもダメです。

全角のスペースは使えます。

"!?"と続けた場合

これは!?.txt # 化ける
これは⁉.txt # 化けない

このことから ! ? が続けてて使われている場合は、 のマルチバイト文字に置換したほうがいいかと思います。

Sambaのファイル名に注意する必要がある。

Sambaでファイルを扱うとどうしてもこういう細かい部分で苦労する。

ファイル名に...!? なんか使うか?と思うんだけど調べたら結構ありました。

作りかけのファイルだったり、ファイル名でコミュニケーションをしている使い方をしてる企業様や、WEBページをHTML保存したときに発生しますね。

スクレーパー掛けたときにファイル名をWebページのtitle にしている場合なんかも起きますね。

参考資料

SMB経由でアクセスすると、ファイルまたはフォルダの名前が12HWA0〜8のように表示されます。どうすればいいですか? - Synology ナレッジセンター

ChromeのHSTS( http → httpsリダイレクト)の記憶を消す

chrome のHSTSが邪魔

443/ 80 の https のリダイレクトを設定しているとブラウザが学習したリダイレクトが邪魔になる。

HSTSを学習するなとは言わないが、クライアント側で完結されるのは邪魔である。リダイレクトの期限をつけて制御したいところである。 しかし学習ししてしまったら消すしか無い。

hsts の設定画面から消す。

chrome://net-internals/#hsts

f:id:takuya_1st:20220411154741p:plain

chrome履歴画面の検索結果を全部クリックする(shadowroot経由のDOM JS)

特定の検索結果の履歴を全部消したい

Googleの閲覧履歴を検索して消したい。消したいけど、「すべて消す」が無い。

検索結果をすべて選択がない。

すべて選択がないので、ちまちま消す必要があるだるすぎる。

Devtoolでまとめてチェックしたい

ボタンがないならDevtoolでアクセスだ。ぜんぶチェックしてやる。

React?Angular?かなshadowRootがある。

id=history-app に下に shadow-root(open) がある。このままでは css セレクタでアクセスできない。

ShadowRootへアクセスするには

エレメントに shadowRootが生えている。これを使う。コンテキストを切り替えて要素をたどってしまえば、強引にクリックできる。この手はSeleniumなどでも使える。まぁ通常はスクリーンの指定領域にClickイベントを送るだろうけど、直接要素を触ることも出る。

const e = document.querySelector('#some-app')
SomeRoot = e.shadowRoot
SomeRoot.querySelector(' div ')
div.click()

履歴の検索結果をぜんぶクリックする。

Chrome のコンソールを使ってつぎのようにする。

appRoot = $('#history-app').shadowRoot
historyRoot = appRoot.querySelector('#main-container #content #tabs-container history-list').shadowRoot
for ( e of historyRoot.querySelectorAll('iron-list history-item[tabindex="-1"]') ){
  e.shadowRoot.querySelector('cr-checkbox#checkbox.no-label').click()
}

コンソールに貼り付けて

実行

一気に選択できた。

仕組み

冒頭で書いた通り、shadowRootを辿っていく。

historyRoot.querySelectorAll('iron-list history-item[tabindex="-1"]') でアイテムを探している

アイテムをクリックする。

#shadowRootに入っていく
appRoot = $('#history-app').shadowRoot
historyRoot = appRoot.querySelector('#main-container #content #tabs-container history-list').shadowRoot
itemRoot =  historyRoot.querySelectorAll('iron-list history-item')[10].shadowRoot # index=10 
# 該当のアイテムをクリック
itemRoot.querySelector('cr-checkbox#checkbox.no-label').click()

表示中のアイテムを全部クリックする。

appRoot = $('#history-app').shadowRoot
historyRoot = appRoot.querySelector('#main-container #content #tabs-container history-list').shadowRoot
for ( e of historyRoot.querySelectorAll('iron-list history-item[tabindex="-1"]') ){
  e.shadowRoot.querySelector('cr-checkbox#checkbox.no-label').click()
}

削除ボタンを押す場合

appRoot = $('#history-app').shadowRoot
toolbar = appRoot.querySelector('#toolbar').shadowRoot
selectionOverlay = toolbar.querySelector('cr-toolbar-selection-overlay').shadowRoot
selectionOverlay.querySelector("#delete").click()

ダイアログボックスを押す場合

appRoot = $('#history-app').shadowRoot
historyRoot = appRoot.querySelector('#content history-list').shadowRoot
historyRoot.querySelector('cr-dialog cr-button:nth-child(2)').click()

表示中のアイテムをすべて削除(コピペ用)

(async function delete_displayed(){
  async function select_displayed(){
    appRoot = $('#history-app').shadowRoot
    historyRoot = appRoot.querySelector('#main-container #content #tabs-container history-list').shadowRoot
    for ( e of historyRoot.querySelectorAll('iron-list history-item[tabindex="-1"]') ){
      e.shadowRoot.querySelector('cr-checkbox#checkbox.no-label').click()
    }  
  }
  async function click_delete(){
    appRoot = $('#history-app').shadowRoot
    toolbar = appRoot.querySelector('#toolbar').shadowRoot
    selectionOverlay = toolbar.querySelector('cr-toolbar-selection-overlay').shadowRoot
    selectionOverlay.querySelector("#delete").click()
  
  }

  async function click_confirm(){
    appRoot = $('#history-app').shadowRoot
    historyRoot = appRoot.querySelector('#content history-list').shadowRoot
    historyRoot.querySelector('cr-dialog cr-button:nth-child(2)').click()  
  }
  await select_displayed();
  await click_delete();
  await click_confirm();
  
})();

インフィニット・ロード

無限ロードなので、スクロールバーが最下部まで行かないと、次がロードされないんので、全部選択したつもりでも残ることはある。

非表示もクリックしてしまう。

ざっと書いただけなので、非表示になってるAppもクリックしてしまうので、エラーになることがある。

async をする必要がある。

shadowrootへのアクセス方法

Shadowrootへの強引なアクセス方法を覚えておくと、一番低レイヤなので、Seleniumなどでスクレーピングするときに使えそう。

Chromeは履歴を消させたくない?

履歴の絞り込みで一気に消したいときなのに、すべて選択がない。

履歴の絞り込みができるのに、マッチ結果をすべて削除が機能にない。

マッチ結果を削除がないのは致命的な欠陥である、Vivaldiなど別のブラウザでは簡単にできるのだが。

ほかブラウザでは可能なのに、Chrome だけが履歴削除について機能を制限している。

これは、アクセス履歴を「何らかの広告」にいや「個人の追跡」に使っている傍証ではないかと想像する。やはり、Googleは組織として邪悪になってるのかなと信用を置けない。

2022/09/10

HTMLの構造が若干変わってたので記述内容を変更

ブラウザでSSHを接続する webssh

ブラウザのHTMLでSSHする

webssh を使うと、ブラウザの中でSSH接続ができる。

ブラウザの中でSSHをサービスとして提供できるようになる。

使用例

f:id:takuya_1st:20220408161035p:plain

接続概要

ブラウザはWebSocketを喋る。

ブラウザ--[websocket]-->webssh

リモートのサーバからは、SSHで出ていく。

webssh -- [ssh] -- > ssh server

接続の概要を見たらわかる通り、Linuxサーバーを踏み台にしてSSHするのと同等である。

インストール

pipenv を使ってインストールすることにする。

mkdir webssh
pipenv 
pipenv shell 
pip install webssh 
webssh

起動

ポート指定・アドレスしてリッスンする。

## または 全部指定
wssh 
## 指定
wssh --port=5571 --address=127.0.0.1

使い所

SSHクライアントのインストールが制限されているような環境でも接続が可能になる。

ただ、この目的であれば、Chrome拡張機能で直接SSHを喋るのでChrome拡張機能が望ましいと思う。

踏み台を用意するのがめんどくさい、SSLで抜けてSSHを接続したいときに使えそうですよね。

nginx にインストール

ssl はnginx で管理したほうが楽

nignx の設定例

server {

  listen 127.0.0.1:443 ;
  server_name _;
  server_tokens off;
  location / {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Real-PORT $remote_port;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Host $http_host;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_connect_timeout 300;
    # Default is HTTP/1, keepalive is only enabled in HTTP/1.1
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    chunked_transfer_encoding off;
    proxy_pass http://127.0.0.1:5571/;
  }

}

nginx のサブディレクト

複数の接続を切り替えて使うなら、サブディレクトリを使うほうが楽だと思う。

location / { location /terminal/ { に切り替える。

server {

  listen 127.0.0.1:443 ;
  server_name _;
  server_tokens off;


  location /terminal/ {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Real-PORT $remote_port;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Host $http_host;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_connect_timeout 300;
    # Default is HTTP/1, keepalive is only enabled in HTTP/1.1
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    chunked_transfer_encoding off;
    proxy_pass http://127.0.0.1:5571/;
  }

}

プロキシ先にも注意

proxy_pass http://127.0.0.1:5571/; にすることがポイント

proxy_pass http://127.0.0.1:5571; だとディレクトリも含めてバックエンドに送られる。

開いた瞬間に接続

開いた瞬間に接続させることもできる。

https://127.0.0.1/terminal/?hostname=192.168.1.1&username=takuya&password=XXX

参考資料

https://github.com/huashengdun/webssh

linxuのターミナルで使えるグラフィカルかリソース監視 gtop

f:id:takuya_1st:20220408155628p:plain

インストール

cd npm i gtop 

起動

npx gtop

モニタリング

フォントと縦横比をうまく調整すればきれいに表示ができる

プロセス一覧

プロセス一覧は、c/m/p でCPU利用率・メモリ占有量・PIDの切り替えができる。mm/ccと二会押せば逆順になる。

終了

q / esc /ctrl-c で終了

気にったらglobalへ

global でのインストール

npm -i -g  gtop
gtop

google chrome 拡張機能でSSH接続を行う。

Chrome 拡張でSSHプロトコルを喋る

Chromeの拡張でSSHを使えるウインドウを開けるものがある。

f:id:takuya_1st:20220406123439p:plain

インストールして動かしたところ

SSHコマンドでリモートに接続ができます。 f:id:takuya_1st:20220411215932p:plain

各種設定

サウンドや文字の設定、あと鍵の設定もできる。 f:id:takuya_1st:20220406123425p:plain

ブラウザ内部で動くSSHクライアント

ChromeBookとかで使うものだし、ちゃんと動くんだよね。

SSHアプリケーションがインストールできない。とかいう極限状態でも使えそうですよね。

通信は、Chromeブラウザから直接SSHプロトコルで通信します。

sslh で ssh / httpsを同時443で使い、しかも接続元IPを維持する

sslh で443 をポートを共有する

sslh を使うと443 ポートを再利用することができる。

どんなときに使うのか。

公衆無線LANや公民館のLANなど。共用部分の共用インターネット接続でで、平然と443以外のポートを閉じてるWiFiがたくさん存在する。

社内LANと勘違いしてるんじゃないかと思うんだけど。まぁしかたない。53 ポート(DNS)か443ポート(https)を使うしか無い

DNSVPN前に試したし、sslhも以前試したnginx を使う方法も試した。

あれから数年たち、443 ポート以外の利用が厳しい環境が増加しているので、実運用に投入することにした。

全体の接続

sslh を使うと、443ポートで複数のプロトコルで転送ができる。

nginx で作った場合に比べて随分とシンプルになる。

f:id:takuya_1st:20220406010014p:plain

インストール

sudo apt install sslh

設定

debian/ubuntu の場合は、/etc/default/sslh が設定ファイルになる。

transparent を使うのが今回の設定の肝になる。

設定例

# Default options for sslh initscript
# sourced by /etc/init.d/sslh

# binary to use: forked (sslh) or single-thread (sslh-select) version
# systemd users: don't forget to modify /lib/systemd/system/sslh.service
DAEMON=/usr/sbin/sslh

DAEMON_OPTS="--transparent --user sslh --listen 192.168.2.1:443 --ssh 127.0.0.1:8022 --tls 127.0.0.1:443 --pidfile /var/run/sslh/sslh.pid"

細かい設定は、github にあった

有効化と起動

sudo systemctl enable  sslh.service
sudo systemctl start sslh.service

設定したら起動して接続テストする。

既存のnginx443 はどうするのか

既存のnginx の443 は、127.0.0.1:443 へ退避するか、eth0 に複数のIPアドレスを割り当ててそちらを使うようにする。

nginx で ip 指定でリッスンする
server {
   listen 127.0.0.1:443 ssl https;
}
apache で ip 指定でリッスンする
<Virtuahost>
<IfModule ssl_module>
    Listen 127.0.0.1:443
</IfModule>

複数のIPを1つのNICに割り当て

 ip addr add ...

または

iface br0:0 inet static
  address 192.168.101.5/24
  gateway 192.168.101.1
iface br0:1 inet static
    address 192.168.101.9/24

複数割当については過去記事に→debianで br0 にIPアドレスを2つ割り当てる。

transparent の問題

transparent はiptablesの設定とバッティングしたり、初期設定のままでは動くが、既存設定と競合して動かないことがあるので十分に注意する。

対策はgitレポジトリに掲載されていた→ Transparent Proxy

問題点 の解決:接続元IPが解決

--transparent を使うことで、ソースIPはそのまま通信ができる。

nginx や apachessh もちゃんと接続元IPは維持されて接続される。

問題点 プロトコル

プロトコル自体をいじってないので、プロトコルを見てロックされることがある。SSLでないと通さないとかはよくあると思う。

その場合HTTPS_PROXYでCONNECTでSSLしてプロトコルを見せない方法もある。

 ssh USER@FINAL_DEST -o "ProxyCommand=nc -X connect -x PROXYHOST:PROXYPORT %h %p"

この場合。ProxyCommand nc -X connect -x proxy.example.com:8080 %h %pの接続が最初に走るので、HTTPSのCONNECTになる。

まぁHTTPSのCONNECTも制限されてたらどうしようもないんだけど。

PROXY CONNECT を使ってHTTPSを転送するときに仕掛けることは可能だと思う。

その場合は、Softetherが最終手段になるかもしれない。

その他の方法。

22番が使えずに、443番でなんとか頑張る方法は他にもある。

ブラウザ中でリモートデスクトップを起動したり、仮想マシンに接続したり、ブラウザでターミナルに接続・・・etc そのような方法で443ポートを通して別サーバーのシェルにログインして作業は工夫すれば可能である。

  • sslh
  • nginx ssl_preread
  • shell in a box
  • portainer
  • cockpit
  • vscode on web

など、代替手段が山程存在する。

最近だと、vs code をサーバーにインストしておけば、好きな場所からHTTPS経由でVs Code使って便利に使える。

接続制限する方も大変だろう。ipv6で接続制限も大変だ。v6時代になっているのに、ポート制限はなくならないってのはしんどいねぇ。

OpenVPNやWireguardにSoftetherなどVPN技術もSSL-VPNも進歩してるし、大変だけど。みんな裏口つくるの楽しそうですよね

コロナ禍が落ち着いて、近所のイオンのフードコードで作業しようと思って無線LANのポートが制限かかってて、イラッとなって、コロナ禍が落ち着いたのでホテルに宿泊してインターネットを楽しんでいたら443ポート以外が閉じられいた。これらを回避するために、sslhを使うことにした。

テザリングでも、携帯電話キャリアによっては443以外のポートのシェービングや優先度を落としたり、切断したりしますからね。契約書類に書いてないことをされるのは本当に怖い。

nginxで443ポートにssh/httpsを共有する

nginx で httpsssh をリッスンする

nginx には $ssl_preread_protocol というStream設定が用意されている。これを使うとTLSプロトコルごとに、プロキシ先を変えることができる

ただし443 はストリームが使うので、リッスン先をいい感じに帰る必要がある。

全体の接続

全体の接続は、次のようになる。 f:id:takuya_1st:20220406000824p:plain

nginx stream は sshd / https に直接接続しない。

sshd のリッスンポートに直接接続すると、プロトコル不一致が起きて接続できないので、server{ .. } を経由してる。

https に直接接続できるといえばできるが、プロキシプロトコルを設定しやすくするため、server{ .. } を経由してある

nginx の設定は次の通り

stream 設定を作る。 ssl_preread_protocol で TLSはwebへ、それ以外は ssh へ流すようにする。

# vim: ft=nginx ts=2 sw=2 sts=2

stream {
  upstream ssh {
    server 127.0.0.1:8023;
  }
  upstream web {
    server 127.0.0.1:8443;
  }

  map $ssl_preread_protocol $upstream {
    default ssh;
    "TLSv1.2" web;
    "TLSv1.3" web;
  }

  # SSH and SSL on the same port
  server {
    listen 192.168.2.1:443 so_keepalive=on;
    proxy_pass $upstream;
    ssl_preread on;
    proxy_protocol on;
  }
  server  {
    listen 127.0.0.1:8443 proxy_protocol;
    proxy_pass 127.0.0.1:443;
  }
  server {
    listen 127.0.0.1:8023 proxy_protocol;
    proxy_pass 127.0.0.1:8022;
  }
}

もし、リッスンポート8443 を使わない場合

使わない場合は、

#  server  {
#    listen 127.0.0.1:8443 proxy_protocol;
#    proxy_pass 127.0.0.1:443;
#  }

通常のホスト名のところに、書く

server {

  server_name  example.com;
  listen 127.0.0.1:443 ssl http2 proxy_protocol;
  # 略
}

nginx のメリットは数多のプロトコルを調べられることだろう。 公式ドキュメントを見ると

次にように他プロトコルを捌く例が出てくる

map $ssl_preread_alpn_protocols $proxy {
    ~\bh2\b           127.0.0.1:8001;
    ~\bhttp/1.1\b     127.0.0.1:8002;
    ~\bxmpp-client\b  127.0.0.1:8003;
}

ALPN( Application-Layer Protocol Negotiation) で使われるアプリ名であればいくつか使えるようである。https://datatracker.ietf.org/doc/html/rfc7301

ALPNについては、wikipedia-enを読めばいいかも

nginx の通常設定を書き換える

listen 443 http2 ssl ; を書いていると、すべてのインターフェイスでnginxがリッスンしてしまう。

このままでは、stream の分割用に、443 ポートを使えなくなる。そのためリッスンポートを変えておく。

次のように、localhost だけをリッスンするしておくといい。

listen 127.0.0.1:443 ssl http2;
##  または
listen 127.0.0.1:443 ssl http2 proxy_protocol;

再起動

設定をチェックして再起動する。

nginx -t
systemctl restart nginx 

接続を試す。

ssh 192.168.2.1 -p 443 
curl -k https://192.168.2.1/

問題 接続元のIPアドレス

443 ポートはプロキシしているのだから、接続元が127.0.0.1 になる。

これが、いろいろな問題を引き起こす。

問題点1:接続ログ

アクセスログがすべて127.0.0.1 になる。

「プロキシ」をしているのだから、当然です。すべての接続元が127.0.0.1 になってしまいます。

nginx の設定で、stream ログを書けばある程度解決します。

stream {

  log ...
}

問題点2:接続元IPで制限ができない。

sshdIPアドレスで指定する設定がある。

Martch address 192.168.2.1 
  passwordAuthentication yes

このようなIPを使うものは、使えなくなる。

代わりに、ポート番号を使う。

8022 が外向き、22 がローカルとすれば、次のように書くことができる。

Martch LocalPort 22
  passwordAuthentication yes
Martch LocalPort 8022
  passwordAuthentication no

接続元が問題になる。

様々な点において、接続元IPが使えなくなるのが、問題を引き起こす。

ほかにも fail2ban や iptables recent など問題が多くなるので、実運用に使うには至らないと思われます。

代替案

sslh の --transparent がこの問題を接続元IP問題を解決してくれる。

sedコマンドが、シンボリックリンク考慮しないが、gnu sedはなんとかなる

sedコマンドで置換すると、symlink が壊れる。

sed -i でファイルを置換するときのリンクファイルの扱い。

実験

echo xxx  > a
ln -sr a b
sed -i "s|xxx|yyy|"  a b    # bは通常ファイルになる。

sed を使うときには絶対に知っておかなくてはいけない。

シンボリックリンクは破壊される。

b はシンボリックリンクだったが、sed -i すると、置換されて「通常ファイルになる。」

takuya@:~$ ls -l a b
-rw-rw-r-- 1 takuya takuya 4  4月  5 20:12 a
-rw-rw-r-- 1 takuya takuya 4  4月  5 20:12 b

対策

GNU sedの場合、次のようにするといい。

sed --follow-symlinks
echo xxx  > a
ln -sr a b
sed  --follow-symlinks -i "s|xxx|yyy|"  a b    # bは維持される。

結果

ちゃんとシンボリックリンクが維持される。

takuya@:~$ ls -l a b
-rw-rw-r-- 1 takuya takuya 4  4月  5 20:18 a
lrwxrwxrwx 1 takuya takuya 1  4月  5 20:17 b -> a

POSIX/bsdの場合

対策はわからない。作業前に自分で注意するしかすべが無い。ってかいたけど、-i を使ってた上でやり方はわからないってだけす。

cat FILE  | sed '/xxx/yyy/' > FILE

cat して sed するとか、やりようはあります。ただ、、、FILE名を2回もかくのだるいよね。

dockerfileで注意

dockerfile などでは、sedが多様されますが、リンクを壊さないように十分に注意

gnu コマンドは便利

今回の記事のような理由で、gnu sedが無いととても不便です。それでもPOSIX互換にこだわりますか?

POSIX至上主義は捨ててGNU至上主義になればいいんじゃないかな。

2022-04-07 追記

「完璧より、完成を目指す」っていう考え方も、また大事だと思う

windows のcmd.exe をutf-8にするchcp と zip ファイルの絵文字・文字化け

windows のcp932の厄介な問題

windowsはファイル名をchp932で扱います。内部的にはUnicodeだけど、アプリケーションにcp932 で渡しています。

そのため、Windowsでは文字コードが混在してるのに正しく表示されるという妙な状態が発生します。

chcp コマンドで文字コード設定

chcp を使えば cmd.exe の文字コードを設定することができます。

コマンド・プロンプトをUTF-8にする

chcp 65001

コマンド・プロンプトでshift-jis(932)にする。

chcp 932

utf-8 の絵文字を使って確認

絵文字を使って、確認してみます。

絵文字入りのファイル作成

WSLはUTF8なので、WSLから作ります。

f:id:takuya_1st:20220405151641p:plain

通常のcmd.exe で確認

f:id:takuya_1st:20220405151540p:plain

chcp 65001 をして確認

f:id:takuya_1st:20220405151515p:plain

コードページを変更すると、文字コードを切り替えて扱えることがわかります。

ただし、このサンプルは、Windows Terminal というUTF8対応のターミナルソフトを使っているので表示されます。cmd.exeの通常ウインドウであれば、chcp を実施した上で、ウインドウの設定をする必要があります。

永続化されない

chcp で切り替えた場合、文字コードは永続化されません。

powershell

chcp はpowershell では使えませんでした。やり方はあると思うけど。 f:id:takuya_1st:20220405151936p:plain

エクスプローラ

私のエクスプローラーでは、プレビュー・ペインでUTF8の絵文字が正しく扱えました。

f:id:takuya_1st:20220405152230p:plain

systeminfo | findstr /B /C:"OS Name" /C:"OS Version"
OS Name:                   Microsoft Windows 10 Pro
OS Version:                10.0.19044 N/A Build 19044

ファイル名について

f:id:takuya_1st:20220405152733p:plain

f:id:takuya_1st:20220405152757p:plain

f:id:takuya_1st:20220405153510p:plain

cmd.exe の通常ウインドウは駄目です。

動くのだけど、表示されません。cmd.exeウインドウ設定を変える必要が f:id:takuya_1st:20220405153149p:plain

zip ファイル化

windowsの標準zip 機能でZipファイルを作成しようとすると。

f:id:takuya_1st:20220405153924p:plain

zip できないフィアル名が含まれているとエラーになってzip が作成できません。

f:id:takuya_1st:20220405153621p:plain

wsl からzip 化

WindowsからZip作成できないので、代用でwsl のzip コマンドで作成します。

f:id:takuya_1st:20220405154006p:plain

f:id:takuya_1st:20220405154031p:plain

エクスプローラーでzip を見る

Windowsの標準のZip閲覧機能、エクスプローラーでZipの中身を見ると・・・・

絵文字も正しく扱えました。

f:id:takuya_1st:20220405154230p:plain

Windowsの定番アイテム

Lhaplus を使うと、化けます。

f:id:takuya_1st:20220405154557p:plain

なんで、化けるのかなと思ってたら、作業用PCには、Lhaplus が初期導入されていて、そいつが化ける犯人だった。

定番ソフトウェアといえども、長期間メンテナンスされてないソフトウェアはもう役割を終えていますね。使わないようにして欲しい。気づかずに導入されていて、なんで文字化けしたのか悩んだ。

まとめ 文字化けが起きる箇所

入力時に文字化けは起きません。

アプリケーションがCp932前提の場合→化ける

例 Lhaplus ・cmd.exeのウインドウ など、アプリケーションに文字が渡ったときに化けます。

Windowsは絵文字も扱える。 エクスプローラーは対応している。

cmd.exe は chcp で文字コードを返らえるが、ウインドウが対応してないことがある。

Windows Terminal は最強。WSL最強

WindowsエクスプローラーからZipファイル作成するとCP932になる。

WindowsエクスプローラーからZipファイルを閲覧するとUTF-8に対応している。

Zipファイルは混乱の原因です。Zipファイルもうやめましょう。

どうしても使う必要がある時はwsl のzip コマンド、winrar や 7-zip を使いましょう。

いまのところ、化けるのは、「ファイル名」だけです。おもしろファイル名でファイルを整理するのもできる限り控えましょう。

補足

Windowsエクスプローラーで文字化けが起きないためには、条件があるようです。

[Windows エクスプローラー、7-Zip でファイル名を UTF-8 エンコードした ZIP ファイルを文字化けせずに解凍するためには ZIP ファイル内で EFS が有効にされている必要がある?] (https://qiita.com/Teloo/items/9cfd3b39a2a5cebf61b8)

vscode で WSLの ruby版 を使う。

vscode で WSLの ruby を使う。

vs code をちょっと使おうと思うと、めっちゃめんどくさいのが wsl 経由でコマンドを使うことですね。

vscode のWSL環境内の実行に慣れていれば、大丈夫なはず。

通常であれば、VS CodeをWSL内部環境で起動して終わり。

今回は、敢えて通常のVS CodeWindowsワールド)からWSLのRubyを叩くことを通して、裏世界(WSL)とうまく連携ととっていく。

VS Codeが環境別の起動をしないようにして通常のWindowsのエディタと同じようにWSLを使う設定を模索してみる。

最初に知っていおく WSLワールドとcmd.exe ワールド

WSLとWindows異世界を相互に使う方法を知っておく。

Visual Studio CodeWindows側にあるので、Windowsのパスで実行される。rubyphppython などは、 windows側か、wsl 側にインストールする。

ruby などコマンドパス設定をすれば動くはずなんだけど。

リモート起動しなくても、rubypython の補完などなら、コマンドのパス設定をすれば動くはずなんだけど。

どうもうまくいかないので、状況整理して動かしてみた。

WSL ruby を cmd.exe から呼び出す。

wsl 版 ruby wsl にインストールしたものは、Windows側から次のように実行される。

wsl --exec ruby -v
wsl --exec python -V
wsl --exec php -v
wsl --exec php -v
wsl --exec bash --version
wsl --exec bash -c "/bin/echo 1;"

実行例

たとえば、ruby を実行するときには、この様になる。

C:\Users\takuya>wsl --exec ruby -v
ruby 2.5.5p157 (2019-03-15 revision 67260) [x86_64-linux-gnu]

逆もできる

逆もやっておく、WSLからWindows世界のコマンドを実行する。

cmd exe などWindowsのコマンドをWSLの世界から呼び出すこともできる。

takuya@wsl$ cmd.exe 
takuya@wsl$ cmd /C start ping 1.1.1.1

code コマンドを、wsl 側から 実行

WSL の世界からWindowsにあるCodeを起動する。

$ which code 
/mnt/c/Program Files/Microsoft VS Code/bin/code

visual studio code も、wsl とWindows(ローカル)に分かれる wsl 側から visual studio code は次のように呼び出される。

wsl / vscoderuby の設定をする。

visual studio code を WSL から使ってインストールする。

code --install-extension castwide.solargraph
code --install-extension rebornix.ruby

wsl 側から使うときは、wsl 側からコマンドを実行する。

通常のwindowsから vscoderuby を設定をする。

code --install-extension castwide.solargraph
code --install-extension rebornix.ruby

そのままだと動かない。

f:id:takuya_1st:20220405150106p:plain

ruby の solargraph を使う設定(ワークスペースへ)

solargraphを、gem インストールして上げる必要がある。gem を bundle でインストールするので「プロジェクト」のbundle でやる

ワークスペースで使うだけでなので、ワークスペースに全てをインストールする方針。

bundle config set path 'vendor/bundle'
bundle init
bundle add solargraph -g development
bundle install

wsl にある。bundle をwindowsから呼び出す。

C:\Users\takuya>wsl --exec bundle -v
Bundler version 1.17.3

wsl bundle を windows から呼び出しチェック

windows側から wsl にあるruby/bundle経由で solargraphを 起動してみる。

solargraph がインストールされていて、windows側から起動できる状態であるとわかる。

f:id:takuya_1st:20220405150252p:plain

vscode に設定する。

vscodewindowsから実行されるので、 bundle のパスがわからないので、wsl 経由で呼び出してあげる。

ruby を使うのはワークスペースなので、設定は ワークスペースで。

先程確認した、wsl -exec bundlevscode で設定

{
    "solargraph.bundlerPath": "wsl --exec bundle",
    "solargraph.useBundler": true
}

f:id:takuya_1st:20220405150303p:plain

動いた

https://blog.freks.jp/vscode-remote-wsl-boot-error/

.vscode のフォルダを削除してあげたら動いたよ。

f:id:takuya_1st:20220405150353p:plain

appdata フォルダを丸コピー

WSL側にapp dataを丸コピーしてあげてもいいけど。基本的には、動くんだけど、一部で不具合が出る

その他の解決方法

WSLのリモートを接続していれば問題ない。

wsl で起動してたらこんな面倒にならない。

wsl$ code . 

f:id:takuya_1st:20220405150421p:plain

f:id:takuya_1st:20220405150431p:plain

通常であればリモート起動をしてしまえばいいんだけど、パス設定で解決を試みたら結構沼だった。

パス設定で解決しないで、まるっとリモート側に入ることがVS Codeの魅力なんだなと改めて実感する。

win10 の powertoys マウスユーティリティが授業・ビデオ会議の画面共有に便利

powertoys のマウスユーティリティでできること

windows 10 の PowerToysにマウスユーティリティが追加されています。

f:id:takuya_1st:20220405143652p:plain

マウスのクリック位置をハイライト

Win+Shift-H でオンオフを切り替えて、クリック場所を強調することができます。

マウスでクリック場所を強調表示することができます。

f:id:takuya_1st:20220405143636p:plain

右クリックの場合を色を替えることもできます。

f:id:takuya_1st:20220405144328p:plain

マウス・カーソル位置を強調

マウスのカーソル位置を視覚的に強調できます。

Alt-Ctrl-p でオンオフ、カーソル位置を強調できます。

ただ、Alt-ctl-pは他のアプリケーションのキーボードショートカットとかぶるので変更したほうが良さそうです。

縦横線で強調

f:id:takuya_1st:20220405143630p:plain

色を替えることもできます。

f:id:takuya_1st:20220405143952p:plain

プレゼンテーション・講義・発表・画面共有に便利

操作手順を説明する時、プロジェクターを使う時、発表時にとても重宝すると思います。

ビデオ会議で画面共有をしていると、解像度が落ちてしまったりfpsが落ちるので、視聴者が見逃す可能性が高いので、ツールを入れると学習効果が上がると思います。