Skip to content

Commit 44e4046

Browse files
authored
Merge pull request #598 from nextcloud/redis-tls
redis tls support
2 parents ce4af53 + 64e6db9 commit 44e4046

10 files changed

Lines changed: 820 additions & 353 deletions

File tree

Cargo.lock

Lines changed: 544 additions & 320 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ name = "notify_push"
55
version = "0.1.0" # this version number is unused, the version number for the binary will be extracted from the appinfo/info.xml during build
66
authors = ["Robin Appelman <robin@icewind.nl>"]
77
edition = "2021"
8-
rust-version = "1.77.2"
8+
rust-version = "1.81.0"
99

1010
[dependencies]
11-
redis = { version = "0.28.1", default-features = false, features = ["tokio-comp", "aio", "cluster", "cluster-async", "keep-alive"] }
11+
redis = { version = "0.30.0", default-features = false, features = ["tokio-comp", "aio", "cluster", "cluster-async", "keep-alive", "tls-rustls", "tokio-rustls-comp", "tls-rustls-webpki-roots", "tls-rustls-insecure"] }
1212
serde = { version = "1.0.217", features = ["derive"] }
1313
serde_json = "1.0.139"
1414
thiserror = "2.0.11"
@@ -29,7 +29,7 @@ rand = { version = "0.8.5", features = ["small_rng"] }
2929
ahash = "0.8.11"
3030
flexi_logger = { version = "0.29.8", features = ["colors"] }
3131
tokio-stream = { version = "0.1.17", features = ["net"] }
32-
nextcloud-config-parser = { version = "0.12.0", features = ["redis-connect"] }
32+
nextcloud-config-parser = "0.13.1"
3333
url = "2.5.4"
3434
clap = { version = "4.5.26", features = ["derive"] }
3535
sd-notify = { version = "0.4.3", optional = true }

DEVELOPING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ listen('my_message_type', (message_type, optional_body) => {
129129

130130
## Building
131131

132-
The server binary is built using rust and cargo, and requires a minimum of rust `1.77`.
132+
The server binary is built using rust and cargo, and requires a minimum of rust `1.81`.
133133

134134
- Install `rust` through your package manager or [rustup](https://rustup.rs/)
135135
- Run `cargo build`

src/config.rs

Lines changed: 132 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ use crate::{Error, Result};
1111
use clap::builder::styling::{AnsiColor, Effects};
1212
use clap::builder::Styles;
1313
use clap::Parser;
14-
use redis::ConnectionInfo;
14+
use nextcloud_config_parser::{
15+
RedisClusterConnectionInfo, RedisConfig, RedisConnectionAddr, RedisConnectionInfo,
16+
RedisTlsParams,
17+
};
18+
use redis::{ConnectionAddr, ConnectionInfo};
1519
use sqlx::any::AnyConnectOptions;
1620
use std::convert::{TryFrom, TryInto};
1721
use std::env::var;
@@ -37,6 +41,21 @@ pub struct Opt {
3741
/// The redis connect url
3842
#[clap(long)]
3943
pub redis_url: Vec<ConnectionInfo>,
44+
/// The client certificate to use when connecting to redis over TLS
45+
#[clap(long)]
46+
pub redis_tls_cert: Option<PathBuf>,
47+
/// The client key to use when connecting to redis over TLS
48+
#[clap(long)]
49+
pub redis_tls_key: Option<PathBuf>,
50+
/// The CA certificate to use when connecting to redis over TLS
51+
#[clap(long)]
52+
pub redis_tls_ca: Option<PathBuf>,
53+
/// Don't validate the server's hostname when connecting to redis over TLS
54+
#[clap(long)]
55+
pub redis_tls_dont_validate_hostname: bool,
56+
/// Don't validate the server's certificate when connecting to redis over TLS
57+
#[clap(long)]
58+
pub redis_tls_insecure: bool,
4059
/// The table prefix for Nextcloud's database tables
4160
#[clap(long)]
4261
pub database_prefix: Option<String>,
@@ -100,7 +119,7 @@ pub struct Opt {
100119
pub struct Config {
101120
pub database: AnyConnectOptions,
102121
pub database_prefix: String,
103-
pub redis: Vec<ConnectionInfo>,
122+
pub redis: RedisConfig,
104123
pub nextcloud_url: String,
105124
pub metrics_bind: Option<Bind>,
106125
pub log_level: String,
@@ -195,7 +214,7 @@ impl TryFrom<PartialConfig> for Config {
195214
database_prefix: config
196215
.database_prefix
197216
.unwrap_or_else(|| String::from("oc_")),
198-
redis: config.redis,
217+
redis: config.redis.ok_or(ConfigError::NoRedis)?,
199218
nextcloud_url,
200219
metrics_bind,
201220
log_level: config.log_level.unwrap_or_else(|| String::from("warn")),
@@ -228,7 +247,7 @@ impl Config {
228247
struct PartialConfig {
229248
pub database: Option<AnyConnectOptions>,
230249
pub database_prefix: Option<String>,
231-
pub redis: Vec<ConnectionInfo>,
250+
pub redis: Option<RedisConfig>,
232251
pub nextcloud_url: Option<String>,
233252
pub port: Option<u16>,
234253
pub metrics_port: Option<u16>,
@@ -248,7 +267,13 @@ impl PartialConfig {
248267
fn from_env() -> Result<Self> {
249268
let database = parse_var("DATABASE_URL")?;
250269
let database_prefix = var("DATABASE_PREFIX").ok();
251-
let redis = parse_var("REDIS_URL")?;
270+
let redis: Option<ConnectionInfo> = parse_var("REDIS_URL")?;
271+
let redis_tls_cert = parse_var("REDIS_TLS_CERT")?;
272+
let redis_tls_key = parse_var("REDIS_TLS_KEY")?;
273+
let redis_tls_ca = parse_var("REDIS_TLS_CA")?;
274+
let redis_tls_dont_validate_hostname: Option<u8> =
275+
parse_var("REDIS_TLS_DONT_VALIDATE_HOSTNAME")?;
276+
let redis_tls_insecure: Option<u8> = parse_var("REDIS_TLS_INSECURE")?;
252277
let nextcloud_url = var("NEXTCLOUD_URL").ok();
253278
let port = parse_var("PORT")?;
254279
let metrics_port = parse_var("METRICS_PORT")?;
@@ -271,10 +296,36 @@ impl PartialConfig {
271296
let max_debounce_time = parse_var("MAX_DEBOUNCE_TIME")?;
272297
let max_connection_time = parse_var("MAX_CONNECTION_TIME")?;
273298

299+
let redis = redis.map(|redis| {
300+
let addr = map_redis_addr(redis.addr);
301+
302+
let accept_invalid_hostname = redis_tls_dont_validate_hostname
303+
.filter(|b| *b != 0)
304+
.is_some();
305+
let redis_tls = matches!(addr, RedisConnectionAddr::Tcp { tls: true, .. });
306+
let redis_tls_insecure = redis_tls_insecure.filter(|b| *b != 0).is_some();
307+
308+
let tls_params = redis_tls.then_some(RedisTlsParams {
309+
local_cert: redis_tls_cert,
310+
local_pk: redis_tls_key,
311+
ca_file: redis_tls_ca,
312+
accept_invalid_hostname,
313+
insecure: redis_tls_insecure,
314+
});
315+
316+
RedisConfig::Single(RedisConnectionInfo {
317+
addr,
318+
db: redis.redis.db,
319+
username: redis.redis.username,
320+
password: redis.redis.password,
321+
tls_params,
322+
})
323+
});
324+
274325
Ok(PartialConfig {
275326
database,
276327
database_prefix,
277-
redis: redis.into_iter().collect(),
328+
redis,
278329
nextcloud_url,
279330
port,
280331
metrics_port,
@@ -302,10 +353,65 @@ impl PartialConfig {
302353
None
303354
};
304355

356+
let redis = match opt.redis_url.len() {
357+
0 => None,
358+
1 => {
359+
let redis = opt.redis_url.into_iter().next().unwrap();
360+
let addr = map_redis_addr(redis.addr);
361+
362+
let redis_tls = matches!(addr, RedisConnectionAddr::Tcp { tls: true, .. });
363+
364+
let tls_params = redis_tls.then_some(RedisTlsParams {
365+
local_cert: opt.redis_tls_cert,
366+
local_pk: opt.redis_tls_key,
367+
ca_file: opt.redis_tls_ca,
368+
accept_invalid_hostname: opt.redis_tls_dont_validate_hostname,
369+
insecure: opt.redis_tls_insecure,
370+
});
371+
372+
Some(RedisConfig::Single(RedisConnectionInfo {
373+
addr,
374+
db: redis.redis.db,
375+
username: redis.redis.username,
376+
password: redis.redis.password,
377+
tls_params,
378+
}))
379+
}
380+
_ => {
381+
let addr: Vec<_> = opt
382+
.redis_url
383+
.iter()
384+
.map(|redis| map_redis_addr(redis.addr.clone()))
385+
.collect();
386+
387+
let redis_tls = matches!(
388+
addr.first(),
389+
Some(RedisConnectionAddr::Tcp { tls: true, .. })
390+
);
391+
392+
let tls_params = redis_tls.then_some(RedisTlsParams {
393+
local_cert: opt.redis_tls_cert,
394+
local_pk: opt.redis_tls_key,
395+
ca_file: opt.redis_tls_ca,
396+
accept_invalid_hostname: opt.redis_tls_dont_validate_hostname,
397+
insecure: opt.redis_tls_insecure,
398+
});
399+
400+
let redis = opt.redis_url.into_iter().next().unwrap().redis;
401+
Some(RedisConfig::Cluster(RedisClusterConnectionInfo {
402+
addr,
403+
db: redis.db,
404+
username: redis.username,
405+
password: redis.password,
406+
tls_params,
407+
}))
408+
}
409+
};
410+
305411
PartialConfig {
306412
database: opt.database_url,
307413
database_prefix: opt.database_prefix,
308-
redis: opt.redis_url,
414+
redis,
309415
nextcloud_url: opt.nextcloud_url,
310416
port: opt.port,
311417
metrics_port: opt.metrics_port,
@@ -330,10 +436,10 @@ impl PartialConfig {
330436
PartialConfig {
331437
database: self.database.or(fallback.database),
332438
database_prefix: self.database_prefix.or(fallback.database_prefix),
333-
redis: if self.redis.is_empty() {
334-
fallback.redis
335-
} else {
439+
redis: if self.redis.is_some() {
336440
self.redis
441+
} else {
442+
fallback.redis
337443
},
338444
nextcloud_url: self.nextcloud_url.or(fallback.nextcloud_url),
339445
port: self.port.or(fallback.port),
@@ -363,3 +469,19 @@ where
363469
.transpose()
364470
.map_err(|e| ConfigError::Env(name, Box::new(e)).into())
365471
}
472+
473+
fn map_redis_addr(addr: ConnectionAddr) -> RedisConnectionAddr {
474+
match addr {
475+
ConnectionAddr::Tcp(host, port) => RedisConnectionAddr::Tcp {
476+
host,
477+
port,
478+
tls: false,
479+
},
480+
ConnectionAddr::TcpTls { host, port, .. } => RedisConnectionAddr::Tcp {
481+
host,
482+
port,
483+
tls: true,
484+
},
485+
ConnectionAddr::Unix(path) => RedisConnectionAddr::Unix { path },
486+
}
487+
}

src/config/nc.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
5-
5+
66
use crate::config::PartialConfig;
77
use crate::error::ConfigError;
88
use nextcloud_config_parser::{parse, parse_glob};
@@ -18,7 +18,7 @@ pub(super) fn parse_config_file(
1818
database: Some(config.database.url().parse()?),
1919
database_prefix: Some(config.database_prefix),
2020
nextcloud_url: Some(config.nextcloud_url),
21-
redis: config.redis.into_vec(),
21+
redis: Some(config.redis),
2222
..PartialConfig::default()
2323
})
2424
}

src/event.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
5-
5+
66
use crate::metrics::METRICS;
77
use crate::{Redis, Result, UserId};
88
use parse_display::Display;

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
5-
5+
66
use crate::config::{Bind, Config, TlsConfig};
77
use crate::connection::{handle_user_socket, ActiveConnections, ConnectionOptions};
88
pub use crate::error::Error;

src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
5-
5+
66
use clap::Parser;
77
use flexi_logger::{detailed_format, AdaptiveFormat, Logger, LoggerHandle};
88
use miette::{IntoDiagnostic, Result, WrapErr};

0 commit comments

Comments
 (0)