【ZeroTier】網内の特定IP範囲にダイレクトアクセスする（自分で管理鯖 「Moon」も作る）

## これはなに？

- ZeroTier: Hamachiのようなやつらしい？
  - 特定IP範囲を網内ノードを経由してアクセスしたりできる。
  - VPNとの違い: 全トラヒックが網内に入らない。重くならない。
  - SOCKS5との違い: Proxyではない。Samba接続が効く。


## 想定シナリオ・確認環境

### ZeroTier

version: `1.10.2`

### PC

- 管理ノード（Ubuntu18.04, VPS）`myvps`
- 網内ノード（Ubuntu18.04, 物理鯖）`mysrv`
- 網外ノード（Windows 11, ラップトップ） `mylaptop`

### 構成

- 仮想IP範囲: `192.168.199.0/24`
  - 網内ノード: `192.168.199.10`
    - 経由経路: `172.22.0.0/16`（この範囲にSamba鯖とかRDP鯖とかある）
  - 網外ノード: `192.168.199.20`


## 実施方法

### Moon（管理ノード） の作成

#### 手順


##### インストール

- スクリプト: `curl -s https://install.zerotier.com | sudo bash`
- git: `git clone https://github.com/zerotier/ZeroTierOne && cd ZeroTierOne && make && sudo make install`
  - need to `cargo` / `rustc`

##### 起動・起動テスト

- ベタ: `sudo /var/lib/zerotier-one/zerotier-one -d` or `sudo zerotier-one -d` (`/usr/sbin` にリンク済みっぽい）
- リッチ: `sudo systemctl start zerotier-one`
   - gitから入れたが、`service` ファイルを作らなくても行けた

チェック: `sudo zerotier-one info`

##### ネットワークの作成

> あとでWebGUIでも作成可能? 

今回はAPIで作ってみる

1. インストール場所にある `authtoken.secret` をチェック: `TOKEN=$(sudo cat /var/lib/zerotier-one/authtoken.secret)`
2. `info` サブコマンドで現在のノードIDをチェック:  `NODEID=$(sudo zerotier-cli info | cut -d " " -f 3)`
3. 作成する `curl -X POST "http://localhost:9993/controller/network/${NODEID}______" -H "X-ZT1-AUTH: ${TOKEN}" -d {}`
4. 作成したネットワークを確認: `curl "http://localhost:9993/controller/network/" -H "X-ZT1-AUTH: ${TOKEN}"`
5. NetworkIDを控えておく `NETWORK_ID="$(curl -sS "http://localhost:9993/controller/network/" -H "X-ZT1-AUTH:
 ${TOKEN}" | cut -d\" -f2)"`
##### Moonの作成

1. 作業ディレクトリ作成、移動: `mkdir -p /tmp/ZeroTier/ && cd /tmp/ZeroTier`
2. インストール場所にある `identity.public` の確認: `ls /var/lib/zero-tier-one/identity.public`
3. 設定ファイルの作成: `sudo zerotier-idtool initmoon  /var/lib/zero-tier-one/identity.public | tee ./moon.json`
4. `moon.json` の `"stableEndpoints"` キーのリストにサーバーのIPアドレスとポートを追記する。 追記例: `[ "192.0.2.1/9993","2001:db8::1/9993"]`
5. Moonノードの作成: `sudo zerotier-idtool genmoon ./moon.json`
6. 直下に作られた構成ファイルを確認: `ls ./*.moon`
7. 構成ファイルをインストール場所に移動する: `sudo mkdir -p /var/lib/zero-tier-one/moon.d && cp ./*.moon /var/lib/zero-tier-one/moon.d`
8. 再起動: `sudo systemctl restart zero-tier-one`
9. World IDを控えておく。 `WORLD_ID="$(grep -F '"id":' ./moon.json | cut -d\" -f4)"`

##### Web管理UI: ztncuiの導入

詳細: https://key-networks.com/ztncui/

1. ダウンロード: `wget -P /tmp/ https://s3-us-west-1.amazonaws.com/key-networks/deb/ztncui/1/x86_64/ztncui_0.8.6_amd64.deb`
2. インストール: `sudo apt install /tmp/ztncui*amd64.deb`
3. デーモン確認: `sudo systemctl status ztncui`
4. ポートの確認: `sudo lsof -i:3000`

###### Web管理画面へのログイン

1. ユーザーのPC(OpenSSH Client on Powershell or linux shell)から管理サーバーへポート転送する: `ssh -L 3000:localhost:3000 myvps`
2. ブラウザからアクセス: `http://localhost:3000/`
3. デフォルトユーザー `admin` デフォルトパスワード `password` でログインする
4. のちほど細やかな設定を行う

#### 参考

1. [Creating Your Own Roots (a.k.a. Moons) - ZeroTier Documentation](https://docs.zerotier.com/zerotier/moons/)
2. [搭建 Zerotier Moons Server](https://umm.js.org/p/d744f606/)

### 網内ノードの設定

#### 手順

##### インストール

- スクリプト: `curl -s https://install.zerotier.com | sudo bash`
- git: `git clone https://github.com/zerotier/ZeroTierOne && cd ZeroTierOne && make && sudo make install`
  - need to `cargo` / `rustc`

##### 起動・起動テスト

- ベタ: `sudo /var/lib/zerotier-one/zerotier-one -d` or `sudo zerotier-one -d` (`/usr/sbin` にリンク済みっぽい）
- リッチ: `sudo systemctl start zerotier-one`
   - gitから入れたが、`service` ファイルを作らなくても行けた

チェック: `sudo zerotier-one info`

##### Moonへの参加・チェック

- `orbit` サブコマンドを使う: `sudo zerotier-cli orbit "$WORLD_ID" "$WORLD_ID"`
   - TODO: `orbit <world_id> <seed>` の `<seed>` がなにか？
-  確認: `sudo zerotier-cli listpeers`

##### Networkへの参加

- `join` サブコマンドを使う: `sudo zerotier-cli join "$NETWORK_ID"`
- 確認: `sudo zerotier-cli listnetworks`

##### ブリッジのための設定（網内ネットワークへ入るため）

- IPv4転送を許可する: `sudo sysctl -w net.ipv4.p_forward=1`
- 転送先の物理インターフェースを定義: `PHY_IFACE=eno1`
- 転送元の仮想インターフェースを定義: `ZT_IFACE=zt7nnig26`
- iptables: 物理インターフェースにNATマスカレード機能をつける？
  - `sudo iptables -t nat -A POSTROUTING -o $PHY_IFACE -j MASQUERADE`
- iptables: 網内からのIncomingに対して許可する（TODO: `ESTABLISHED`?）
  - `sudo iptables -A FORWARD -i $PHY_IFACE -o $ZT_IFACE -m state --state RELATED,ESTABLISHED -j ACCEPT`
- iptables: 網内へのOutgoingの転送を行う
  - `sudo iptables -A FORWARD -i $ZT_IFACE -o $PHY_IFACE -j ACCEPT`
- 保存のための手続き
  - iptablesを保存するパッケージをインストール
    - `sudo apt install iptables-persistent`
  - 保存
    - `sudo bash -c iptables-save > /etc/iptables/rules.v4`

#### 参考

- [Route between ZeroTier and Physical Networks -  Knowledge Base](https://zerotier.atlassian.net/wiki/spaces/SD/pages/224395274/Route+between+ZeroTier+and+Physical+Networks)

### 網外ノードの設定

1. ここからダウンロード: https://www.zerotier.com/download/
2. msiでインストールする
3. ZeroTierを起動する。システムトレイにアイコンが表示される。
4. `Join New Network...` で `$NETWORK_ID` を入れる

### 管理ノードの設定

###### Web管理画面へのログイン

1. ユーザーのPC(OpenSSH Client on Powershell or linux shell)から管理サーバーへポート転送する: `ssh -L 3000:localhost:3000 myvps`
2. ブラウザからアクセス: `http://localhost:3000/`
3. デフォルトユーザー `admin` デフォルトパスワード `password` でログインする（もしくは再設定済みパスワード）
4. Networksタブからネットワークを表示
5. Assignment Pools でIP範囲を設定 `192.168.199.0/24`
6. Membersに表示されている網内と網外ノードの IP Assignmentでそれぞれ充てる
   - 網外ノード: `192.168.199.10`
   - 網内ノード: `192.168.199.20`
7. Memberに表示されている網内と網外ノードの Authorized にチェックを入れる
8. Memberに表示されてい網外ノードの Active bridge にチェックを入れる
9. Routesにアクセスし、Target: `172.22.0.0/16` は Gateway `192.168.199.20` を経由するようルートを追加
10. あとは網外ノードがどのIP範囲であっても（`192.168.0.210/24`であっても）、網内ノードのIP（192.168.0.210/24`）も直アクセスしたいので `192.168.0.210/32` で特定IPだけはルートを作っておく

### テスト

1. `172.22.*.*` に網外のラップトップからできるか
2. `192.168.0.210` は網内の鯖になるか

## 謝辞

- ZeroTierを教えてくれた後輩に感謝。
- 先人のブログなどに感謝
- オープンチャットでHamachiとにてるんじゃね？とか情報いただいたユーザにも感謝: 
  - 参考: [N2N 局域网VPN的今生前世(转贴)](https://www.zhuguodong.com/?id=405)


## その他参考

- [Introduction - ZeroTier Documentation](https://docs.zerotier.com/self-hosting/introduction)
- [Alternatives to ZeroTier? - reddit r/zerotier](https://www.reddit.com/r/zerotier/comments/jfpj5r/alternatives_to_zerotier/)