Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/actions/run-tests/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ runs:
- name: Set up Docker Compose environment with redis ${{ inputs.redis-version }}
run: |
make docker.start
sleep 5
shell: bash
- name: Run tests
env:
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/test-redis-enterprise.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,12 @@ jobs:
REDIS_VERSION: "7.4"
run: |
go test \
-skip="^TestTLS" \
--ginkgo.skip-file="ring_test.go" \
--ginkgo.skip-file="sentinel_test.go" \
--ginkgo.skip-file="osscluster_test.go" \
--ginkgo.skip-file="pubsub_test.go" \
--ginkgo.skip-file="tls_test.go" \
--ginkgo.skip-file="tls_cluster_test.go" \
--ginkgo.skip-file="tls_sentinel_test.go" \
Comment thread
ndyakov marked this conversation as resolved.
Outdated
--ginkgo.label-filter='!NonRedisEnterprise'
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ tmp/*

# maintenanceNotifications upgrade documentation (temporary)
maintenanceNotifications/docs/

# Docker-generated files (TLS certificates, cluster data, etc.)
dockers/*/tls/
dockers/osscluster-tls/
22 changes: 22 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,28 @@ services:
- all-stack
- all

osscluster-tls:
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:8.4.0}
Comment thread
elena-kolevska marked this conversation as resolved.
Outdated
platform: linux/amd64
container_name: redis-osscluster-tls
environment:
- NODES=6
- PORT=6430
- TLS_PORT=5430
- TLS_ENABLED=yes
- REDIS_CLUSTER=yes
- REPLICAS=1
command: "--tls-auth-clients optional --cluster-announce-ip 127.0.0.1"
ports:
- "6430-6435:6430-6435" # Regular ports
- "5430-5435:5430-5435" # TLS ports (set via TLS_PORT env var)
- "16430-16435:16430-16435" # Cluster bus ports (PORT + 10000)
volumes:
- "./dockers/osscluster-tls:/redis/work"
profiles:
- cluster-tls
- all

sentinel-cluster:
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:custom-21183968220-debian-amd64}
platform: linux/amd64
Expand Down
117 changes: 117 additions & 0 deletions example/tls-connection/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# TLS Connection Examples

Shows different ways to connect to Redis over TLS.

## Running

Start Redis with TLS:

```shell
cd ../..
docker compose --profile standalone up -d
```

Then run the example:

```shell
go run .
```

## Connection Methods

### 1. InsecureSkipVerify (for testing)

Quick way to test with self-signed certs:

```go
client := redis.NewClient(&redis.Options{
Addr: "localhost:6666",
TLSConfig: &tls.Config{
InsecureSkipVerify: true,
},
})
```

Don't use this in production.

### 2. With CA certificate

Proper way for production:

```go
caCert, _ := os.ReadFile("path/to/ca.crt")
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)

client := redis.NewClient(&redis.Options{
Addr: "localhost:6666",
TLSConfig: &tls.Config{
RootCAs: caCertPool,
ServerName: "localhost",
},
})
```

### 3. Mutual TLS

If Redis requires client certs:

```go
caCert, _ := os.ReadFile("path/to/ca.crt")
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)

cert, _ := tls.LoadX509KeyPair("path/to/client.crt", "path/to/client.key")

client := redis.NewClient(&redis.Options{
Addr: "localhost:6666",
TLSConfig: &tls.Config{
RootCAs: caCertPool,
Certificates: []tls.Certificate{cert},
ServerName: "localhost",
},
})
```

### 4. Using rediss:// URL

```go
opt, _ := redis.ParseURL("rediss://localhost:6666")
opt.TLSConfig = &tls.Config{
InsecureSkipVerify: true, // for testing only
}
client := redis.NewClient(opt)
```

### 5. Certificate-based auth

Redis 6.2+ can authenticate users based on the certificate CN field. You need to configure Redis with:

```
tls-auth-clients optional
tls-auth-clients-user CN
```

Then the CN in your client cert becomes your username - no password needed.

Check `../../tls_cert_auth_test.go` for a working example that:
- Generates a client cert with a specific CN
- Connects to Redis with that cert
- Verifies auth based on the CN

Note: Current Redis test build doesn't support this yet, so the test skips gracefully.

## Tests

Run the TLS tests:

```shell
go test -v -run "^TestTLS" -timeout 30s
```

## Notes

- Always verify certs in production (don't use InsecureSkipVerify)
- Keep your private keys safe
- Use TLS 1.2 or higher

13 changes: 13 additions & 0 deletions example/tls-connection/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module github.com/redis/go-redis/example/tls-connection

go 1.21

replace github.com/redis/go-redis/v9 => ../..

require github.com/redis/go-redis/v9 v9.18.0-beta.2

require (
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
go.uber.org/atomic v1.11.0 // indirect
)
16 changes: 16 additions & 0 deletions example/tls-connection/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
124 changes: 124 additions & 0 deletions example/tls-connection/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package main

import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"os"

"github.com/redis/go-redis/v9"
)

func main() {
ctx := context.Background()

// Example 1: TLS with InsecureSkipVerify (for testing with self-signed certs)
fmt.Println("Example 1: TLS with InsecureSkipVerify")
client1 := redis.NewClient(&redis.Options{
Addr: "localhost:6666", // TLS port
TLSConfig: &tls.Config{
InsecureSkipVerify: true,
Comment thread
ndyakov marked this conversation as resolved.
Dismissed
},
})
defer client1.Close()

if err := client1.Ping(ctx).Err(); err != nil {
fmt.Printf("Failed to connect: %v\n", err)
} else {
fmt.Println("✅ Connected successfully with InsecureSkipVerify")
}

// Example 2: TLS with CA certificate verification
fmt.Println("\nExample 2: TLS with CA certificate verification")

// Load CA certificate
caCert, err := os.ReadFile("path/to/ca.crt")
if err != nil {
fmt.Printf("Note: CA cert not found (this is expected in this example): %v\n", err)
} else {
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)

client2 := redis.NewClient(&redis.Options{
Addr: "localhost:6666",
TLSConfig: &tls.Config{
RootCAs: caCertPool,
ServerName: "localhost",
},
})
defer client2.Close()

if err := client2.Ping(ctx).Err(); err != nil {
fmt.Printf("Failed to connect: %v\n", err)
} else {
fmt.Println("✅ Connected successfully with CA verification")
}
}

// Example 3: TLS with client certificate (mutual TLS)
fmt.Println("\nExample 3: TLS with client certificate (mutual TLS)")

// Load CA certificate
caCert, err = os.ReadFile("path/to/ca.crt")
if err != nil {
fmt.Printf("Note: CA cert not found (this is expected in this example): %v\n", err)
} else {
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)

// Load client certificate and key
cert, err := tls.LoadX509KeyPair("path/to/client.crt", "path/to/client.key")
if err != nil {
fmt.Printf("Note: Client cert not found (this is expected in this example): %v\n", err)
} else {
client3 := redis.NewClient(&redis.Options{
Addr: "localhost:6666",
TLSConfig: &tls.Config{
RootCAs: caCertPool,
Certificates: []tls.Certificate{cert},
ServerName: "localhost",
},
})
defer client3.Close()

if err := client3.Ping(ctx).Err(); err != nil {
fmt.Printf("Failed to connect: %v\n", err)
} else {
fmt.Println("✅ Connected successfully with client certificate")
}
}
}

// Example 4: Using rediss:// URL scheme
fmt.Println("\nExample 4: Using rediss:// URL scheme")

opt, err := redis.ParseURL("rediss://localhost:6666")
if err != nil {
fmt.Printf("Failed to parse URL: %v\n", err)
return
}

// Add InsecureSkipVerify for testing with self-signed certs
opt.TLSConfig = &tls.Config{
InsecureSkipVerify: true,
Comment thread
ndyakov marked this conversation as resolved.
Dismissed
}

client4 := redis.NewClient(opt)
defer client4.Close()

if err := client4.Ping(ctx).Err(); err != nil {
fmt.Printf("Failed to connect: %v\n", err)
} else {
fmt.Println("✅ Connected successfully using rediss:// URL")
}

// Example 5: TLS with certificate-based authentication (future feature)
// This demonstrates how to use client certificates for authentication
// when Redis is configured with: tls-auth-clients-user CN
fmt.Println("\nExample 5: TLS with certificate-based authentication")
fmt.Println("Note: This requires Redis 6.2+ with tls-auth-clients-user CN configuration")
fmt.Println("The certificate's CN (Common Name) field will be used as the Redis username")
fmt.Println("See tls_cert_auth_test.go for a complete working example")
}

Loading
Loading