Bascule shell runtime workspace — governed shell access layer for Substrate/Guildhouse FFC deployments. Crates: - bascule-agent: node agent with SSH server + command filtering - bascule-core: audit, grant engine, ceremony types, session - bascule-filter-core: log line filtering (stdio protocol) - bascule-gateway: OIDC auth, session management, SAT validation - bascule-node-agent: k8s DaemonSet agent (pod watcher, BPF manager) - bascule-proto: protobuf definitions - bascule-shell: governed SSH shell (commands, elevation, REPL) - bascule-tail: chronicle log tail + fanout - ceremony-engine: ceremony lifecycle (6 types + request/resolution) 172 tests passing. Implements SBS-SPEC-0001 shell model. Reference impl for SPEC-SHELLOPS-0001 Layer 1 (root shell).
71 lines
2.2 KiB
Rust
71 lines
2.2 KiB
Rust
//! SECRETS namespace (0x0003) — secret retrieval from env or Vault.
|
|
|
|
use async_trait::async_trait;
|
|
|
|
use crate::shellstream::{secrets, ShellstreamResponse};
|
|
use super::NamespaceHandler;
|
|
|
|
pub struct SecretsHandler;
|
|
|
|
impl SecretsHandler {
|
|
pub fn new() -> Self {
|
|
Self
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl NamespaceHandler for SecretsHandler {
|
|
async fn handle(
|
|
&self,
|
|
function_id: u16,
|
|
payload: &[u8],
|
|
session_id: &[u8; 16],
|
|
) -> ShellstreamResponse {
|
|
match function_id {
|
|
secrets::GET => self.get(payload, session_id).await,
|
|
secrets::PUT => ShellstreamResponse::denied(*session_id, 0, "PUT not supported in soft mode"),
|
|
secrets::ROTATE => ShellstreamResponse::denied(*session_id, 0, "ROTATE not supported in soft mode"),
|
|
_ => ShellstreamResponse::error(
|
|
*session_id,
|
|
0,
|
|
&format!("Unknown SECRETS function: {function_id:#06x}"),
|
|
),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl SecretsHandler {
|
|
async fn get(&self, payload: &[u8], session_id: &[u8; 16]) -> ShellstreamResponse {
|
|
let payload_parsed: serde_json::Value =
|
|
rmp_serde::from_slice(payload).unwrap_or(serde_json::Value::Null);
|
|
|
|
let path = payload_parsed
|
|
.get("path")
|
|
.and_then(|v| v.as_str())
|
|
.unwrap_or("");
|
|
|
|
if path.is_empty() {
|
|
return ShellstreamResponse::error(*session_id, 0, "Missing 'path'");
|
|
}
|
|
|
|
// Soft mode: look up environment variable
|
|
// Convert path like "db/password" to env var "DB_PASSWORD"
|
|
let env_key = path.replace('/', "_").to_uppercase();
|
|
|
|
match std::env::var(&env_key) {
|
|
Ok(value) => {
|
|
let response = rmp_serde::to_vec(&serde_json::json!({
|
|
"value": value,
|
|
"source": "env",
|
|
}))
|
|
.unwrap_or_default();
|
|
ShellstreamResponse::ok(*session_id, 0, response)
|
|
}
|
|
Err(_) => ShellstreamResponse::error(
|
|
*session_id,
|
|
0,
|
|
&format!("Secret not found: {path} (env: {env_key})"),
|
|
),
|
|
}
|
|
}
|
|
}
|