Skip to content

Commit

Permalink
feat(mTLS): adds mTLS support to dataplane api-server
Browse files Browse the repository at this point in the history
  • Loading branch information
EandrewJones committed Sep 19, 2024
1 parent c8151d5 commit dab1f86
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 57 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions dataplane/api-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ version.workspace = true
[dependencies]
anyhow = { workspace = true }
aya = { workspace = true, features = ["async_tokio"] }
clap = { workspace = true }
common = { workspace = true, features = ["user"] }
libc = { workspace = true }
log = { workspace = true }
Expand Down
61 changes: 61 additions & 0 deletions dataplane/api-server/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
Copyright 2023 The Kubernetes Authors.
SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
*/
use clap::Parser;
use std::path::PathBuf;

#[derive(Debug, Parser, Clone)]
pub struct GrpcConfig {
#[clap(long, default_value = "false")]
pub enable_tls: bool,
#[clap(long, default_value = "false")]
pub enable_mtls: bool,
pub certificate_authority_root_path: Option<PathBuf>,
pub server_certificate_path: Option<PathBuf>,
pub server_private_key_path: Option<PathBuf>,
pub client_certificate_authority_root_path: Option<PathBuf>,
pub client_certificate_path: Option<PathBuf>,
pub client_private_key_path: Option<PathBuf>,
}

impl GrpcConfig {
pub fn validate(&self) -> Result<(), String> {
fn validate_paths(paths: &[(&Option<PathBuf>, &str)]) -> Result<(), String> {
for (path, name) in paths {
if path.is_none() {
return Err(format!("Missing required path: {}", name));
}
}
Ok(())
}

let tls_paths = &[
(
&self.certificate_authority_root_path,
"certificate_authority_root_path",
),
(&self.server_certificate_path, "server_certificate_path"),
(&self.server_private_key_path, "server_private_key_path"),
];

let mtls_paths = &[
(
&self.client_certificate_authority_root_path,
"client_certificate_authority_root_path",
),
(&self.client_certificate_path, "client_certificate_path"),
(&self.client_private_key_path, "client_private_key_path"),
];

if self.enable_mtls {
validate_paths(tls_paths)?;
validate_paths(mtls_paths)?;
} else if self.enable_tls {
validate_paths(tls_paths)?;
}

Ok(())
}
}
53 changes: 49 additions & 4 deletions dataplane/api-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,78 @@ SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
*/

pub mod backends;
pub mod config;
pub mod netutils;
pub mod server;

use std::net::{Ipv4Addr, SocketAddrV4};
use std::{
fs,
net::{Ipv4Addr, SocketAddrV4},
};

use anyhow::Error;
use aya::maps::{HashMap, MapData};
use tonic::transport::Server;
use log::info;
use tonic::transport::{Certificate, Identity, Server, ServerTlsConfig};

use backends::backends_server::BackendsServer;
use common::{BackendKey, BackendList, ClientKey, LoadBalancerMapping};
use config::GrpcConfig;

pub async fn start(
addr: Ipv4Addr,
port: u16,
backends_map: HashMap<MapData, BackendKey, BackendList>,
gateway_indexes_map: HashMap<MapData, BackendKey, u16>,
tcp_conns_map: HashMap<MapData, ClientKey, LoadBalancerMapping>,
tls_config: GrpcConfig,
) -> Result<(), Error> {
let (_, health_service) = tonic_health::server::health_reporter();

let server = server::BackendService::new(backends_map, gateway_indexes_map, tcp_conns_map);
// TODO: mTLS https://github.com/Kong/blixt/issues/50
Server::builder()
let mut server_builder = Server::builder();
server_builder = setup_tls(server_builder, &tls_config);
server_builder
.add_service(health_service)
.add_service(BackendsServer::new(server))
.serve(SocketAddrV4::new(addr, port).into())
.await?;
Ok(())
}

fn setup_tls(mut builder: Server, tls_config: &GrpcConfig) -> Server {
if tls_config.enable_tls || tls_config.enable_mtls {
let mut tls = ServerTlsConfig::new();

if let Some(cert_path) = &tls_config.server_certificate_path {
let cert = fs::read_to_string(cert_path).expect("Error reading server certificate");
let key = fs::read_to_string(
tls_config
.server_private_key_path
.as_ref()
.expect("Missing private key path"),
)
.expect("Error reading server private key");
let server_identity = Identity::from_pem(cert, key);
tls = tls.identity(server_identity);
}

if tls_config.enable_mtls {
if let Some(client_ca_cert_path) = &tls_config.client_certificate_authority_root_path {
let client_ca_cert =
fs::read_to_string(client_ca_cert_path).expect("Error reading CA certificate");
let client_ca_root = Certificate::from_pem(client_ca_cert);
tls = tls.client_ca_root(client_ca_root);

builder = builder
.tls_config(tls)
.expect("Error adding mTLS to server");
info!("gRPC mTLS enabled");
return builder;
}
}
builder = builder.tls_config(tls).expect("Error adding TLS to server");
info!("gRPC TLS enabled");
}
builder
}
110 changes: 57 additions & 53 deletions dataplane/loader/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
use std::net::Ipv4Addr;

use anyhow::Context;
use api_server::config::GrpcConfig;
use api_server::start as start_api_server;
use aya::maps::HashMap;
use aya::programs::{tc, SchedClassifier, TcAttachType};
Expand All @@ -20,6 +21,8 @@ use log::{info, warn};
struct Opt {
#[clap(short, long, default_value = "lo")]
iface: String,
#[clap(flatten)]
tls_config: GrpcConfig,
}

#[tokio::main]
Expand All @@ -28,59 +31,60 @@ async fn main() -> Result<(), anyhow::Error> {

env_logger::init();

info!("loading ebpf programs");

#[cfg(debug_assertions)]
let mut bpf = Bpf::load(include_bytes_aligned!(
"../../target/bpfel-unknown-none/debug/loader"
))?;
#[cfg(not(debug_assertions))]
let mut bpf = Bpf::load(include_bytes_aligned!(
"../../target/bpfel-unknown-none/release/loader"
))?;
if let Err(e) = BpfLogger::init(&mut bpf) {
warn!("failed to initialize eBPF logger: {}", e);
}

info!("attaching tc_ingress program to {}", &opt.iface);

let _ = tc::qdisc_add_clsact(&opt.iface);
let ingress_program: &mut SchedClassifier =
bpf.program_mut("tc_ingress").unwrap().try_into()?;
ingress_program.load()?;
ingress_program
.attach(&opt.iface, TcAttachType::Ingress)
.context("failed to attach the ingress TC program")?;

info!("attaching tc_egress program to {}", &opt.iface);

let egress_program: &mut SchedClassifier =
bpf.program_mut("tc_egress").unwrap().try_into()?;
egress_program.load()?;
egress_program
.attach(&opt.iface, TcAttachType::Egress)
.context("failed to attach the egress TC program")?;

info!("starting api server");
let backends: HashMap<_, BackendKey, BackendList> =
HashMap::try_from(bpf.take_map("BACKENDS").expect("no maps named BACKENDS"))?;
let gateway_indexes: HashMap<_, BackendKey, u16> = HashMap::try_from(
bpf.take_map("GATEWAY_INDEXES")
.expect("no maps named GATEWAY_INDEXES"),
)?;
let tcp_conns: HashMap<_, ClientKey, LoadBalancerMapping> = HashMap::try_from(
bpf.take_map("LB_CONNECTIONS")
.expect("no maps named LB_CONNECTIONS"),
)?;

start_api_server(
Ipv4Addr::new(0, 0, 0, 0),
9874,
backends,
gateway_indexes,
tcp_conns,
)
.await?;
info!("loading ebpf programs");

#[cfg(debug_assertions)]
let mut bpf = Bpf::load(include_bytes_aligned!(
"../../target/bpfel-unknown-none/debug/loader"
))?;
#[cfg(not(debug_assertions))]
let mut bpf = Bpf::load(include_bytes_aligned!(
"../../target/bpfel-unknown-none/release/loader"
))?;
if let Err(e) = BpfLogger::init(&mut bpf) {
warn!("failed to initialize eBPF logger: {}", e);
}

info!("attaching tc_ingress program to {}", &opt.iface);

let _ = tc::qdisc_add_clsact(&opt.iface);
let ingress_program: &mut SchedClassifier =
bpf.program_mut("tc_ingress").unwrap().try_into()?;
ingress_program.load()?;
ingress_program
.attach(&opt.iface, TcAttachType::Ingress)
.context("failed to attach the ingress TC program")?;

info!("attaching tc_egress program to {}", &opt.iface);

let egress_program: &mut SchedClassifier = bpf.program_mut("tc_egress").unwrap().try_into()?;
egress_program.load()?;
egress_program
.attach(&opt.iface, TcAttachType::Egress)
.context("failed to attach the egress TC program")?;

info!("starting api server");
info!("Using tls config: {:?}", &opt.tls_config);
let backends: HashMap<_, BackendKey, BackendList> =
HashMap::try_from(bpf.take_map("BACKENDS").expect("no maps named BACKENDS"))?;
let gateway_indexes: HashMap<_, BackendKey, u16> = HashMap::try_from(
bpf.take_map("GATEWAY_INDEXES")
.expect("no maps named GATEWAY_INDEXES"),
)?;
let tcp_conns: HashMap<_, ClientKey, LoadBalancerMapping> = HashMap::try_from(
bpf.take_map("LB_CONNECTIONS")
.expect("no maps named LB_CONNECTIONS"),
)?;

start_api_server(
Ipv4Addr::new(0, 0, 0, 0),
9874,
backends,
gateway_indexes,
tcp_conns,
opt.tls_config,
)
.await?;

info!("Exiting...");

Expand Down

0 comments on commit dab1f86

Please sign in to comment.