Add secrets.keys_dir config option. (#4877)
This commit is contained in:
@@ -240,6 +240,23 @@ impl From<Encryption> for EncryptionRaw {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads all keys from the given directory.
|
||||||
|
async fn key_configs_from_path(path: &Utf8PathBuf) -> anyhow::Result<Vec<KeyConfig>> {
|
||||||
|
let mut result = vec![];
|
||||||
|
let mut read_dir = tokio::fs::read_dir(path).await?;
|
||||||
|
while let Some(dir_entry) = read_dir.next_entry().await? {
|
||||||
|
if !dir_entry.path().is_file() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
result.push(KeyConfig {
|
||||||
|
kid: None,
|
||||||
|
password: None,
|
||||||
|
key: Key::File(dir_entry.path().try_into()?),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
/// Application secrets
|
/// Application secrets
|
||||||
#[serde_as]
|
#[serde_as]
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||||
@@ -250,9 +267,14 @@ pub struct SecretsConfig {
|
|||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
encryption: Encryption,
|
encryption: Encryption,
|
||||||
|
|
||||||
/// List of private keys to use for signing and encrypting payloads
|
/// List of private keys to use for signing and encrypting payloads.
|
||||||
#[serde(default)]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
keys: Vec<KeyConfig>,
|
keys: Option<Vec<KeyConfig>>,
|
||||||
|
|
||||||
|
/// Directory of private keys to use for signing and encrypting payloads.
|
||||||
|
#[schemars(with = "Option<String>")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
keys_dir: Option<Utf8PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SecretsConfig {
|
impl SecretsConfig {
|
||||||
@@ -263,7 +285,8 @@ impl SecretsConfig {
|
|||||||
/// Returns an error when a key could not be imported
|
/// Returns an error when a key could not be imported
|
||||||
#[tracing::instrument(name = "secrets.load", skip_all)]
|
#[tracing::instrument(name = "secrets.load", skip_all)]
|
||||||
pub async fn key_store(&self) -> anyhow::Result<Keystore> {
|
pub async fn key_store(&self) -> anyhow::Result<Keystore> {
|
||||||
let web_keys = try_join_all(self.keys.iter().map(KeyConfig::json_web_key)).await?;
|
let key_configs = self.key_configs().await?;
|
||||||
|
let web_keys = try_join_all(key_configs.iter().map(KeyConfig::json_web_key)).await?;
|
||||||
|
|
||||||
Ok(Keystore::new(JsonWebKeySet::new(web_keys)))
|
Ok(Keystore::new(JsonWebKeySet::new(web_keys)))
|
||||||
}
|
}
|
||||||
@@ -297,6 +320,21 @@ impl SecretsConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a combined list of key configs given inline and from files.
|
||||||
|
///
|
||||||
|
/// If `keys_dir` was given, the keys are read from file.
|
||||||
|
async fn key_configs(&self) -> anyhow::Result<Vec<KeyConfig>> {
|
||||||
|
let mut key_configs = match &self.keys_dir {
|
||||||
|
Some(keys_dir) => key_configs_from_path(keys_dir).await?,
|
||||||
|
None => vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
let inline_key_configs = self.keys.as_deref().unwrap_or_default();
|
||||||
|
key_configs.extend(inline_key_configs.iter().cloned());
|
||||||
|
|
||||||
|
Ok(key_configs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConfigurationSection for SecretsConfig {
|
impl ConfigurationSection for SecretsConfig {
|
||||||
@@ -378,7 +416,8 @@ impl SecretsConfig {
|
|||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
encryption: Encryption::Value(Standard.sample(&mut rng)),
|
encryption: Encryption::Value(Standard.sample(&mut rng)),
|
||||||
keys: vec![rsa_key, ec_p256_key, ec_p384_key, ec_k256_key],
|
keys: Some(vec![rsa_key, ec_p256_key, ec_p384_key, ec_k256_key]),
|
||||||
|
keys_dir: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -419,7 +458,8 @@ impl SecretsConfig {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
encryption: Encryption::Value([0xEA; 32]),
|
encryption: Encryption::Value([0xEA; 32]),
|
||||||
keys: vec![rsa_key, ecdsa_key],
|
keys: Some(vec![rsa_key, ecdsa_key]),
|
||||||
|
keys_dir: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -435,6 +475,132 @@ mod tests {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn load_config() {
|
||||||
|
task::spawn_blocking(|| {
|
||||||
|
Jail::expect_with(|jail| {
|
||||||
|
jail.create_file(
|
||||||
|
"config.yaml",
|
||||||
|
indoc::indoc! {r"
|
||||||
|
secrets:
|
||||||
|
encryption_file: encryption
|
||||||
|
keys_dir: keys
|
||||||
|
"},
|
||||||
|
)?;
|
||||||
|
jail.create_file(
|
||||||
|
"encryption",
|
||||||
|
"0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff",
|
||||||
|
)?;
|
||||||
|
jail.create_dir("keys")?;
|
||||||
|
jail.create_file(
|
||||||
|
"keys/key1",
|
||||||
|
indoc::indoc! {r"
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
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
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
|
"},
|
||||||
|
)?;
|
||||||
|
jail.create_file(
|
||||||
|
"keys/key2",
|
||||||
|
indoc::indoc! {r"
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
MHcCAQEEIKlZz/GnH0idVH1PnAF4HQNwRafgBaE2tmyN1wjfdOQqoAoGCCqGSM49
|
||||||
|
AwEHoUQDQgAEHrgPeG+Mt8eahih1h4qaPjhl7jT25cdzBkg3dbVks6gBR2Rx4ug9
|
||||||
|
h27LAir5RqxByHvua2XsP46rSTChof78uw==
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
|
"},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let config = Figment::new()
|
||||||
|
.merge(Yaml::file("config.yaml"))
|
||||||
|
.extract_inner::<SecretsConfig>("secrets")?;
|
||||||
|
|
||||||
|
Handle::current().block_on(async move {
|
||||||
|
assert!(
|
||||||
|
matches!(config.encryption, Encryption::File(ref p) if p == "encryption")
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
config.encryption().await.unwrap(),
|
||||||
|
[
|
||||||
|
0, 0, 17, 17, 34, 34, 51, 51, 68, 68, 85, 85, 102, 102, 119, 119, 136,
|
||||||
|
136, 153, 153, 170, 170, 187, 187, 204, 204, 221, 221, 238, 238, 255,
|
||||||
|
255
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut key_config = config.key_configs().await.unwrap();
|
||||||
|
key_config.sort_by_key(|a| {
|
||||||
|
if let Key::File(p) = &a.key {
|
||||||
|
Some(p.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let key_store = config.key_store().await.unwrap();
|
||||||
|
|
||||||
|
assert!(key_config[0].kid.is_none());
|
||||||
|
assert!(matches!(&key_config[0].key, Key::File(p) if p == "keys/key1"));
|
||||||
|
assert!(key_store.iter().any(|k| k.kid() == Some("xmgGCzGtQFmhEOP0YAqBt-oZyVauSVMXcf4kwcgGZLc")));
|
||||||
|
assert!(key_config[1].kid.is_none());
|
||||||
|
assert!(matches!(&key_config[1].key, Key::File(p) if p == "keys/key2"));
|
||||||
|
assert!(key_store.iter().any(|k| k.kid() == Some("ONUCn80fsiISFWKrVMEiirNVr-QEvi7uQI0QH9q9q4o")));
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn load_config_inline_secrets() {
|
async fn load_config_inline_secrets() {
|
||||||
task::spawn_blocking(|| {
|
task::spawn_blocking(|| {
|
||||||
@@ -487,4 +653,57 @@ mod tests {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn load_config_mixed_key_sources() {
|
||||||
|
task::spawn_blocking(|| {
|
||||||
|
Jail::expect_with(|jail| {
|
||||||
|
jail.create_file(
|
||||||
|
"config.yaml",
|
||||||
|
indoc::indoc! {r"
|
||||||
|
secrets:
|
||||||
|
encryption_file: encryption
|
||||||
|
keys_dir: keys
|
||||||
|
keys:
|
||||||
|
- kid: lekid0
|
||||||
|
key: |
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
MHcCAQEEIOtZfDuXZr/NC0V3sisR4Chf7RZg6a2dpZesoXMlsPeRoAoGCCqGSM49
|
||||||
|
AwEHoUQDQgAECfpqx64lrR85MOhdMxNmIgmz8IfmM5VY9ICX9aoaArnD9FjgkBIl
|
||||||
|
fGmQWxxXDSWH6SQln9tROVZaduenJqDtDw==
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
|
"},
|
||||||
|
)?;
|
||||||
|
jail.create_dir("keys")?;
|
||||||
|
jail.create_file(
|
||||||
|
"keys/key_from_file",
|
||||||
|
indoc::indoc! {r"
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
MHcCAQEEIKlZz/GnH0idVH1PnAF4HQNwRafgBaE2tmyN1wjfdOQqoAoGCCqGSM49
|
||||||
|
AwEHoUQDQgAEHrgPeG+Mt8eahih1h4qaPjhl7jT25cdzBkg3dbVks6gBR2Rx4ug9
|
||||||
|
h27LAir5RqxByHvua2XsP46rSTChof78uw==
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
|
"},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let config = Figment::new()
|
||||||
|
.merge(Yaml::file("config.yaml"))
|
||||||
|
.extract_inner::<SecretsConfig>("secrets")?;
|
||||||
|
|
||||||
|
Handle::current().block_on(async move {
|
||||||
|
let key_config = config.key_configs().await.unwrap();
|
||||||
|
let key_store = config.key_store().await.unwrap();
|
||||||
|
|
||||||
|
assert!(key_config[0].kid.is_none());
|
||||||
|
assert!(matches!(&key_config[0].key, Key::File(p) if p == "keys/key_from_file"));
|
||||||
|
assert!(key_store.iter().any(|k| k.kid() == Some("ONUCn80fsiISFWKrVMEiirNVr-QEvi7uQI0QH9q9q4o")));
|
||||||
|
assert!(key_store.iter().any(|k| k.kid() == Some("lekid0")));
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1640,12 +1640,21 @@
|
|||||||
"pattern": "[0-9a-fA-F]{64}"
|
"pattern": "[0-9a-fA-F]{64}"
|
||||||
},
|
},
|
||||||
"keys": {
|
"keys": {
|
||||||
"description": "List of private keys to use for signing and encrypting payloads",
|
"description": "List of private keys to use for signing and encrypting payloads.",
|
||||||
"type": "array",
|
"type": [
|
||||||
|
"array",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/KeyConfig"
|
"$ref": "#/definitions/KeyConfig"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"default": []
|
"keys_dir": {
|
||||||
|
"description": "Directory of private keys to use for signing and encrypting payloads.",
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -222,7 +222,7 @@ The secret is not updated when the content of the file changes.
|
|||||||
> Changing the encryption secret afterwards will lead to a loss of all encrypted
|
> Changing the encryption secret afterwards will lead to a loss of all encrypted
|
||||||
> information in the database.
|
> information in the database.
|
||||||
|
|
||||||
### `secrets.keys`
|
### Singing Keys
|
||||||
|
|
||||||
The service can use a number of key types for signing.
|
The service can use a number of key types for signing.
|
||||||
The following key types are supported:
|
The following key types are supported:
|
||||||
@@ -232,15 +232,26 @@ The following key types are supported:
|
|||||||
- ECDSA with the P-384 (`secp384r1`) curve
|
- ECDSA with the P-384 (`secp384r1`) curve
|
||||||
- ECDSA with the K-256 (`secp256k1`) curve
|
- ECDSA with the K-256 (`secp256k1`) curve
|
||||||
|
|
||||||
Each entry in the list corresponds to one signing key used by MAS.
|
|
||||||
The key can either be specified inline (with the `key` property),
|
|
||||||
or loaded from a file (with the `key_file` property).
|
|
||||||
The following key formats are supported:
|
The following key formats are supported:
|
||||||
|
|
||||||
- PKCS#1 PEM or DER-encoded RSA private key
|
- PKCS#1 PEM or DER-encoded RSA private key
|
||||||
- PKCS#8 PEM or DER-encoded RSA or ECDSA private key, encrypted or not
|
- PKCS#8 PEM or DER-encoded RSA or ECDSA private key, encrypted or not
|
||||||
- SEC1 PEM or DER-encoded ECDSA private key
|
- SEC1 PEM or DER-encoded ECDSA private key
|
||||||
|
|
||||||
|
The keys can be given as a directory path via `secrets.keys_dir`
|
||||||
|
or, alternatively, as an inline configuration list via `secrets.keys`.
|
||||||
|
|
||||||
|
#### `secrets.keys_dir`
|
||||||
|
|
||||||
|
Path to the directory containing MAS signing key files.
|
||||||
|
Only keys that don’t require a password are supported.
|
||||||
|
|
||||||
|
#### `secrets.keys`
|
||||||
|
|
||||||
|
Each entry in the list corresponds to one signing key used by MAS.
|
||||||
|
The key can either be specified inline (with the `key` property),
|
||||||
|
or loaded from a file (with the `key_file` property).
|
||||||
|
|
||||||
A [JWK Key ID] is automatically derived from each key.
|
A [JWK Key ID] is automatically derived from each key.
|
||||||
To override this default, set `kid` to a custom value.
|
To override this default, set `kid` to a custom value.
|
||||||
The `kid` can be any case-sensitive string value as long as it is unique to this list;
|
The `kid` can be any case-sensitive string value as long as it is unique to this list;
|
||||||
|
|||||||
Reference in New Issue
Block a user