シン・テレワークシステムのポリシー規制をラズパイで構築

テレワークが一般的に普及してきた昨今、IPAが提供している[シン・テレワークシステム](https://telework.cyber.ipa.go.jp/news/)を使用する場面もあると思います。

最低限の設備投資でエンタープライズ環境用ポリシー規制サーバーを構築してみます
https://telework.cyber.ipa.go.jp/enterprise/


## サーバーソフトウェアの作成

最低限の構成としたいので一つのファイルで完結するGO言語で作成する。
ファイルを一つ配信するだけなのでNginxで直接ファイル配信するのもアリ。
その場合は読み飛ばしてください


### 証明書の作成

ポリシーサーバーはhttpsが必須で、なんでもいいのでTLS1.2かTLS1.3が必須とのこと

> ポリシーサーバーには、TLS 1.2 または TLS 1.3 で、何らかの SSL 証明書 (RSA X.509 1024bit ～ 4096bit のいずれか) が設定されている必要があります。

opensslでセルフ証明書を作成する

```shell
 $ openssl genrsa 2048 > server.key
 $ openssl req -new -key server.key > server.csr
 $ openssl x509 -days 365 -req -sha256 -signkey server.key < server.csr > server.crt
```

使用する成果物は`server.key`と`server.crt`の二つ


### ポリシーの作成

サンプルを参照する。

https://telework.cyber.ipa.go.jp/enterprise/sample_policy.txt

どうも本文内に空行が存在すると無効になるらしいので必要な部分だけ抜き取って`policy.txt`というファイルを作成する。
今回はひとまずメッセージとsyslogの設定のみ

```
SERVER_MESSAGE   コンピュータID・パスワード・ファイルの取り扱いには十分に注意してください<BR>万が一疑わしい事態が発生した場合や不明点は担当（＊＊＊＊）まで
SYSLOG_HOSTNAME    xxx.xxx.xxx.xxx
SYSLOG_PORT        514
```

### クライアントソフト

GO言語で作成

goのバージョンは`1.19.7`で作成。
もう少し低いバージョンでもいいが、TLS1.3のサポートが`1.17`くらいだったはず

このファイルと同一ディレクトリに上で作った`policy.txt`と`server.key`と`policy.crt`を配置する
アレンジはお好みでどうぞ

```go
package main

import (
	"crypto/tls"
	"log"
	"net/http"
	"os"
	"strings"

	_ "embed"

	"github.com/go-chi/chi/v5/middleware"
)

// ポリシーファイル。以下のサンプルを編集して同一policy.txtという名前で保存する
// https://telework.cyber.ipa.go.jp/enterprise/sample_policy.txt
//
//go:embed policy.txt
var policy []byte

// 作成したサーバー秘密鍵
// 1024bit ～ 4096bit
// TLSを使用しないなら空ファイルで良い
//
//go:embed server.key
var private []byte

// 作成したサーバー証明書
// 期限が切れていてもよい
// TLSを使用しないなら空ファイルで良い
//
//go:embed server.crt
var cert []byte

// 待ち受けするアドレス
var addr string = "10.255.255.127:443"

func init() {
	// 起動時に環境変数"ADDR"を読み込んで待受アドレスを変更する
	a := os.Getenv("ADDR")
	if len(a) > 0 {
		addr = a
	}
}

// 最低限のhttpハンドラ
func handle(w http.ResponseWriter, r *http.Request) {
	if strings.TrimRight(r.URL.Path, "/") == "/get-telework-policy" {
		w.Write(policy)
		return
	}

	w.WriteHeader(http.StatusNotFound)
}

// net/httpに渡すハンドラ
// 機能を増やすならここでchiなりginなりを使用するとよい
func newHandler() http.Handler {
	var handler http.Handler
	handler = http.HandlerFunc(handle)
	// ログを表示したいのでgo-chiのものを使用する
	handler = middleware.Logger(handler)

	return handler
}

// TLSを使用しない
func run() error {
	return http.ListenAndServe(addr, newHandler())
}

// TLSを使用して開始
func runTLS() error {
	keyPair, err := tls.X509KeyPair(cert, private)
	if err != nil {
		return err
	}

	srv := &http.Server{
		Addr:    addr,
		Handler: newHandler(),
		TLSConfig: &tls.Config{
			ServerName:   "10.255.255.127",
			Certificates: []tls.Certificate{keyPair},
			CipherSuites: []uint16{
				tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
				tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
				tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
				tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
				tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
			},
			PreferServerCipherSuites: true,

			MinVersion: tls.VersionTLS12,
			MaxVersion: tls.VersionTLS13,
		},
	}

	return srv.ListenAndServeTLS("", "")
}

func main() {
	var err error

	err = runTLS()
	if err != nil {
		log.Fatal(err)
	}
}

```

### 依存パッケージの取得

```shell
$ go get github.com/go-chi/chi/v5/middleware
```


### アプリの動作確認

```shell
$ ADDR=:8080 go run main.go
```

もう一つターミナルを起動してcurlを打ってみる
```shell
$ curl https://localhost:8080/get-telework-policy/
curl: (60) SSL certificate problem: self-signed certificate
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
```
いわゆるオレオレ証明書なのでこんなエラーになる。

では`--insecure`オプションをつけて証明書の検証をスキップさせて再度検証

```shell
$ curl --insecure https://localhost:8080/get-telework-policy/
SERVER_MESSAGE   パスワード・コンピュータID・ファイルの取り扱いには十分に注意してください<BR>万が一疑わしい事態が発生した場合や不明点は担当(****)まで
SYSLOG_HOSTNAME    xxx.xxx.xxx.xxx
SYSLOG_PORT        514%     
```

意図通りのポリシーが返ってきたら完了

### ビルド
ビルドして一つの実行ファイルに固める。
今回はRaspberry Pi上で動かすのでarm向けにコンパイル

```shell
$ GOOS=linux GOARCH=arm go build main.go
```

何もエラーがなかったら`main`というファイルが作成されていると思います

## ルーターの設定

ドキュメントによるとテレワークシステムは定期的に`10.255.255.127`へのアクセスを試みるという。
というわけでルーターに静的ルーティングを追加し、上記ip宛のパケットを任意のアドレスにルーティングさせる

まず配信サーバーとして動作させるRaspberryPiのIPを固定する。
そしてルーターのルートテーブルを変更する。

例えば、RaspberryPiの固定IPが`192.168.0.100`として、
ヤマハのルーターならこう
```
ip route 10.255.255.127 gateway 192.168.0.100
```

## RaspberryPiの設定

いよいよRaspberryPi側の設定。

### ネットワーク設定(RaspberryPi側)

この時点で`10.255.255.127`宛のパケットはRaspberryPiに飛んでいるはず。
RaspberryPiにそのアドレスを認識させて、パケットを受け取るようにする。

```shell
$ sudo ip address del 10.255.255.127 dev eth0 
```
WiFi接続している場合は`eth0`の部分を変更する。

pingを飛ばして受け取っているかを確認。
windowsでコマンドプロンプトを起動し、下記コマンドを打つ

```shell
>ping 10.255.255.127

10.255.255.127 に ping を送信しています 32 バイトのデータ:
10.255.255.127 からの応答: バイト数 =32 時間 =1ms TTL=64
10.255.255.127 からの応答: バイト数 =32 時間 <1ms TTL=64
10.255.255.127 からの応答: バイト数 =32 時間 <1ms TTL=64
10.255.255.127 からの応答: バイト数 =32 時間 <1ms TTL=64

10.255.255.127 の ping 統計:
    パケット数: 送信 = 4、受信 = 4、損失 = 0 (0% の損失)、
ラウンド トリップの概算時間 (ミリ秒):
    最小 = 0ms、最大 = 1ms、平均 = 0ms
```


### サーバーを動作

上で作った`main`をRaspberryPiに転送して動かしてみる。

```shell
$ sudo ./main
```

そしてwindowsのブラウザから下記URLにアクセスする。
https://10.255.255.127/get-telework-policy/

*この接続ではプライバシーが保護されません* みたいな警告が出たら成功。
`詳細設定`をクリックして`10.255.255.127にアクセスする（安全ではありません）`をクリックしてみましょう


このタイミングでシン・テレワークサーバー設定を起動すればこんな表示が出ると思います
(確認間隔の影響で最大5分のタイムラグがあるかも)

![NTT 東日本 - IPA シン・テレワークシステム サーバー へようこそ 2023_10_23 17_01_16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31309/ab4004d5-c61f-ec80-9a08-d0348140f382.png)

これが意図通りだったら疎通確認は完了です。

## 常駐化

これを常駐化させて再起動しても動くようにしましょう。

### アドレス追加の自動化

`ip addr add`は終了するとリセットされるのでsystemdに登録して起動時にコマンドを打たせる

```shell
$ sudo vim /etc/systemd/system/add-address.service
```

```
Description = add virtual address
After = syslog.target

[Service]
ExecStart = /usr/sbin/ip address add 10.255.255.127 dev eth0
Type = oneshot
RemainAfterExit=yes

[Install]
WantedBy = multi-user.target

```
そして有効化

```shell
$ sudo systemctl enable /etc/systemd/system/add-address.service
```


※本当は[Install]の部分を適切に設定したほうがいいが今回は割愛


### アプリケーションのdaemon化

まず`main`を専用ディレクトリに格納する。場所はお好みで

```
$ sudo mkdir /opt/thin-telework
$ sudo mv main /opt/thin-telework/
```

サービスファイルを作成する

```shell
$ sudo vim /etc/systemd/system/thin-telework.service
```

```
Description = thin telework policy server
After = syslog.target

[Service]
WorkingDirectory = /opt/thin-telework
ExecStart = /opt/thin-telework/main

Restart=on-success
Type = simple
StandardError = syslog
NotifyAccess = all

[Install]
WantedBy = multi-user.target
```

作成したら有効化して起動
```shell
$ sudo systemctl enable thin-telework.service
Created symlink /etc/systemd/system/multi-user.target.wants/thin-telework.service → /etc/systemd/system/thin-telework.service.
$ sudo systemctl start thin-telework.service
$ sudo systemctl status thin-telework.service
● thin-telework.service
   Loaded: loaded (/etc/systemd/system/thin-telework.service; enabled; vendor preset: enabled)
   Active: active (running) since Mon 2023-10-23 08:37:32 BST; 37min ago
 Main PID: 852 (main)
    Tasks: 4 (limit: 877)
   CGroup: /system.slice/thin-telework.service
           └─852 /opt/thin-telework/main
```

### 動作確認

RaspberryPiとWindowsを再起動し、テレワークシステム設定画面で設定が反映されていれば完了