store the forge webhook secret and grafana's secret key in bao, seeded from the existing files
This commit is contained in:
parent
50c1f2a3d1
commit
a4e48a7bca
2 changed files with 697 additions and 142 deletions
|
|
@ -16,6 +16,9 @@
|
|||
//! `swarm-controller`'s own forge token ([`controller_token_path`]) is the
|
||||
//! fourth: the forge's `forgejo-swarm-controller-account`, in its container,
|
||||
//! mints it and writes it here, and the controller reads it as it starts.
|
||||
//!
|
||||
//! The webhook HMAC secret ([`controller_webhook_secret_path`]) is the one
|
||||
//! value here the controller mints itself.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
|
@ -81,6 +84,30 @@ pub fn controller_token_path() -> Result<String, Error> {
|
|||
Ok(format!("{prefix}/forge/token"))
|
||||
}
|
||||
|
||||
/// The path holding the HMAC secret of the swarm-wide forge webhooks.
|
||||
///
|
||||
/// `swarm-controller` creates it, seeded from the file it kept before the store
|
||||
/// held it, and reads it as it starts; it registers the same value with the
|
||||
/// forge. The nix half is `swarm-bao.nix`'s `controllerWebhookSecretLeaf`.
|
||||
///
|
||||
/// # Errors
|
||||
/// Never in practice: the name segment is a constant. The `Result` is
|
||||
/// [`principal_prefix`]'s.
|
||||
pub fn controller_webhook_secret_path() -> Result<String, Error> {
|
||||
let prefix = principal_prefix(Kind::Controller, crate::matrix::CONTROLLER)?;
|
||||
Ok(format!("{prefix}/forge/webhook-secret"))
|
||||
}
|
||||
|
||||
/// What [`controller_webhook_secret_path`] holds.
|
||||
///
|
||||
/// No `Debug` derive: `value` signs every delivery the controller accepts.
|
||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct WebhookSecret {
|
||||
/// The hex secret itself. Named `value` like the other single-value
|
||||
/// objects here, so `bao kv get -field=value` reads it.
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
/// What [`controller_token_path`] holds. No token name beside the value, unlike
|
||||
/// [`Credential`]: the mint writes the token alone.
|
||||
///
|
||||
|
|
@ -188,6 +215,15 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_webhook_secret_is_under_the_controller_prefix() {
|
||||
// Spelled out: `swarm-bao.nix` grants exactly this string.
|
||||
assert_eq!(
|
||||
controller_webhook_secret_path().expect("a constant segment is legal"),
|
||||
"swarm/controller/swarm-controller/forge/webhook-secret"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_hive_policy_reaches_the_controller_token() {
|
||||
let policy = crate::policy::effective_hive("alpha");
|
||||
|
|
|
|||
|
|
@ -120,6 +120,20 @@ impl From<vaultrs::error::ClientError> for Error {
|
|||
}
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// Whether the store answered 403: the principal's policy does not cover
|
||||
/// the path. A policy the store's host has not written yet looks the same
|
||||
/// as one that never will, so a caller that can do without the path
|
||||
/// checks this before treating the failure as an outage.
|
||||
#[must_use]
|
||||
pub fn is_denied(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::Vault(e) if matches!(**e, vaultrs::error::ClientError::APIError { code: 403, .. })
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Install `aws-lc-rs` as the process-wide rustls crypto provider. Call it
|
||||
/// first thing in `main` of every binary that dials the queue or reaches the
|
||||
/// secret store over TLS.
|
||||
|
|
@ -138,6 +152,21 @@ pub fn install_crypto_provider() {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn only_a_403_is_a_denial() {
|
||||
let api = |code| {
|
||||
Error::from(vaultrs::error::ClientError::APIError {
|
||||
code,
|
||||
errors: Vec::new(),
|
||||
})
|
||||
};
|
||||
assert!(api(403).is_denied());
|
||||
// The controls: a miss and an outage are not denials.
|
||||
assert!(!api(404).is_denied());
|
||||
assert!(!api(503).is_denied());
|
||||
assert!(!Error::MissingEnv("BAO_ADDR").is_denied());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_tls_client_config_builds_once_the_provider_is_installed() {
|
||||
install_crypto_provider();
|
||||
|
|
|
|||
Loading…
Reference in a new issue