mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-26 08:18:18 +00:00
fix(credentials): enforce RPC fallback credential policy (#5005)
This commit is contained in:
@@ -260,13 +260,10 @@ fn resolve_rpc_secret(env_secret: Option<&str>, global_access: Option<&str>, glo
|
|||||||
|
|
||||||
match (global_access, global_secret) {
|
match (global_access, global_secret) {
|
||||||
(Some(access_key), Some(secret_key)) => {
|
(Some(access_key), Some(secret_key)) => {
|
||||||
// Fail closed: never derive the RPC secret while the default secret
|
// Fail closed when either half of the active credential pair still
|
||||||
// key is in effect. The derivation uses `secret_key` as the HMAC key,
|
// uses the public default. Operators must configure both custom
|
||||||
// so a public default secret yields a publicly computable RPC secret
|
// credentials or provide RUSTFS_RPC_SECRET explicitly.
|
||||||
// that any network peer can use to forge internode RPC signatures.
|
if access_key.trim() == DEFAULT_ACCESS_KEY || secret_key.trim() == DEFAULT_SECRET_KEY {
|
||||||
// Operators running with default credentials must configure
|
|
||||||
// RUSTFS_RPC_SECRET (or set a non-default RUSTFS_SECRET_KEY) instead.
|
|
||||||
if secret_key.trim() == DEFAULT_SECRET_KEY {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
derive_rpc_secret(access_key, secret_key)
|
derive_rpc_secret(access_key, secret_key)
|
||||||
@@ -589,18 +586,11 @@ mod tests {
|
|||||||
fn test_resolve_rpc_secret_rejects_default_credentials_for_derivation() {
|
fn test_resolve_rpc_secret_rejects_default_credentials_for_derivation() {
|
||||||
assert!(resolve_rpc_secret(None, None, None).is_none());
|
assert!(resolve_rpc_secret(None, None, None).is_none());
|
||||||
|
|
||||||
// Fail closed: the default secret key must not yield a derivable RPC
|
// Fail closed when either half of the credential pair uses the public
|
||||||
// secret, otherwise the derived value is publicly computable and any
|
// default.
|
||||||
// network peer can forge internode RPC signatures.
|
|
||||||
assert!(resolve_rpc_secret(None, Some(DEFAULT_ACCESS_KEY), Some(DEFAULT_SECRET_KEY)).is_none());
|
assert!(resolve_rpc_secret(None, Some(DEFAULT_ACCESS_KEY), Some(DEFAULT_SECRET_KEY)).is_none());
|
||||||
|
assert!(resolve_rpc_secret(None, Some(DEFAULT_ACCESS_KEY), Some("custom-global-secret")).is_none());
|
||||||
// A default access key paired with a non-default secret key is still
|
assert!(resolve_rpc_secret(None, Some("custom-access"), Some(DEFAULT_SECRET_KEY)).is_none());
|
||||||
// safe to derive: the HMAC key (the secret key) is not public.
|
|
||||||
let expected = derive_rpc_secret(DEFAULT_ACCESS_KEY, "custom-global-secret").expect("secret should derive");
|
|
||||||
assert_eq!(
|
|
||||||
resolve_rpc_secret(None, Some(DEFAULT_ACCESS_KEY), Some("custom-global-secret")).as_deref(),
|
|
||||||
Some(expected.as_str())
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(resolve_rpc_secret(Some(DEFAULT_SECRET_KEY), Some("custom-access"), Some("custom-global-secret")).is_none());
|
assert!(resolve_rpc_secret(Some(DEFAULT_SECRET_KEY), Some("custom-access"), Some("custom-global-secret")).is_none());
|
||||||
}
|
}
|
||||||
@@ -635,6 +625,10 @@ mod tests {
|
|||||||
resolve_rpc_secret(Some("custom-rpc-secret"), None, None).as_deref(),
|
resolve_rpc_secret(Some("custom-rpc-secret"), None, None).as_deref(),
|
||||||
Some("custom-rpc-secret")
|
Some("custom-rpc-secret")
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
resolve_rpc_secret(Some("custom-rpc-secret"), Some(DEFAULT_ACCESS_KEY), Some(DEFAULT_SECRET_KEY)).as_deref(),
|
||||||
|
Some("custom-rpc-secret")
|
||||||
|
);
|
||||||
let expected = derive_rpc_secret("custom-access", "custom-global-secret").expect("secret should derive");
|
let expected = derive_rpc_secret("custom-access", "custom-global-secret").expect("secret should derive");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resolve_rpc_secret(None, Some("custom-access"), Some("custom-global-secret")).as_deref(),
|
resolve_rpc_secret(None, Some("custom-access"), Some("custom-global-secret")).as_deref(),
|
||||||
|
|||||||
@@ -87,6 +87,12 @@ async fn cluster_two_pool_smoke() -> TestResult {
|
|||||||
|
|
||||||
let mut cluster =
|
let mut cluster =
|
||||||
RustFSTestClusterEnvironment::with_topology(ClusterTopology::per_node_pools(2, vec![vec![0], vec![1]])).await?;
|
RustFSTestClusterEnvironment::with_topology(ClusterTopology::per_node_pools(2, vec![vec![0], vec![1]])).await?;
|
||||||
|
cluster.access_key = "custom-two-pool-access".to_string();
|
||||||
|
cluster.secret_key = "custom-two-pool-secret".to_string();
|
||||||
|
// Override any inherited test-runner value with an explicit blank. The
|
||||||
|
// credentials getter treats blank as absent and must derive one stable RPC
|
||||||
|
// secret from the shared non-default credential pair on every node.
|
||||||
|
cluster.set_env("RUSTFS_RPC_SECRET", "");
|
||||||
|
|
||||||
// The two-pool layout must emit one ellipses argument per pool.
|
// The two-pool layout must emit one ellipses argument per pool.
|
||||||
let volumes = cluster.rustfs_volumes_arg();
|
let volumes = cluster.rustfs_volumes_arg();
|
||||||
|
|||||||
Reference in New Issue
Block a user