Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
43 changes: 43 additions & 0 deletions crates/dhcp-server/src/command_line.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,23 @@ pub struct Args {
)]
pub host_config: Option<String>,

#[arg(long, help = "Root CA certificate used to connect to the Carbide API.")]
pub forge_root_ca_path: Option<String>,

#[arg(
long,
requires = "client_key_path",
help = "Client certificate used to connect to the Carbide API."
)]
pub client_cert_path: Option<String>,

#[arg(
long,
requires = "client_cert_path",
help = "Client private key used to connect to the Carbide API."
)]
pub client_key_path: Option<String>,

#[arg(short, long, value_enum, default_value_t=ServerMode::Dpu)]
pub mode: ServerMode,

Expand Down Expand Up @@ -99,6 +116,9 @@ mod tests {
SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 67)
);
assert_eq!(defaults.relay_response_port, 67);
assert_eq!(defaults.forge_root_ca_path, None);
assert_eq!(defaults.client_cert_path, None);
assert_eq!(defaults.client_key_path, None);

let overridden = Args::try_parse_from([
"forge-dhcp-server",
Expand All @@ -117,5 +137,28 @@ mod tests {
assert!(
Args::try_parse_from(["forge-dhcp-server", "--listen-addr", "[::]:6767",]).is_err()
);

let tls = Args::try_parse_from([
"forge-dhcp-server",
"--forge-root-ca-path",
"/local/ca.crt",
"--client-cert-path",
"/local/client.crt",
"--client-key-path",
"/local/client.key",
])
.unwrap();
assert_eq!(tls.forge_root_ca_path.as_deref(), Some("/local/ca.crt"));
assert_eq!(tls.client_cert_path.as_deref(), Some("/local/client.crt"));
assert_eq!(tls.client_key_path.as_deref(), Some("/local/client.key"));

assert!(
Args::try_parse_from([
"forge-dhcp-server",
"--client-cert-path",
"/local/client.crt",
])
.is_err()
);
}
}
63 changes: 62 additions & 1 deletion crates/dhcp-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,15 @@ use std::net::SocketAddr;
use std::sync::Arc;

use ::rpc::forge::{DhcpDiscovery, DhcpRecord};
use ::rpc::forge_tls_client::ForgeClientConfig;
use cache::CacheEntry;
use carbide_instrument::emit;
use carbide_rpc_utils::dhcp::{DhcpConfig, DhcpTimestamps, DhcpTimestampsFilePath, HostConfig};
use chrono::Utc;
use command_line::{Args, ServerMode};
use errors::DhcpError;
use forge_tls::client_config::ClientCert;
use forge_tls::default::{default_client_cert, default_client_key, default_root_ca};
use grpc_server::{ControlRequest, run_grpc_server};
use lru::LruCache;
use metrics::{DhcpPacketDropped, DhcpReplySent, DropReason};
Expand Down Expand Up @@ -558,9 +561,11 @@ pub struct Config {
dhcp_config: DhcpConfig,
host_config: Option<HostConfig>, // Valid only for Dpu mode.
relay_response_port: u16,
forge_client_config: ForgeClientConfig,
}

async fn init(args: Args) -> Result<Config, DhcpError> {
let forge_client_config = forge_client_config(&args)?;
let f = tokio::fs::read_to_string(args.dhcp_config).await?;
let dhcp_config: DhcpConfig = serde_yaml::from_str(&f)?;

Expand All @@ -575,9 +580,34 @@ async fn init(args: Args) -> Result<Config, DhcpError> {
dhcp_config,
host_config,
relay_response_port: args.relay_response_port,
forge_client_config,
})
}

fn forge_client_config(args: &Args) -> Result<ForgeClientConfig, DhcpError> {
let root_ca_path = args
.forge_root_ca_path
.clone()
.unwrap_or_else(|| default_root_ca().to_string());
let client_cert = match (&args.client_cert_path, &args.client_key_path) {
(Some(cert_path), Some(key_path)) => ClientCert {
cert_path: cert_path.clone(),
key_path: key_path.clone(),
},
(None, None) => ClientCert {
cert_path: default_client_cert().to_string(),
key_path: default_client_key().to_string(),
},
_ => {
return Err(DhcpError::MissingArgument(
"client_cert_path and client_key_path must be configured together".to_string(),
));
}
};

Ok(ForgeClientConfig::new(root_ca_path, Some(client_cert)))
}

#[derive(Debug)]
pub struct TestArm {}

Expand Down Expand Up @@ -733,7 +763,10 @@ mod test {

use crate::command_line::{Args, ServerMode};
use crate::errors::DhcpError;
use crate::{DhcpMode, Test, TestArm, cache, handle_reload, init, packet_handler, process};
use crate::{
DhcpMode, Test, TestArm, cache, forge_client_config, handle_reload, init, packet_handler,
process,
};

fn make_reload_args(td: &TempDir, interfaces: Vec<String>) -> Args {
Args {
Expand All @@ -742,6 +775,9 @@ mod test {
relay_response_port: 67,
dhcp_config: td.path().join("dhcp.yaml").display().to_string(),
host_config: Some(td.path().join("host.yaml").display().to_string()),
forge_root_ca_path: None,
client_cert_path: None,
client_key_path: None,
mode: ServerMode::Dpu,
grpc_listen_addr: None,
metrics_listen_addr: None,
Expand Down Expand Up @@ -914,6 +950,9 @@ mod test {
.display()
.to_string(),
),
forge_root_ca_path: None,
client_cert_path: None,
client_key_path: None,
mode: crate::command_line::ServerMode::Dpu,
grpc_listen_addr: None,
metrics_listen_addr: None,
Expand All @@ -925,6 +964,28 @@ mod test {
init(get_test_args()).await.unwrap();
}

#[test]
fn forge_client_tls_paths_are_configurable() {
let defaults = forge_client_config(&get_test_args()).unwrap();
assert_eq!(defaults.root_ca_path, forge_tls::default::ROOT_CA);
let default_identity = defaults.client_cert.unwrap();
assert_eq!(default_identity.cert_path, forge_tls::default::CLIENT_CERT);
assert_eq!(default_identity.key_path, forge_tls::default::CLIENT_KEY);

let mut explicit = get_test_args();
explicit.forge_root_ca_path = Some("/local/ca.crt".to_string());
explicit.client_cert_path = Some("/local/client.crt".to_string());
explicit.client_key_path = Some("/local/client.key".to_string());
let configured = forge_client_config(&explicit).unwrap();
assert_eq!(configured.root_ca_path, "/local/ca.crt");
let configured_identity = configured.client_cert.unwrap();
assert_eq!(configured_identity.cert_path, "/local/client.crt");
assert_eq!(configured_identity.key_path, "/local/client.key");

explicit.client_key_path = None;
assert!(forge_client_config(&explicit).is_err());
}

#[tokio::test]
async fn test_arm_non_relayed_packet() {
let byte_stream = get_byte_stream(Ipv4Addr::new(0, 0, 0, 0), None, MessageType::Request);
Expand Down
14 changes: 2 additions & 12 deletions crates/dhcp-server/src/rpc/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use forge_tls::client_config::ClientCert;
use forge_tls::default::{default_client_cert, default_client_key, default_root_ca};
use rpc::forge::{DhcpDiscovery, DhcpRecord};
use rpc::forge_tls_client::{ApiConfig, ForgeClientConfig, ForgeTlsClient};
use rpc::forge_tls_client::{ApiConfig, ForgeTlsClient};

use crate::Config;
use crate::errors::DhcpError;
Expand All @@ -32,15 +30,7 @@ pub async fn discover_dhcp(
));
};

let client_config = ForgeClientConfig::new(
default_root_ca().to_string(),
Some(ClientCert {
cert_path: default_client_cert().to_string(),
key_path: default_client_key().to_string(),
}),
);

let api_config = ApiConfig::new(carbide_api_url, &client_config);
let api_config = ApiConfig::new(carbide_api_url, &config.forge_client_config);

let mut client = ForgeTlsClient::retry_build(&api_config)
.await
Expand Down
Loading