--- title: ContourのTLS Session Proxyingを使ってRedisにSNIでアクセスするメモ tags: ["Contour", "Kubernetes", "Redis", "Spring Boot", "Spring Data Redis", "Bitnami", "Helm"] categories: ["Dev", "CaaS", "Kubernetes", "Contour"] date: 2025-06-25T04:11:41Z updated: 2025-06-25T04:13:40Z --- Contourの隠れた機能である[TLS Session Proxying](https://projectcontour.io/docs/1.32/config/tls-termination/#tls-session-proxying)を使ってみます。 Googleで検索しても使用例が出てこなかったので、メモを残しておきます。 Contourは基本的にはHTTP/HTTPSのリバースプロキシですが、TCP Proxyingもサポートしています。TCP Proxyingは、RedisのようなTCPベースのサービスに対しても使えます。 普通にTCPのサービスをtype: LoadBalancerで公開してもいいのですが、TLS Session Proxyingを使うと次の利点があります。 * TLSのSNIを使って複数のサービスを同じEnvoyポートで公開できる (追加でExternal IPを割り当てる必要がない) * EnvoyでTLSの終端を行うので、RedisのようなサービスにTLSを設定する必要がない * 必要であれば、Upstream TLSを有効にして、EnvoyからRedisへの通信もTLS化できるし、EnvoyをTLS終端にせずTLS Passthroughもできる ### RedisをKubernetesにデプロイ まずは、TCPサービスの例としてRedisをBitnamiのHelmでデプロイします。今回は、永続化を無効にして、認証も無効にしています。 ```bash helm upgrade --install redis \ oci://registry-1.docker.io/bitnamicharts/redis \ -n redis \ --create-namespace --wait \ --set master.persistence.enabled=false \ --set replica.persistence.enabled=false \ --set auth.enabled=false ``` PodとServiceが作成されていることを確認します。 ```bash $ kubectl get pod,svc -n redis -owide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod/redis-master-0 1/1 Running 0 3m28s 10.1.173.163 cherry pod/redis-replicas-0 1/1 Running 0 3m28s 10.1.173.164 cherry pod/redis-replicas-1 1/1 Running 0 3m 10.1.42.164 banana pod/redis-replicas-2 1/1 Running 0 2m33s 10.1.247.56 apricot NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR service/redis-headless ClusterIP None 6379/TCP 3m28s app.kubernetes.io/instance=redis,app.kubernetes.io/name=redis service/redis-master ClusterIP 10.152.183.167 6379/TCP 3m28s app.kubernetes.io/component=master,app.kubernetes.io/instance=redis,app.kubernetes.io/name=redis service/redis-replicas ClusterIP 10.152.183.48 6379/TCP 3m28s app.kubernetes.io/component=replica,app.kubernetes.io/instance=redis,app.kubernetes.io/name=redis ``` 次に、ContourのHTTPProxyを使ってRedisのTCP Proxyingを設定します。 > [!NOTE] > この例では`projectcontoure` namespaceに作成されたCertificate`default-tls` (Let's Encryptによるワイルドカード証明書)が[TLSCertificateDelegation](https://projectcontour.io/docs/1.32/config/tls-delegation/#tls-certificate-delegation)で公開されている前提です。環境に応じて適切な証明書設定を行ってください。 ```yaml cat < /tmp/redis-sni.yaml --- apiVersion: projectcontour.io/v1 kind: HTTPProxy metadata: name: redis namespace: redis spec: virtualhost: fqdn: redis.lan.ik.am tls: secretName: projectcontour/default-tls tcpproxy: services: - name: redis-master port: 6379 --- EOF kubectl apply -f /tmp/redis-sni.yaml ``` HTTPProxyが作成されたことを確認します。STATUSが`valid`になっていれば成功です。 ```bash $ kubectl get httpproxy -n redis NAME FQDN TLS SECRET STATUS STATUS DESCRIPTION redis redis.lan.ik.am projectcontour/default-tls valid Valid HTTPProxy ``` EnvoyのExternal IPを確認します。ここでは、`projectcontour` namespaceにデプロイされているContourのLoadBalancer Serviceを使います。 ```bash $ kubectl get svc -n projectcontour NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE contour ClusterIP 10.152.183.197 8001/TCP 24h contour-envoy LoadBalancer 10.152.183.23 192.168.11.240 80:31496/TCP,443:31967/TCP 24h ``` `redis.lan.ik.am`は`192.168.11.240`に解決されるようにDNS設定されています。 ### redis-cliでRedisに接続 次のコマンドで、Redisにアクセスを試みます。 ```bash redis-cli -h redis.lan.ik.am -p 443 --tls ``` 次のエラーが出力されます。 ``` Could not connect to Redis at redis.lan.ik.am:443: SSL_connect failed: Connection reset by peer ``` `redis-cli`はデフォルトではTLSのSNIをサポートしていないため、接続できません。そこで、`--sni`オプションを使ってSNIを指定します。 ```bash redis-cli -h redis.lan.ik.am -p 443 --tls --sni redis.lan.ik.am ``` 今度は接続でき、Key-Valueの読み書きができます。 ```bash redis.lan.ik.am:443> set foo 100 OK redis.lan.ik.am:443> get foo "100" ``` 次のように`-h`オプションにEnvoyのExternal IPを指定しても同じように接続できます。 ```bash redis-cli -h 192.168.11.240 -p 443 --tls --sni redis.lan.ik.am ``` ### Spring Boot (Spring Data Redis)アプリケーションからRedisに接続 次にSpring BootアプリケーションからRedisに接続してみます。ここでは、Spring Data Redisを使った[簡単なデモアプリケーション](https://github.com/making/demo-redis)を使用します。 次のコマンドでアプリケーションのソースコードを取得し、ビルドします。 ```bash git clone https://github.com/making/demo-redis cd demo-redis ./mvnw clean package -DskipTests ``` 次のコマンドでアプリケーションを起動します。Redisのホスト名とポートを指定して、TLSを有効にします。Spring Data Redis(というよりLettuce?)に`redis-cli`の`--sni`オプションに相当するプロパティはなく、 TLSを有効にすれば、 接続先のホスト名でSNIを自動的に設定するため、特にSNIの指定は必要ありません。逆に接続先にIPアドレスを指定すると、SNIが設定されずに接続できません。 ```bash java -jar target/demo-redis-0.0.1-SNAPSHOT.jar --spring.data.redis.host=redis.lan.ik.am --spring.data.redis.port=443 --spring.data.redis.ssl.enabled=true ``` 次のコマンドで、アプリケーションが機能していることを確認します。 ```bash $ curl "http://localhost:8080?key=foo" 100 $ curl "http://localhost:8080?key=foo" -H "Content-Type: text/plain" -d Hello $ curl "http://localhost:8080?key=foo" Hello ``` --- ContourのTLS Session Proxyingを使うことで、RedisのようなTCPサービスに対してもSNIを利用したTLS接続が可能になります。 これにより、複数のサービスを同じポートで公開できるため、リソースの効率的な利用が可能になります。