mirror of
https://github.com/deuxfleurs-org/garage.git
synced 2026-09-06 03:59:15 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 33d50666b5 |
Generated
+1
@@ -1890,6 +1890,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2 0.10.9",
|
"sha2 0.10.9",
|
||||||
|
"subtle",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml",
|
"toml",
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ pnet_datalink = "0.35"
|
|||||||
rand = "0.9"
|
rand = "0.9"
|
||||||
sha1 = "0.10"
|
sha1 = "0.10"
|
||||||
sha2 = "0.10"
|
sha2 = "0.10"
|
||||||
|
subtle = "2.6.1"
|
||||||
timeago = { version = "0.5", default-features = false }
|
timeago = { version = "0.5", default-features = false }
|
||||||
xxhash-rust = { version = "0.8", default-features = false, features = ["xxh3"] }
|
xxhash-rust = { version = "0.8", default-features = false, features = ["xxh3"] }
|
||||||
|
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ impl RequestHandler for GetCurrentAdminTokenInfoRequest {
|
|||||||
.admin
|
.admin
|
||||||
.metrics_token
|
.metrics_token
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.is_some_and(|s| s == &self.admin_token)
|
.is_some_and(|s| s.eq_ct(&self.admin_token))
|
||||||
{
|
{
|
||||||
return Ok(GetCurrentAdminTokenInfoResponse(
|
return Ok(GetCurrentAdminTokenInfoResponse(
|
||||||
GetAdminTokenInfoResponse {
|
GetAdminTokenInfoResponse {
|
||||||
@@ -210,7 +210,7 @@ impl RequestHandler for GetCurrentAdminTokenInfoRequest {
|
|||||||
.admin
|
.admin
|
||||||
.admin_token
|
.admin_token
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.is_some_and(|s| s == &self.admin_token)
|
.is_some_and(|s| s.eq_ct(&self.admin_token))
|
||||||
{
|
{
|
||||||
return Ok(GetCurrentAdminTokenInfoResponse(
|
return Ok(GetCurrentAdminTokenInfoResponse(
|
||||||
GetAdminTokenInfoResponse {
|
GetAdminTokenInfoResponse {
|
||||||
|
|||||||
@@ -117,8 +117,14 @@ impl AdminApiServer {
|
|||||||
#[cfg(feature = "metrics")] exporter: PrometheusExporter,
|
#[cfg(feature = "metrics")] exporter: PrometheusExporter,
|
||||||
) -> Arc<Self> {
|
) -> Arc<Self> {
|
||||||
let cfg = &garage.config.admin;
|
let cfg = &garage.config.admin;
|
||||||
let metrics_token = cfg.metrics_token.as_deref().map(hash_bearer_token);
|
let metrics_token = cfg
|
||||||
let admin_token = cfg.admin_token.as_deref().map(hash_bearer_token);
|
.metrics_token
|
||||||
|
.as_ref()
|
||||||
|
.map(|token| hash_bearer_token(token.extract_secret()));
|
||||||
|
let admin_token = cfg
|
||||||
|
.admin_token
|
||||||
|
.as_ref()
|
||||||
|
.map(|token| hash_bearer_token(token.extract_secret()));
|
||||||
let metrics_require_token = cfg.metrics_require_token;
|
let metrics_require_token = cfg.metrics_require_token;
|
||||||
|
|
||||||
let endpoint = garage.system.netapp.endpoint(ADMIN_RPC_PATH.into());
|
let endpoint = garage.system.netapp.endpoint(ADMIN_RPC_PATH.into());
|
||||||
|
|||||||
+2
-1
@@ -307,7 +307,8 @@ async fn cli_command(opt: Opt) -> Result<(), Error> {
|
|||||||
|
|
||||||
let net_key_hex_str = rpc_secret.ok_or("No RPC secret provided")?;
|
let net_key_hex_str = rpc_secret.ok_or("No RPC secret provided")?;
|
||||||
let network_key = NetworkKey::from_slice(
|
let network_key = NetworkKey::from_slice(
|
||||||
&hex::decode(&net_key_hex_str).err_context("Invalid RPC secret key (bad hex)")?[..],
|
&hex::decode(net_key_hex_str.extract_secret())
|
||||||
|
.err_context("Invalid RPC secret key (bad hex)")?[..],
|
||||||
)
|
)
|
||||||
.ok_or("Invalid RPC secret provided (wrong length)")?;
|
.ok_or("Invalid RPC secret provided (wrong length)")?;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::path::PathBuf;
|
|||||||
|
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use garage_util::config::Config;
|
use garage_util::config::{Config, Secret};
|
||||||
use garage_util::error::Error;
|
use garage_util::error::Error;
|
||||||
|
|
||||||
/// Structure for secret values or paths that are passed as CLI arguments or environment
|
/// Structure for secret values or paths that are passed as CLI arguments or environment
|
||||||
@@ -99,7 +99,7 @@ pub fn fill_secrets(mut config: Config, secrets: Secrets) -> Result<Config, Erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn fill_secret(
|
pub(crate) fn fill_secret(
|
||||||
config_secret: &mut Option<String>,
|
config_secret: &mut Option<Secret<String>>,
|
||||||
config_secret_file: &Option<PathBuf>,
|
config_secret_file: &Option<PathBuf>,
|
||||||
cli_secret: &Option<String>,
|
cli_secret: &Option<String>,
|
||||||
cli_secret_file: &Option<PathBuf>,
|
cli_secret_file: &Option<PathBuf>,
|
||||||
@@ -110,7 +110,7 @@ pub(crate) fn fill_secret(
|
|||||||
(Some(_), Some(_)) => {
|
(Some(_), Some(_)) => {
|
||||||
return Err(format!("only one of `{}` and `{}_file` can be set", name, name).into());
|
return Err(format!("only one of `{}` and `{}_file` can be set", name, name).into());
|
||||||
}
|
}
|
||||||
(Some(secret), None) => Some(secret.to_string()),
|
(Some(secret), None) => Some(Secret::new(secret.to_string())),
|
||||||
(None, Some(file)) => Some(read_secret_file(file, allow_world_readable)?),
|
(None, Some(file)) => Some(read_secret_file(file, allow_world_readable)?),
|
||||||
(None, None) => None,
|
(None, None) => None,
|
||||||
};
|
};
|
||||||
@@ -132,7 +132,10 @@ pub(crate) fn fill_secret(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_secret_file(file_path: &PathBuf, allow_world_readable: bool) -> Result<String, Error> {
|
fn read_secret_file(
|
||||||
|
file_path: &PathBuf,
|
||||||
|
allow_world_readable: bool,
|
||||||
|
) -> Result<Secret<String>, Error> {
|
||||||
if !allow_world_readable {
|
if !allow_world_readable {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
@@ -152,7 +155,7 @@ fn read_secret_file(file_path: &PathBuf, allow_world_readable: bool) -> Result<S
|
|||||||
|
|
||||||
// trim_end: allows for use case such as `echo "$(openssl rand -hex 32)" > somefile`.
|
// trim_end: allows for use case such as `echo "$(openssl rand -hex 32)" > somefile`.
|
||||||
// also editors sometimes add a trailing newline
|
// also editors sometimes add a trailing newline
|
||||||
Ok(String::from(secret_buf.trim_end()))
|
Ok(Secret::new(String::from(secret_buf.trim_end())))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
+1
-1
@@ -137,7 +137,7 @@ impl Garage {
|
|||||||
info!("Initializing RPC...");
|
info!("Initializing RPC...");
|
||||||
let network_key = hex::decode(config.rpc_secret.as_ref().ok_or_message(
|
let network_key = hex::decode(config.rpc_secret.as_ref().ok_or_message(
|
||||||
"rpc_secret value is missing, not present in config file or in environment",
|
"rpc_secret value is missing, not present in config file or in environment",
|
||||||
)?)
|
)?.extract_secret())
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|x| NetworkKey::from_slice(&x))
|
.and_then(|x| NetworkKey::from_slice(&x))
|
||||||
.ok_or_message("Invalid RPC secret key: expected 32 bytes of random hex, please check the documentation for requirements")?;
|
.ok_or_message("Invalid RPC secret key: expected 32 bytes of random hex, please check the documentation for requirements")?;
|
||||||
|
|||||||
+1
-1
@@ -115,7 +115,7 @@ impl ConsulDiscovery {
|
|||||||
let mut headers = reqwest::header::HeaderMap::new();
|
let mut headers = reqwest::header::HeaderMap::new();
|
||||||
headers.insert(
|
headers.insert(
|
||||||
"x-consul-token",
|
"x-consul-token",
|
||||||
reqwest::header::HeaderValue::from_str(token)?,
|
reqwest::header::HeaderValue::from_str(token.extract_secret())?,
|
||||||
);
|
);
|
||||||
builder = builder.default_headers(headers);
|
builder = builder.default_headers(headers);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ lazy_static.workspace = true
|
|||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
rand.workspace = true
|
rand.workspace = true
|
||||||
sha2.workspace = true
|
sha2.workspace = true
|
||||||
|
subtle.workspace = true
|
||||||
|
|
||||||
chrono.workspace = true
|
chrono.workspace = true
|
||||||
rmp-serde.workspace = true
|
rmp-serde.workspace = true
|
||||||
|
|||||||
+35
-4
@@ -90,7 +90,7 @@ pub struct Config {
|
|||||||
pub allow_world_readable_secrets: bool,
|
pub allow_world_readable_secrets: bool,
|
||||||
|
|
||||||
/// RPC secret key: 32 bytes hex encoded
|
/// RPC secret key: 32 bytes hex encoded
|
||||||
pub rpc_secret: Option<String>,
|
pub rpc_secret: Option<Secret<String>>,
|
||||||
/// Optional file where RPC secret key is read from
|
/// Optional file where RPC secret key is read from
|
||||||
pub rpc_secret_file: Option<PathBuf>,
|
pub rpc_secret_file: Option<PathBuf>,
|
||||||
/// Address to bind for RPC
|
/// Address to bind for RPC
|
||||||
@@ -205,6 +205,37 @@ pub struct WebConfig {
|
|||||||
pub add_host_to_metrics: bool,
|
pub add_host_to_metrics: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct Secret<T>(T);
|
||||||
|
|
||||||
|
impl<T> Secret<T> {
|
||||||
|
pub fn new(secret: T) -> Self {
|
||||||
|
Secret(secret)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extract_secret(&self) -> &T {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: std::ops::Deref<Target = str>> Secret<T> {
|
||||||
|
pub fn eq_ct(&self, other: &T) -> bool {
|
||||||
|
use subtle::ConstantTimeEq;
|
||||||
|
self.0
|
||||||
|
.deref()
|
||||||
|
.as_bytes()
|
||||||
|
.ct_eq(other.deref().as_bytes())
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> std::fmt::Debug for Secret<T> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("Secret").finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Configuration for the admin and monitoring HTTP API
|
/// Configuration for the admin and monitoring HTTP API
|
||||||
#[derive(Deserialize, Debug, Clone, Default)]
|
#[derive(Deserialize, Debug, Clone, Default)]
|
||||||
pub struct AdminConfig {
|
pub struct AdminConfig {
|
||||||
@@ -212,7 +243,7 @@ pub struct AdminConfig {
|
|||||||
pub api_bind_addr: Option<UnixOrTCPSocketAddress>,
|
pub api_bind_addr: Option<UnixOrTCPSocketAddress>,
|
||||||
|
|
||||||
/// Bearer token to use to scrape metrics
|
/// Bearer token to use to scrape metrics
|
||||||
pub metrics_token: Option<String>,
|
pub metrics_token: Option<Secret<String>>,
|
||||||
/// File to read metrics token from
|
/// File to read metrics token from
|
||||||
pub metrics_token_file: Option<PathBuf>,
|
pub metrics_token_file: Option<PathBuf>,
|
||||||
/// Whether to require an access token for accessing the metrics endpoint
|
/// Whether to require an access token for accessing the metrics endpoint
|
||||||
@@ -220,7 +251,7 @@ pub struct AdminConfig {
|
|||||||
pub metrics_require_token: bool,
|
pub metrics_require_token: bool,
|
||||||
|
|
||||||
/// Bearer token to use to access Admin API endpoints
|
/// Bearer token to use to access Admin API endpoints
|
||||||
pub admin_token: Option<String>,
|
pub admin_token: Option<Secret<String>>,
|
||||||
/// File to read admin token from
|
/// File to read admin token from
|
||||||
pub admin_token_file: Option<PathBuf>,
|
pub admin_token_file: Option<PathBuf>,
|
||||||
|
|
||||||
@@ -252,7 +283,7 @@ pub struct ConsulDiscoveryConfig {
|
|||||||
/// Client TLS key to use when connecting to Consul
|
/// Client TLS key to use when connecting to Consul
|
||||||
pub client_key: Option<String>,
|
pub client_key: Option<String>,
|
||||||
/// /// Token to use for connecting to consul
|
/// /// Token to use for connecting to consul
|
||||||
pub token: Option<String>,
|
pub token: Option<Secret<String>>,
|
||||||
/// Skip TLS hostname verification
|
/// Skip TLS hostname verification
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub tls_skip_verify: bool,
|
pub tls_skip_verify: bool,
|
||||||
|
|||||||
Reference in New Issue
Block a user