前回はConsulのHTTP APIのTLSを有効化し、HTTPSでアクセスするように制限しました。
今回はNomadのHTTP APIをTLS化します。
公式のチュートリアルに手順が示されているので、それをベースに若干アレンジしています。
openssl ではなく、チュートリアルでの例のとおりcfsslを使って、証明書や鍵を生成します。
cfsslのインストールについては公式ドキュメント等を参照してください。
ここではDebianにapt-getでインストールしています。
$ apt-get install golang-cfssl
概要
NomadはConsulと異なり、HTTPとHTTPSを別ポートで共存することはできません。
HTTP APIはデフォルトだとポート4646をリッスンしますが、TLSを有効にするとHTTPではアクセスできなくなります。
今回はこのポート4646のHTTP APIをTLS化し、クライアント認証を必須に設定していきます。
注意点としては、Consulと同様にNomadの管理UI画面もHTTP APIと同一ポートで提供されるため、管理UI画面にアクセスする場合にもクライアント認証が必要になります。
またNomadのCLIも裏側ではHTTP APIが使用されるため、Nomad CLIを使用する場合もクライアント認証が必須となります。
では進めていきましょう。
ルートCAの作成
まず、プライベートなルートCAの証明書と鍵を生成します。
$ cat <<EOF > ca_csr.json
{
"CN": "My Nomad CA",
"key": {
"algo": "ecdsa",
"size": 256
},
"names": [
{
"OU": "Your Unit",
"O": "Your Company",
"C": "JP",
"ST": "TOKYO",
"L": "XXX-KU"
}
]
}
EOF
$
$ cfssl gencert -initca ca_csr.json | cfssljson -bare nomad-ca
2021/03/30 06:30:31 [INFO] generating a new CA key and certificate from CSR
2021/03/30 06:30:31 [INFO] generate received request
2021/03/30 06:30:31 [INFO] received CSR
2021/03/30 06:30:31 [INFO] generating key: ecdsa-256
2021/03/30 06:30:31 [INFO] encoded CSR
2021/03/30 06:30:31 [INFO] signed certificate with serial number 88956190062274699214290191130303041727763892227
$ ls
ca_csr.json nomad-ca.csr nomad-ca-key.pem nomad-ca.pem
$
$ openssl x509 -text -noout < nomad-ca.pem
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
0f:94:ee:fb:38:e1:b5:9b:fb:e7:5e:65:59:af:9c:ff:b3:a4:c8:03
Signature Algorithm: ecdsa-with-SHA256
Issuer: C = JP, ST = TOKYO, L = XXX-KU, O = Your Company, OU = Your Unit, CN = My Nomad CA
Validity
Not Before: Mar 30 06:26:00 2021 GMT
Not After : Mar 29 06:26:00 2026 GMT
Subject: C = JP, ST = TOKYO, L = XXX-KU, O = Your Company, OU = Your Unit, CN = My Nomad CA
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:f9:fa:90:22:fc:f7:05:7a:e0:af:69:97:9e:80:
...(中略)...
21:e2:13:56:de
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Key Usage: critical
Certificate Sign, CRL Sign
X509v3 Basic Constraints: critical
CA:TRUE
X509v3 Subject Key Identifier:
82:43:67:03:C2:09:B1:51:94:33:43:7D:B2:54:09:7C:11:A5:F3:9E
Signature Algorithm: ecdsa-with-SHA256
30:45:02:21:00:ad:87:3c:5a:70:07:4a:91:3b:63:45:5e:61:
...(中略)...
42:e3:93:d8:33:1a:93:43:f5:d8:7f:f8:49:57:63:fd:bc
ca_csr.jsonでSubjectの内容(OU,CN等)や鍵のスペック(ECDSA 256bit)を指定しています。
cfsslを使うと、あっさりと自己署名のルートCA証明書と鍵ファイルを生成できました。
便利ですね。
- nomad-ca.pem: ルートCA証明書
- nomad-ca-key.pem: ルートCAの鍵
以下の2つは中間生成物なので削除して問題ありません。
- ca_csr.json
- nomad-ca.csr
サーバ証明書、クライアント証明書の作成
ここで一気に以下のファイルを作成します。
- Nomadサーバ用のサーバ証明書と鍵
- Nomadクライアント用のサーバ証明書と鍵
- Nomad CLI/HTTP APIクライアント用のクライアント証明書と鍵
- クライアント証明書と鍵をPKCS#12形式にパッケージングしたもの
流れは以下のスクリプトファイルの通りです。
#!/bin/bash
CA_FILE=./nomad-ca.pem
CA_KEY=./nomad-ca-key.pem
#有効期間:3650日
cat <<EOF > config.json
{
"signing": {
"default": {
"expiry": "87600h",
"usages": ["signing", "key encipherment", "server auth", "client auth"]
}
}
}
EOF
# 管理UI画面へアクセスする際のDNS名をhostsに指定しておく
cat <<EOF > nomad_server.json
{
"CN": "nomad.pmtmsfw.tk",
"hosts": [
"server.global.nomad",
"localhost",
"127.0.0.1",
"nomad.mydomain.tk"
],
"key": {
"algo": "ecdsa",
"size": 256
},
"names": [
{
"OU": "Your Unit",
"O": "Your Company",
"C": "JP",
"ST": "TOKYO",
"L": "XXX-KU"
}
]
}
EOF
cfssl gencert -ca=$CA_FILE -ca-key=$CA_KEY \
-config=config.json nomad_server.json | cfssljson -bare nomad-server
cat <<EOF > nomad_client.json
{
"CN": "nomad-client.mydomain.tk",
"hosts": [
"client.global.nomad",
"localhost",
"127.0.0.1"
],
"key": {
"algo": "ecdsa",
"size": 256
},
"names": [
{
"OU": "Your Unit",
"O": "Your Company",
"C": "JP",
"ST": "TOKYO",
"L": "XXX-KU"
}
]
}
EOF
cfssl gencert -ca=$CA_FILE -ca-key=$CA_KEY \
-config=config.json nomad_client.json | cfssljson -bare nomad-client
cat <<EOF > nomad_cli.json
{
"CN": "nomad-cli.mydomain.tk",
"hosts": [
"localhost",
"127.0.0.1"
],
"key": {
"algo": "ecdsa",
"size": 256
},
"names": [
{
"OU": "Your Unit",
"O": "Your Company",
"C": "JP",
"ST": "TOKYO",
"L": "XXX-KU"
}
]
}
EOF
cfssl gencert -ca=$CA_FILE -ca-key=$CA_KEY \
-profile=client nomad_cli.json | cfssljson -bare nomad-cli
スクリプトを実行します。
$ chmod +x create_certs.sh
$ ./create_certs.sh
2021/03/30 08:28:40 [INFO] generate received request
2021/03/30 08:28:40 [INFO] received CSR
2021/03/30 08:28:40 [INFO] generating key: ecdsa-256
2021/03/30 08:28:40 [INFO] encoded CSR
2021/03/30 08:28:40 [INFO] signed certificate with serial number 494608314106644591725088700987600713379823876766
2021/03/30 08:28:40 [INFO] generate received request
2021/03/30 08:28:40 [INFO] received CSR
2021/03/30 08:28:40 [INFO] generating key: ecdsa-256
2021/03/30 08:28:40 [INFO] encoded CSR
2021/03/30 08:28:40 [INFO] signed certificate with serial number 639450421206660877889416706379311058516682651023
2021/03/30 08:28:40 [INFO] generate received request
2021/03/30 08:28:40 [INFO] received CSR
2021/03/30 08:28:40 [INFO] generating key: ecdsa-256
2021/03/30 08:28:40 [INFO] encoded CSR
2021/03/30 08:28:40 [INFO] signed certificate with serial number 184117806583655875159914202903602417004142126537
$
$ for F in `ls -X *.pem`; do echo $F;done
nomad-ca-key.pem
nomad-ca.pem
nomad-client-key.pem
nomad-client.pem
nomad-cli-key.pem
nomad-cli.pem
nomad-server-key.pem
nomad-server.pem
$
$ openssl pkcs12 -export -out nomad-ui-client.p12 -passout pass: \
-inkey nomad-cli-key.pem -in nomad-cli.pem -certfile ./nomad-ca.pem
$
$ ls *.p12
nomad-ui-client.p12
最後のopensslコマンドで、クライアント証明書と鍵をPKCS#12形式にパッケージングしています。
この「nomad-ui-client.p12」については後述しますが、管理UI画面にアクセスする際に使用します。
ここまでで以下のファイルが出来上がりました。
ファイル名 | 用途 |
---|---|
nomad-ca.pem | Nomad用プライベートルートCAの証明書 |
nomad-ca-key.pem | Nomad用プライベートルートCAの鍵 |
nomad-server.pem | Nomadサーバ向けのサーバ証明書 |
nomad-server-key.pem | Nomadサーバ向けの鍵 |
nomad-client.pem | Nomadクライアント向けのサーバ証明書 |
nomad-client-key.pem | Nomadクライアント向けの鍵 |
nomad-cli.pem | Nomad CLI/HTTP API向けのクライアント証明書 |
nomad-cli-key.pem | Nomad CLI/HTTP API向けの鍵 |
nomad-ui-client.p12 | Nomad CLI/HTTP API向けのクライアント証明書と鍵を同梱したもの |
ここでも*.jsonと*.csrは、中間生成物なので削除して問題ありません。
Nomadサーバへのサーバ証明書・鍵の配置と設定
Nomadサーバへ配置するファイルは以下のとおりです。
前回、ConsulのHTTP APIをTLS化したので、Consulにクライアント認証有りHTTPSで接続するためのファイルも配置します。
- nomad-ca.pem
- nomad-server.pem
- nomad-server-key.pem
- consul-agent-ca.pem(ConsulのルートCA証明書)
- dc1-cli-consul-0.pem(Consulのクライアント証明書)
- dc1-cli-consul-0-key.pem(Consulのクライアント認証用の鍵)
以下はNomadサーバの設定ファイルの例です。
datacenter = "dc1"
data_dir = "/opt/nomad"
bind_addr = "0.0.0.0"
advertise {
http = "{{ GetInterfaceIP \"eth0\" }}"
rpc = "{{ GetInterfaceIP \"eth0\" }}"
serf = "{{ GetInterfaceIP \"eth0\" }}"
}
server {
enabled = true
bootstrap_expect = 3
raft_protocol = 3
# Enable gossip encryption
encrypt = "VUeFI3llUP3EHuHxV/TEOIpMJL5EMgMYIFpaCA8j2dw="
}
client {
enabled = false
}
# access Consul with TLS
consul {
ssl = true
address = "127.0.0.1:8501"
ca_file = "/path/to/consul-agent-ca.pem"
cert_file = "/path/to/dc1-cli-consul-0.pem"
key_file = "/path/to/dc1-cli-consul-0-key.pem"
}
# Require TLS
tls {
http = true
rpc = true
ca_file = "/path/to/nomad-ca.pem"
cert_file = "/path/to/nomad-server.pem"
key_file = "/path/to/nomad-server-key.pem"
verify_server_hostname = true
verify_https_client = true
}
consulブロックでlocalhostに配備したconsulにHTTPSでアクセスするための設定を加えています。
tlsブロックのverify_server_hostname はサーバ間通信の際に接続先サーバのホスト名を検証するフラグです。
verify_https_client はサーバに接続するアクセス元にクライアント認証を要求するフラグです。
同一のCAで署名されたクライアント証明書を持たなければ接続を許可しません。
Nomadクライアントのサーバ証明書・鍵の配置と設定
Nomadサーバへ配置するファイルは以下のとおりです。
Nomadサーバとの違いはNomadクライアント用のサーバ証明書を配置しているところです。
- nomad-ca.pem
- nomad-client.pem
- nomad-client-key.pem
- consul-agent-ca.pem(ConsulのルートCA証明書)
- dc1-cli-consul-0.pem(Consulのクライアント証明書)
- dc1-cli-consul-0-key.pem(Consulのクライアント認証用の鍵)
datacenter = "dc1"
data_dir = "/opt/nomad"
bind_addr = "0.0.0.0"
advertise {
http = "{{ GetInterfaceIP \"eth0\" }}"
rpc = "{{ GetInterfaceIP \"eth0\" }}"
serf = "{{ GetInterfaceIP \"eth0\" }}"
}
server {
enabled = false
}
client {
enabled = true
network_interface = "eth0"
}
# adopt TLS for consul
consul {
ssl = true
address = "127.0.0.1:8501"
ca_file = "/path/to/consul-agent-ca.pem"
cert_file = "/path/to/dc1-cli-consul-0.pem"
key_file = "/path/to/dc1-cli-consul-0-key.pem"
}
# Require TLS
tls {
http = true
rpc = true
ca_file = "/path/to/nomad-ca.pem"
cert_file = "/path/to/nomad-client.pem"
key_file = "/path/to/nomad-client-key.pem"
verify_server_hostname = true
verify_https_client = true
}
Nomad CLI/HTTP APIの実行方法
CLIを実行する場合は、環境変数を設定すると便利です。
以下のようにします。
$ export NOMAD_ADDR=https://nomad.mydomain.tk:443
$ export NOMAD_CACERT=/path/to/nomad-ca.pem
$ export NOMAD_CLIENT_CERT=/path/to/nomad-cli.pem
$ export NOMAD_CLIENT_KEY=/path/to/nomad-cli-key.pem
$
$ nomad server menbers
Name Address Port Status Leader Protocol Build Datacenter Region
ip-10-10-xx-xx.service.consul.global 10.10.xx.xx 4648 alive true 2 1.0.3 dc1 global
ip-10-10-xx-xx.service.consul.global 10.10.xx.xx 4648 alive false 2 1.0.3 dc1 global
ip-10-10-xx-xx.service.consul.global 10.10.xx.xx 4648 alive false 2 1.0.3 dc1 global
curlでHTTP APIを呼び出す場合は、下記のようにオプションを指定します。
$ curl -s --cacert ${NOMAD_CACERT} --cert ${NOMAD_CLIENT_CERT} --key ${NOMAD_CLIENT_KEY} ${NOMAD_ADDR}/v1/agent/health | jq
{
"server": {
"message": "ok",
"ok": true
}
}
Nomad管理UI画面へのアクセス
前述の手順の中で、クライアント証明書と鍵をPKCS#12形式にパッケージングした「nomad-ui-client.p12」を作成しました。
このファイルをPC側のWebブラウザに登録することで管理UI画面をHTTPSでアクセス可能です。
(Windowsの場合、nomad-ui-client.p12をダブルクリックすると「証明書のインポートウイザード」が起動するので、ウィザードに従って登録します)
まとめ
これでNomadのHTTP APIのTLS化ができました。
NomadはNomadサーバ群を認識するためにConsulに問い合わせますので、ConsulのHTTP APIをTLS化した場合は、Consulのクライアント認証を満たすように設定が必要です。
ここまででConsulとNomadのTLS化ができましたので、次回はNomad上にPrometheusを配置してみます。
そこでConsulとNomadのメトリクス情報をPrometheusで収集してみましょう。
おまけ:Gossip通信の暗号化
上記のTLSの有効化では、エージェント間のRPC呼び出しやエージェントへのHTTP API呼び出しを保護します。
一方、Consulの場合もそうでしたが、Nomadサーバ間のゴシップ通信については、別の手段で通信内容を保護する必要があります。
ゴシップ通信の暗号化はチュートリアルに記載しているとおり、同一の共通鍵をすべてのNomadサーバの設定ファイルに記載します。
opensslで生成した32byteのBase64値を設定ファイルのencryptに指定します。
$ openssl rand -base64 32
VUeFI3llUP3EHuHxV/TEOIpMJL5EMgMYIFpaCA8j2dw=
datacenter = "dc1"
data_dir = "/opt/nomad"
...
encrypt = "VUeFI3llUP3EHuHxV/TEOIpMJL5EMgMYIFpaCA8j2dw="
...