Kubernetesアプリを独自ドメインでTailnet内だけに公開する(SSL対応)
自宅鯖をお持ちの皆さんこんにちは!
サーバーで動かしているサービス、自宅の外からも接続したいですよね
ただ、サービスをインターネットに公開することには一定のセキュリティリスクが伴い、自分だけがアクセスするのであればインターネットの全員が到達できる場所にある必要はありません。
ここではVPNを通したクライアントだけが接続できるように、Tailscaleを使って公開することを考えます。
*.ts.net
は使えますが、ホスト名の前に文字列がつくので覚えにくいです(tailscale: Generate a fun tailnet name)
せっかく独自ドメインを持っているなら、それを使ってわかりやすい名前にしたいですよね
やりたいこと
-
xxx.example.com
で外出先からでもサービスが閲覧できるようにする - tailscaleを有効化したクライアントのみがアクセス可能
前提
- Kubernetesを運用しており、IngressやServiceなどについての知識がある程度ある
- DNSを自由に設定できるドメインを持っている(今回はCloudflareの前提で説明します)
構成図
はじめに、本解説の構成図を示します
Cloudflare API Token の取得
external-dns (Ingressの設定を読み取ってDNS設定) と cert-manager (DNS-01 チャレンジを行い証明書を取得) のためにCloudflare API Tokenを取得しておきます
これを見て取得しましょう今回の用途であれば、
- ゾーン / DNS / 読み取り
- ゾーン / DNS / 編集
の権限があれば問題ないです。ゾーンリソースについては設定したいドメインを指定しましょう。
トークンが取得できたらメモをしておいて、後で使います
前提ミドルウェアのデプロイ
以下4つのミドルウェアを使用します
- Tailscale Operator
- cert-manager
- ExternalDNS
- ingress-nginx
Tailscale Operator
loadBalancerClass: tailscale
で正常に 100.x.x.x
がバインドされたServiceが生成されればよいです
cert-manager
helmでインストールする場合、values.yamlは以下のようにしましょう
crds:
enabled: true
DNS-01チャレンジを通す
みんな大好きLet's Encryptで証明書を取得しますが、tailnet内はインターネットからはアクセスできません。なのでドメイン単位で証明書を取得できるDNS-01チャレンジを使用します。
また、namespaceの関係上ここでは ClusterIssuer
の作成のみ行い、 Certificate
は nginx-ingress namespaceの方で作成します
cert-manager namespaceに cloudflare-api-token-secret
の名前でSecretを作っておきます
先ほどメモっておいたCloudflare API Tokenを使います
$ token=xxxxx
$ kubectl create secret generic cloudflare-api-token-secret -n cert-manager --from-literal=api-token=$token
ClusterIssuer を作成します
ingress-nginxと同じnamespaceを使用するのであれば Issuerでも構いません
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: example-com-issuer
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: example-com-issuer-account-key
solvers:
- dns01:
cloudflare:
apiTokenSecretRef:
name: cloudflare-api-token-secret
key: api-token
ExternalDNS
external-dns namespaceに cloudflare-api-token-secret
の名前でSecretを作っておきます
先ほどメモっておいたCloudflare API Tokenを使います(namespaceが異なる場合、ここでもSecretを新規に作成する必要があります)
$ token=xxxxx
$ kubectl create secret generic cloudflare-api-token-secret -n external-dns --from-literal=api-token=$token
helmでデプロイする場合、valuesは以下のようにしてください
provider: cloudflare
policy: sync
env:
- name: CF_API_TOKEN
valueFrom:
secretKeyRef:
name: cloudflare-api-token-secret
key: api-token
ingress-nginx
Ingress Controllerである Ingress NGINX Controller をクラスタに展開します
helmでデプロイする場合、valuesは以下のようにしてください
controller:
service:
loadBalancerClass: tailscale
extraArgs:
default-ssl-certificate: "ingress-nginx/wildcard-example-com-tls"
service の loadBalancerClass: tailscale
を設定することで、ingress-nginx podの前段にtailscaleのIPがついたServiceができます
また、Certificateを作成しておきます
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: wildcard-example-com
namespace: ingress-nginx
spec:
secretName: wildcard-example-com-tls
issuerRef:
name: example-com-issuer
kind: ClusterIssuer
dnsNames:
- "*.example.com"
これで、 *.example.com
のワイルドカード証明書が取得できました。
サービスをデプロイする
ここまで来たら、準備は完了です
以下のマニフェストをデプロイすることでtailnet内のみからアクセスできるhttpsサービスができます
apiVersion: apps/v1
kind: Deployment
metadata:
name: sample
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: sample
template:
metadata:
labels:
app: sample
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
namespace: default
spec:
selector:
app: sample
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ingress
namespace: default
spec:
ingressClassName: nginx
tls:
- hosts:
- nginx.example.com
rules:
- host: nginx.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-service
port:
number: 80
$ nslookup nginx.example.com
Server: 10.255.255.254
Address: 10.255.255.254#53
Non-authoritative answer:
Name: nginx.example.com
Address: 100.123.456.789
Address
がtailnetのIPとなっていることがわかります。
このように、tailscaleを接続した状態でnginxのwelcome画面が出たら成功です
念の為tailscaleを切断した状態でアクセスできないことも確認しておきましょう
大丈夫そうですね
IngressのTLS設定の補足
上記のマニフェスト、主にIngressの補足説明を少しだけします
nginx-ingressではtls.hostsにsecret設定をしない場合デフォルトのワイルドカード証明書が利用されます(参考:公式ドキュメント)
そのため、ingress-nginxで指定した default-ssl-certificate
の設定が使われます
Discussion