From 5feb6a1f64aaede10f8172c37645da18a96b419c Mon Sep 17 00:00:00 2001 From: networkException Date: Tue, 3 Oct 2023 17:49:30 +0200 Subject: [PATCH 1/4] docs: add documentation for specifying token / secret file as environment variables --- doc/book/reference-manual/configuration.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/doc/book/reference-manual/configuration.md b/doc/book/reference-manual/configuration.md index 3110aab7..2a8c5df5 100644 --- a/doc/book/reference-manual/configuration.md +++ b/doc/book/reference-manual/configuration.md @@ -276,7 +276,7 @@ Compression is done synchronously, setting a value too high will add latency to This value can be different between nodes, compression is done by the node which receive the API call. -### `rpc_secret`, `rpc_secret_file` or `GARAGE_RPC_SECRET` (env) +### `rpc_secret`, `rpc_secret_file` or `GARAGE_RPC_SECRET`, `GARAGE_RPC_SECRET_FILE` (env) Garage uses a secret key, called an RPC secret, that is shared between all nodes of the cluster in order to identify these nodes and allow them to @@ -288,6 +288,9 @@ Since Garage `v0.8.2`, the RPC secret can also be stored in a file whose path is given in the configuration variable `rpc_secret_file`, or specified as an environment variable `GARAGE_RPC_SECRET`. +Since Garage `v0.9.0`, you can also specify the path of a file storing the secret +as the `GARAGE_RPC_SECRET_FILE` environment variable. + ### `rpc_bind_addr` The address and port on which to bind for inter-cluster communcations @@ -465,7 +468,7 @@ See [administration API reference](@/documentation/reference-manual/admin-api.md Alternatively, since `v0.8.5`, a path can be used to create a unix socket. Note that for security reasons, the socket will have 0220 mode. Make sure to set user and group permissions accordingly. -### `metrics_token`, `metrics_token_file` or `GARAGE_METRICS_TOKEN` (env) +### `metrics_token`, `metrics_token_file` or `GARAGE_METRICS_TOKEN`, `GARAGE_METRICS_TOKEN_FILE` (env) The token for accessing the Metrics endpoint. If this token is not set, the Metrics endpoint can be accessed without access control. @@ -475,8 +478,9 @@ You can use any random string for this value. We recommend generating a random t `metrics_token` was introduced in Garage `v0.7.2`. `metrics_token_file` and the `GARAGE_METRICS_TOKEN` environment variable are supported since Garage `v0.8.2`. +`GARAGE_METRICS_TOKEN_FILE` is supported since `v0.9.0`. -### `admin_token`, `admin_token_file` or `GARAGE_ADMIN_TOKEN` (env) +### `admin_token`, `admin_token_file` or `GARAGE_ADMIN_TOKEN`, `GARAGE_ADMIN_TOKEN_FILE` (env) The token for accessing all of the other administration endpoints. If this token is not set, access to these endpoints is disabled entirely. @@ -486,6 +490,7 @@ You can use any random string for this value. We recommend generating a random t `admin_token` was introduced in Garage `v0.7.2`. `admin_token_file` and the `GARAGE_ADMIN_TOKEN` environment variable are supported since Garage `v0.8.2`. +`GARAGE_ADMIN_TOKEN_FILE` is supported since `v0.9.0`. ### `trace_sink` From c99cb58d712fcfdcc9ec03095c0cac25e0c2b934 Mon Sep 17 00:00:00 2001 From: networkException Date: Thu, 19 Oct 2023 03:29:48 +0200 Subject: [PATCH 2/4] util: move reading secret file into seperate helper this patch moves the logic to read a secret file (and check for correct permissions) from `secret_from_file` into a new `read_secret_file` helper. --- src/util/config.rs | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/util/config.rs b/src/util/config.rs index ebcb5fbe..756f7ec5 100644 --- a/src/util/config.rs +++ b/src/util/config.rs @@ -238,6 +238,24 @@ pub fn read_config(config_file: PathBuf) -> Result { Ok(parsed_config) } +pub fn read_secret_file(file_path: &String) -> Result { + #[cfg(unix)] + if std::env::var("GARAGE_ALLOW_WORLD_READABLE_SECRETS").as_deref() != Ok("true") { + use std::os::unix::fs::MetadataExt; + let metadata = std::fs::metadata(file_path)?; + if metadata.mode() & 0o077 != 0 { + return Err(format!("File {} is world-readable! (mode: 0{:o}, expected 0600)\nRefusing to start until this is fixed, or environment variable GARAGE_ALLOW_WORLD_READABLE_SECRETS is set to true.", file_path, metadata.mode()).into()); + } + } + let mut file = std::fs::OpenOptions::new().read(true).open(file_path)?; + let mut secret_buf = String::new(); + file.read_to_string(&mut secret_buf)?; + + // trim_end: allows for use case such as `echo "$(openssl rand -hex 32)" > somefile`. + // also editors sometimes add a trailing newline + Ok(String::from(secret_buf.trim_end())) +} + fn secret_from_file( secret: &mut Option, secret_file: &Option, @@ -250,22 +268,7 @@ fn secret_from_file( (Some(_), Some(_)) => { return Err(format!("only one of `{}` and `{}_file` can be set", name, name).into()); } - (None, Some(file_path)) => { - #[cfg(unix)] - if std::env::var("GARAGE_ALLOW_WORLD_READABLE_SECRETS").as_deref() != Ok("true") { - use std::os::unix::fs::MetadataExt; - let metadata = std::fs::metadata(file_path)?; - if metadata.mode() & 0o077 != 0 { - return Err(format!("File {} is world-readable! (mode: 0{:o}, expected 0600)\nRefusing to start until this is fixed, or environment variable GARAGE_ALLOW_WORLD_READABLE_SECRETS is set to true.", file_path, metadata.mode()).into()); - } - } - let mut file = std::fs::OpenOptions::new().read(true).open(file_path)?; - let mut secret_buf = String::new(); - file.read_to_string(&mut secret_buf)?; - // trim_end: allows for use case such as `echo "$(openssl rand -hex 32)" > somefile`. - // also editors sometimes add a trailing newline - *secret = Some(String::from(secret_buf.trim_end())); - } + (None, Some(file_path)) => *secret = Some(read_secret_file(file_path)?), } Ok(()) } From 4a19ee94bb9c846af1c74db8ba501b4ff625a3f6 Mon Sep 17 00:00:00 2001 From: networkException Date: Thu, 19 Oct 2023 03:31:50 +0200 Subject: [PATCH 3/4] garage: fix admin-token description --- src/garage/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/garage/main.rs b/src/garage/main.rs index e8aee892..09e77b35 100644 --- a/src/garage/main.rs +++ b/src/garage/main.rs @@ -70,7 +70,7 @@ pub struct Secrets { #[structopt(short = "s", long = "rpc-secret", env = "GARAGE_RPC_SECRET")] pub rpc_secret: Option, - /// Metrics API authentication token, replaces admin.metrics_token in config.toml when + /// Admin API authentication token, replaces admin.admin_token in config.toml when /// running the Garage daemon #[structopt(long = "admin-token", env = "GARAGE_ADMIN_TOKEN")] pub admin_token: Option, From 8599051c492d7df22305e4c65659395d9102955c Mon Sep 17 00:00:00 2001 From: networkException Date: Thu, 19 Oct 2023 03:39:02 +0200 Subject: [PATCH 4/4] garage: support specifying token / secret as environment variables this patch adds support for specifying the `rpc_secret_file`, `metrics_token_file` and `admin_token_file` as environment variables. --- src/garage/main.rs | 30 +++++++++++++++++++++++++++--- src/garage/repair/offline.rs | 2 +- src/garage/server.rs | 2 +- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/garage/main.rs b/src/garage/main.rs index 09e77b35..ab84fa11 100644 --- a/src/garage/main.rs +++ b/src/garage/main.rs @@ -25,7 +25,7 @@ use structopt::StructOpt; use netapp::util::parse_and_resolve_peer_addr; use netapp::NetworkKey; -use garage_util::config::Config; +use garage_util::config::{read_secret_file, Config}; use garage_util::error::*; use garage_rpc::system::*; @@ -70,15 +70,30 @@ pub struct Secrets { #[structopt(short = "s", long = "rpc-secret", env = "GARAGE_RPC_SECRET")] pub rpc_secret: Option, + /// RPC secret network key, used to replace rpc_secret in config.toml and rpc-secret + /// when running the daemon or doing admin operations + #[structopt(long = "rpc-secret-file", env = "GARAGE_RPC_SECRET_FILE")] + pub rpc_secret_file: Option, + /// Admin API authentication token, replaces admin.admin_token in config.toml when /// running the Garage daemon #[structopt(long = "admin-token", env = "GARAGE_ADMIN_TOKEN")] pub admin_token: Option, + /// Admin API authentication token file path, replaces admin.admin_token in config.toml + /// and admin-token when running the Garage daemon + #[structopt(long = "admin-token-file", env = "GARAGE_ADMIN_TOKEN_FILE")] + pub admin_token_file: Option, + /// Metrics API authentication token, replaces admin.metrics_token in config.toml when /// running the Garage daemon #[structopt(long = "metrics-token", env = "GARAGE_METRICS_TOKEN")] pub metrics_token: Option, + + /// Metrics API authentication token file path, replaces admin.metrics_token in config.toml + /// and metrics-token when running the Garage daemon + #[structopt(long = "metrics-token-file", env = "GARAGE_METRICS_TOKEN_FILE")] + pub metrics_token_file: Option, } #[tokio::main] @@ -256,15 +271,24 @@ async fn cli_command(opt: Opt) -> Result<(), Error> { } } -fn fill_secrets(mut config: Config, secrets: Secrets) -> Config { +fn fill_secrets(mut config: Config, secrets: Secrets) -> Result { if secrets.rpc_secret.is_some() { config.rpc_secret = secrets.rpc_secret; + } else if secrets.rpc_secret_file.is_some() { + config.rpc_secret = Some(read_secret_file(&secrets.rpc_secret_file.unwrap())?); } + if secrets.admin_token.is_some() { config.admin.admin_token = secrets.admin_token; + } else if secrets.admin_token_file.is_some() { + config.admin.admin_token = Some(read_secret_file(&secrets.admin_token_file.unwrap())?); } + if secrets.metrics_token.is_some() { config.admin.metrics_token = secrets.metrics_token; + } else if secrets.metrics_token_file.is_some() { + config.admin.metrics_token = Some(read_secret_file(&secrets.metrics_token_file.unwrap())?); } - config + + Ok(config) } diff --git a/src/garage/repair/offline.rs b/src/garage/repair/offline.rs index f4edcf03..beb48d65 100644 --- a/src/garage/repair/offline.rs +++ b/src/garage/repair/offline.rs @@ -20,7 +20,7 @@ pub async fn offline_repair( } info!("Loading configuration..."); - let config = fill_secrets(read_config(config_file)?, secrets); + let config = fill_secrets(read_config(config_file)?, secrets)?; info!("Initializing Garage main data store..."); let garage = Garage::new(config)?; diff --git a/src/garage/server.rs b/src/garage/server.rs index 3ad10b72..96ea900d 100644 --- a/src/garage/server.rs +++ b/src/garage/server.rs @@ -29,7 +29,7 @@ async fn wait_from(mut chan: watch::Receiver) { pub async fn run_server(config_file: PathBuf, secrets: Secrets) -> Result<(), Error> { info!("Loading configuration..."); - let config = fill_secrets(read_config(config_file)?, secrets); + let config = fill_secrets(read_config(config_file)?, secrets)?; // ---- Initialize Garage internals ----