Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f46fa6f
feat(metrics): add metrics TLS configuration model
gmenher Sep 29, 2026
d58e24b
feat(metrics): add TLS configuration inputs
gmenher Sep 29, 2026
8db78b5
feat(metrics): serve metrics through optional TLS listener
gmenher Sep 29, 2026
ce2a565
test(metrics): cover metrics mTLS trust boundary
gmenher Sep 29, 2026
ab4e9ed
feat(helm): configure metrics TLS with operator Secrets
gmenher Sep 29, 2026
95bd020
feat(tls): identify certificate reload listener
gmenher Sep 29, 2026
53539a8
docs(metrics): document secure metrics TLS configuration
gmenher Sep 29, 2026
535876a
fix(metrics): preserve client CA trust boundary
gmenher Sep 29, 2026
c5a3906
chore(metrics): clarify TLS handshake rejection logs
gmenher Sep 29, 2026
33694b3
test(metrics): cover TLS handshake rejection logs
gmenher Sep 29, 2026
176c34b
fix(metrics): preserve gateway availability on TLS failures
gmenher Oct 22, 2026
8790d60
fix(helm): reject gateway client TLS Secret as metrics client CA
gmenher Oct 27, 2026
34a9ed4
docs(metrics): distinguish missing and malformed metrics TLS Secrets
gmenher Oct 27, 2026
f96efaa
docs(metrics): document the require-client-auth equals-sign form
gmenher Oct 27, 2026
fee0cc6
refactor(metrics): split handshake log limiters by event type
gmenher Oct 27, 2026
eb704e9
test(metrics): align pre-existing reload JSONL test with listener tag
gmenher Oct 27, 2026
83964c8
docs(metrics): scope certgen CA seeding claim to chart-managed PKI
gmenher Oct 27, 2026
b13d54b
style(metrics): drop unnecessary command qualification in flag test
gmenher Oct 27, 2026
b0be882
test(metrics): cover partial metrics TLS flag combinations
gmenher Oct 27, 2026
40875c9
test(metrics): cover TLS configuration without a metrics listener
gmenher Oct 27, 2026
eddd13e
chore: remove accidental OpenCode memory
gmenher Oct 29, 2026
388464a
chore: ignore local OpenCode memory
gmenher Oct 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -249,3 +249,4 @@ scripts/lint-mermaid/node_modules/

# Local triage-duty reports, checkpoint, actions, and inbox
/.agents/triage-duty/
.opencode-memory.md
57 changes: 55 additions & 2 deletions crates/openshell-core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,13 @@ pub struct Config {
/// When `None`, the dedicated metrics listener is disabled.
pub metrics_bind_address: Option<SocketAddr>,

/// TLS configuration for the dedicated metrics listener.
///
/// When `None`, the metrics listener retains its plaintext HTTP behavior.
/// This configuration is intentionally separate from [`Self::tls`] so
/// metrics clients can use their own client CA trust boundary.
pub metrics_tls: Option<MetricsTlsConfig>,

/// Log level (trace, debug, info, warn, error).
pub log_level: String,

Expand Down Expand Up @@ -466,6 +473,27 @@ pub struct TlsConfig {
pub external_server_names: Vec<String>,
}

/// TLS configuration for the dedicated metrics listener.
///
/// Unlike [`TlsConfig`], metrics TLS does not support external certificates
/// or SNI routing. The metrics listener has one narrow HTTPS/mTLS contract
/// with an optional, metrics-specific client CA.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MetricsTlsConfig {
/// Path to the TLS certificate file served by the metrics listener.
pub cert_path: PathBuf,

/// Path to the TLS private key file served by the metrics listener.
pub key_path: PathBuf,

/// Path to the CA certificate file used to verify metrics clients.
pub client_ca_path: Option<PathBuf>,

/// Whether metrics clients must present a certificate trusted by
/// [`Self::client_ca_path`].
pub require_client_auth: bool,
}

/// OIDC (`OpenID` Connect) configuration for JWT-based authentication.
///
/// When configured, the server validates `authorization: Bearer <JWT>`
Expand Down Expand Up @@ -997,6 +1025,7 @@ impl Config {
bind_address: default_bind_address(),
health_bind_address: None,
metrics_bind_address: None,
metrics_tls: None,
log_level: default_log_level(),
policy_validation_failure_mode: PolicyValidationFailureMode::default(),
config_delivery_mode: ConfigDeliveryMode::default(),
Expand Down Expand Up @@ -1050,6 +1079,13 @@ impl Config {
self
}

/// Create a new configuration with TLS for the dedicated metrics listener.
#[must_use]
pub fn with_metrics_tls(mut self, tls: MetricsTlsConfig) -> Self {
self.metrics_tls = Some(tls);
self
}

/// Create a new configuration with the given log level.
#[must_use]
pub fn with_log_level(mut self, level: impl Into<String>) -> Self {
Expand Down Expand Up @@ -1323,12 +1359,12 @@ mod tests {
use super::{
AppArmorProfile, Config, ConfigDeliveryMode, DEFAULT_SERVICE_ROUTING_DOMAIN,
GatewayInterceptorBindingPolicy, GatewayInterceptorConfig, GatewayInterceptorFailurePolicy,
GatewayJwtConfig, GatewayProviderProfileSourceConfig, ImagePullPolicy,
GatewayJwtConfig, GatewayProviderProfileSourceConfig, ImagePullPolicy, MetricsTlsConfig,
PolicyValidationFailureMode, ServiceHostnameFormat, UpstreamProxyConfig,
default_sandbox_pids_limit, normalize_compute_driver_name,
};
use std::net::SocketAddr;
use std::time::Duration;
use std::{net::SocketAddr, path::PathBuf};

#[test]
fn policy_validation_failure_mode_is_secure_by_default() {
Expand Down Expand Up @@ -1896,6 +1932,23 @@ mod tests {
assert_eq!(cfg.health_bind_address, Some(addr));
}

#[test]
fn metrics_tls_is_disabled_by_default_and_can_be_configured() {
assert!(Config::new(None).metrics_tls.is_none());

let tls = MetricsTlsConfig {
cert_path: PathBuf::from("/etc/openshell-metrics/tls.crt"),
key_path: PathBuf::from("/etc/openshell-metrics/tls.key"),
client_ca_path: Some(PathBuf::from("/etc/openshell-metrics/ca.crt")),
require_client_auth: true,
};

assert_eq!(
Config::new(None).with_metrics_tls(tls.clone()).metrics_tls,
Some(tls)
);
}

#[test]
fn supervisor_image_tag_prefers_explicit_build_tags() {
use super::resolve_supervisor_image_tag;
Expand Down
4 changes: 2 additions & 2 deletions crates/openshell-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ pub use config::{
AppArmorProfile, Config, ExtensionBindingPolicy, GatewayAuthConfig,
GatewayInterceptorBindingOverride, GatewayInterceptorBindingPolicy, GatewayInterceptorConfig,
GatewayInterceptorFailurePolicy, GatewayInterceptorPhaseConfig, GatewayJwtConfig,
GatewayProviderProfileSourceConfig, ImagePullPolicy, MtlsAuthConfig, OidcConfig,
PolicyValidationFailureMode, TlsConfig, UpstreamProxyConfig,
GatewayProviderProfileSourceConfig, ImagePullPolicy, MetricsTlsConfig, MtlsAuthConfig,
OidcConfig, PolicyValidationFailureMode, TlsConfig, UpstreamProxyConfig,
};
pub use dynamic_string_allowlist::DynamicStringAllowlist;
pub use error::{ComputeDriverError, Error, Result};
Expand Down
Loading
Loading