diff options
author | Alex <alex@adnab.me> | 2023-10-19 09:33:12 +0000 |
---|---|---|
committer | Alex <alex@adnab.me> | 2023-10-19 09:33:12 +0000 |
commit | 0215b114028c9a9987c1581e50820a42e069ffe5 (patch) | |
tree | 5d388a9643d5474b914b7f261c72c21142d8c592 | |
parent | e75fe2157d0973eb4320bbc87be5e54a1cb91a63 (diff) | |
parent | 8599051c492d7df22305e4c65659395d9102955c (diff) | |
download | garage-0215b114028c9a9987c1581e50820a42e069ffe5.tar.gz garage-0215b114028c9a9987c1581e50820a42e069ffe5.zip |
Merge pull request 'Add support for specifying `rpc_secret_file`, `metrics_token_file` and `admin_token_file` using environment variables' (#643) from networkException/garage:token-file-env into main-0.8.x
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/643
-rw-r--r-- | doc/book/reference-manual/configuration.md | 11 | ||||
-rw-r--r-- | src/garage/main.rs | 32 | ||||
-rw-r--r-- | src/garage/repair/offline.rs | 2 | ||||
-rw-r--r-- | src/garage/server.rs | 2 | ||||
-rw-r--r-- | src/util/config.rs | 35 |
5 files changed, 57 insertions, 25 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` diff --git a/src/garage/main.rs b/src/garage/main.rs index bec3197c..a9f1ad29 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<String>, - /// Metrics API authentication token, replaces admin.metrics_token in config.toml when + /// 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<String>, + + /// 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<String>, + /// 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<String>, + /// 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<String>, + + /// 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<String>, } #[tokio::main] @@ -259,15 +274,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<Config, Error> { 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<bool>) { 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 ---- 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<Config, Error> { Ok(parsed_config) } +pub fn read_secret_file(file_path: &String) -> Result<String, Error> { + #[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<String>, secret_file: &Option<String>, @@ -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(()) } |