---
title: TraefikのTCP over TLSを使ってRedisにSNIでアクセスするメモ
summary: この記事では、TraefikのTCP over TLSとSNIを使い、RedisへTLS接続する手順と設定例、Spring Bootからの利用方法を紹介します。
tags: ["Traefik", "Kubernetes", "Redis", "Spring Boot", "Spring Data Redis", "Bitnami", "Helm"]
categories: ["Dev", "CaaS", "Kubernetes", "Traefik"]
date: 2026-01-05T02:53:41Z
updated: 2026-01-05T02:53:41Z
---

TraefikのTCP over TLS機能を使ってRedisにSNI経由でアクセスしてみます。

Traefikは基本的にはHTTP/HTTPSのリバースプロキシですが、TCP Proxyingもサポートしています。TCP Proxyingは、RedisのようなTCPベースのサービスに対しても使えます。
普通にTCPのサービスをtype: LoadBalancerで公開してもいいのですが、TCP over TLS with SNIを使うと次の利点があります。

* TLSのSNIを使って複数のサービスを同じTraefikポートで公開できる (追加でExternal IPを割り当てる必要がない)
* TraefikでTLSの終端を行うので、RedisのようなサービスにTLSを設定する必要がない
* 必要であれば、Traefikを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    <none>           <none>
pod/redis-replicas-0   1/1     Running   0          3m28s   10.1.173.164   cherry    <none>           <none>
pod/redis-replicas-1   1/1     Running   0          3m      10.1.42.164    banana    <none>           <none>
pod/redis-replicas-2   1/1     Running   0          2m33s   10.1.247.56    apricot   <none>           <none>

NAME                     TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE     SELECTOR
service/redis-headless   ClusterIP   None             <none>        6379/TCP   3m28s   app.kubernetes.io/instance=redis,app.kubernetes.io/name=redis
service/redis-master     ClusterIP   10.152.183.167   <none>        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    <none>        6379/TCP   3m28s   app.kubernetes.io/component=replica,app.kubernetes.io/instance=redis,app.kubernetes.io/name=redis
```

### TLS証明書の作成

cert-managerを使ってTLS証明書を作成します。

```yaml
cat <<EOF > /tmp/redis-certificate.yaml
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: redis-tls
  namespace: redis
spec:
  secretName: redis-tls-cert
  dnsNames:
    - redis.lan.ik.am
  issuerRef:
    name: letsencrypt  # Use your issuer name
    kind: ClusterIssuer
---
EOF

kubectl apply -f /tmp/redis-certificate.yaml
```

証明書が発行されたことを確認します。

```bash
$ kubectl get certificate -n redis
NAME        READY   SECRET           AGE
redis-tls   True    redis-tls-cert   30s
```

### TraefikのIngressRouteTCPを作成

次に、TraefikのIngressRouteTCPを使ってRedisのTCP Proxyingを設定します。

```yaml
cat <<EOF > /tmp/redis-sni.yaml
---
apiVersion: traefik.io/v1alpha1
kind: IngressRouteTCP
metadata:
  name: redis
  namespace: redis
spec:
  entryPoints:
    - websecure  # Traefik's 443 port entry point
  routes:
    - match: HostSNI(\`redis.lan.ik.am\`)
      services:
        - name: redis-master
          port: 6379
  tls:
    secretName: redis-tls-cert
---
EOF

kubectl apply -f /tmp/redis-sni.yaml
```

IngressRouteTCPが作成されたことを確認します。

```bash
$ kubectl get ingressroutetcp -n redis
NAME    AGE
redis   10s
```

TraefikのExternal IPを確認します。ここでは、`traefik` namespaceにデプロイされているTraefikのLoadBalancer Serviceを使います。

```bash
$ kubectl get svc -n traefik
NAME      TYPE           CLUSTER-IP       EXTERNAL-IP      PORT(S)                      AGE
traefik   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: certificate verify failed
```

`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`オプションにTraefikの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
```

### TLS Passthroughを使う場合

TraefikをTLS終端にせず、TLS PassthroughでRedis側でTLSを処理したい場合は、次のように設定します。

```yaml
cat <<EOF > /tmp/redis-sni-passthrough.yaml
---
apiVersion: traefik.io/v1alpha1
kind: IngressRouteTCP
metadata:
  name: redis
  namespace: redis
spec:
  entryPoints:
    - websecure  # Traefik's 443 port entry point
  routes:
    - match: HostSNI(\`redis.lan.ik.am\`)
      services:
        - name: redis-master
          port: 6379
  tls:
    passthrough: true  # Enable TLS passthrough
---
EOF

kubectl apply -f /tmp/redis-sni-passthrough.yaml
```

この場合、Redis側でTLSの設定が必要になります。

---

TraefikのTCP over TLSを使うことで、RedisのようなTCPサービスに対してもSNIを利用したTLS接続が可能になります。
これにより、複数のサービスを同じポートで公開できるため、リソースの効率的な利用が可能になります。
