diff --git a/.github/workflows/service_test_redis.yml b/.github/workflows/service_test_redis.yml index d6a891839a0c..d7de6a340933 100644 --- a/.github/workflows/service_test_redis.yml +++ b/.github/workflows/service_test_redis.yml @@ -60,6 +60,88 @@ jobs: OPENDAL_REDIS_ROOT: / OPENDAL_REDIS_DB: 0 + redis-tls: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Configure Redis with TLS + run: | + mkdir ssl + + # Create CA + + openssl req \ + -x509 -new -nodes \ + -keyout ssl/ca.key \ + -sha256 \ + -days 365 \ + -out ssl/ca.crt \ + -subj '/CN=Test Root CA/C=US/ST=Test/L=Test/O=Opendal' + + # Create redis certificate + + openssl req \ + -new -nodes \ + -out ssl/redis.csr \ + -keyout ssl/redis.key \ + -subj '/CN=Redis certificate/C=US/ST=Test/L=Test/O=Opendal' + + cat > ssl/redis.v3.ext << EOF + authorityKeyIdentifier=keyid,issuer + basicConstraints=CA:FALSE + keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment + subjectAltName = @alt_names + [alt_names] + DNS.1 = localhost + IP.1 = 127.0.0.1 + EOF + + openssl x509 \ + -req \ + -in ssl/redis.csr \ + -CA ssl/ca.crt \ + -CAkey ssl/ca.key \ + -CAcreateserial \ + -out ssl/redis.crt \ + -days 300 \ + -sha256 \ + -extfile ssl/redis.v3.ext + + chmod 777 ssl/redis.crt ssl/redis.key # allow the redis docker to read these files + + # Launch redis + + docker run -d \ + --rm \ + --name redis \ + --network host \ + --mount type=bind,source=$PWD/ssl,target=/etc/redis/ssl \ + redis \ + --tls-port 6380 \ + --tls-cert-file /etc/redis/ssl/redis.crt \ + --tls-key-file /etc/redis/ssl/redis.key \ + --tls-auth-clients no + + # Install the CA in the system + + sudo cp ssl/ca.crt /usr/local/share/ca-certificates + sudo update-ca-certificates + + - name: Setup Rust toolchain + uses: ./.github/actions/setup + with: + need-nextest: true + - name: Test + shell: bash + working-directory: core + run: cargo nextest run redis --features services-redis-rustls + env: + OPENDAL_REDIS_TEST: on + OPENDAL_REDIS_ENDPOINT: rediss://localhost:6380 + OPENDAL_REDIS_ROOT: / + OPENDAL_REDIS_DB: 0 + dragonfly: runs-on: ubuntu-latest services: diff --git a/Cargo.lock b/Cargo.lock index 1e1e8b5967db..259e164d3c16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4110,11 +4110,16 @@ dependencies = [ "futures", "futures-util", "itoa", + "native-tls", "percent-encoding", "pin-project-lite", + "rustls 0.21.2", + "rustls-native-certs", "ryu", "sha1_smol", "tokio", + "tokio-native-tls", + "tokio-rustls", "tokio-util", "url", ] diff --git a/core/Cargo.toml b/core/Cargo.toml index cb04ade5fa04..a813b9f9f9b9 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -144,6 +144,8 @@ services-oss = [ services-persy = ["dep:persy"] services-redb = ["dep:redb"] services-redis = ["dep:redis"] +services-redis-rustls = ["dep:redis", "redis?/tokio-rustls-comp"] +services-redis-native-tls = ["dep:redis", "redis?/tokio-native-tls-comp"] services-rocksdb = ["dep:rocksdb"] services-s3 = [ "dep:reqsign", diff --git a/core/benches/ops/utils.rs b/core/benches/ops/utils.rs index f8d9af2d7da0..fc2f0925f45f 100644 --- a/core/benches/ops/utils.rs +++ b/core/benches/ops/utils.rs @@ -58,7 +58,11 @@ pub fn services() -> Vec<(&'static str, Option)> { ("mini-moka", service::()), #[cfg(feature = "services-moka")] ("moka", service::()), - #[cfg(feature = "services-redis")] + #[cfg(any( + feature = "services-redis", + feature = "services-redis-rustls", + feature = "services-redis-native-tls" + ))] ("redis", service::()), ] } diff --git a/core/src/docs/features.md b/core/src/docs/features.md index f3633a036dfc..5770bf4b9c6e 100644 --- a/core/src/docs/features.md +++ b/core/src/docs/features.md @@ -15,7 +15,9 @@ - `services-mini-moka`: Enable mini-moka service support. - `services-moka`: Enable moka service support. - `services-ipfs`: Enable ipfs service support. -- `services-redis`: Enable redis service support. +- `services-redis`: Enable redis service support without TLS. +- `services-redis-rustls`: Enable redis service support with `rustls`. +- `services-redis-native-tls`: Enable redis service support with `native-tls`. - `services-rocksdb`: Enable rocksdb service support. - `services-sled`: Enable sled service support. diff --git a/core/src/services/mod.rs b/core/src/services/mod.rs index b484fcc301f1..bf80c18419f9 100644 --- a/core/src/services/mod.rs +++ b/core/src/services/mod.rs @@ -119,9 +119,17 @@ mod persy; #[cfg(feature = "services-persy")] pub use self::persy::Persy; -#[cfg(feature = "services-redis")] +#[cfg(any( + feature = "services-redis", + feature = "services-redis-rustls", + feature = "services-redis-native-tls" +))] mod redis; -#[cfg(feature = "services-redis")] +#[cfg(any( + feature = "services-redis", + feature = "services-redis-rustls", + feature = "services-redis-native-tls" +))] pub use self::redis::Redis; #[cfg(feature = "services-rocksdb")] diff --git a/core/src/services/redis/backend.rs b/core/src/services/redis/backend.rs index e1fde6fdfb79..87a1da06b8d5 100644 --- a/core/src/services/redis/backend.rs +++ b/core/src/services/redis/backend.rs @@ -185,7 +185,18 @@ impl Builder for RedisBuilder { let port = ep_url.port_u16().unwrap_or(DEFAULT_REDIS_PORT); ConnectionAddr::Tcp(host, port) } - // TODO: wait for upstream to support `rustls` based TLS connection. + Some("rediss") => { + let host = ep_url + .host() + .map(|h| h.to_string()) + .unwrap_or_else(|| "127.0.0.1".to_string()); + let port = ep_url.port_u16().unwrap_or(DEFAULT_REDIS_PORT); + ConnectionAddr::TcpTls { + host, + port, + insecure: false, + } + } Some("unix") | Some("redis+unix") => { let path = PathBuf::from(ep_url.path()); ConnectionAddr::Unix(path) diff --git a/core/src/types/operator/builder.rs b/core/src/types/operator/builder.rs index 3f78d3cedf7f..899726424ddd 100644 --- a/core/src/types/operator/builder.rs +++ b/core/src/types/operator/builder.rs @@ -197,7 +197,11 @@ impl Operator { Scheme::Oss => Self::from_map::(map)?.finish(), #[cfg(feature = "services-persy")] Scheme::Persy => Self::from_map::(map)?.finish(), - #[cfg(feature = "services-redis")] + #[cfg(any( + feature = "services-redis", + feature = "services-redis-rustls", + feature = "services-redis-native-tls" + ))] Scheme::Redis => Self::from_map::(map)?.finish(), #[cfg(feature = "services-rocksdb")] Scheme::Rocksdb => Self::from_map::(map)?.finish(), diff --git a/core/tests/behavior/main.rs b/core/tests/behavior/main.rs index 055124d29748..7146806789ed 100644 --- a/core/tests/behavior/main.rs +++ b/core/tests/behavior/main.rs @@ -139,7 +139,11 @@ fn main() -> anyhow::Result<()> { tests.extend(behavior_test::()); #[cfg(feature = "services-persy")] tests.extend(behavior_test::()); - #[cfg(feature = "services-redis")] + #[cfg(any( + feature = "services-redis", + feature = "services-redis-rustls", + feature = "services-redis-native-tls" + ))] tests.extend(behavior_test::()); #[cfg(feature = "services-rocksdb")] tests.extend(behavior_test::());