Skip to content

Commit 07b44aa

Browse files
authored
Error handling for network services (#294)
1 parent 3691b82 commit 07b44aa

8 files changed

Lines changed: 75 additions & 84 deletions

File tree

Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/config.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
use std::{fs, net::IpAddr, path::PathBuf, time::Duration};
1+
use std::{
2+
fs,
3+
net::{IpAddr, Ipv4Addr, SocketAddr},
4+
path::PathBuf,
5+
time::Duration,
6+
};
27

38
use clap::Parser;
49
use serde::Deserialize;
@@ -123,6 +128,12 @@ impl Config {
123128
pub fn stats_period(&self) -> Duration {
124129
Duration::from_secs(self.stats_period)
125130
}
131+
132+
/// Return [`SocketAddr`] for gRPC to listen on.
133+
#[must_use]
134+
pub(crate) fn grpc_socket(&self) -> SocketAddr {
135+
SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), self.grpc_port)
136+
}
126137
}
127138

128139
impl Default for Config {

src/error.rs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,12 @@ pub enum GatewayError {
99
#[error("Command {command} execution failed. Error: {error}")]
1010
CommandExecutionFailed { command: String, error: String },
1111

12-
#[error("WireGuard key error")]
13-
KeyDecode(#[from] base64::DecodeError),
14-
1512
#[error("Logger error")]
1613
Logger(#[from] log::SetLoggerError),
1714

1815
#[error("Syslog error")]
1916
Syslog(#[from] syslog::Error),
2017

21-
#[error("Token parsing error")]
22-
Token(#[from] tonic::metadata::errors::InvalidMetadataValue),
23-
2418
#[error("Tonic error")]
2519
Tonic(#[from] tonic::transport::Error),
2620

@@ -33,9 +27,6 @@ pub enum GatewayError {
3327
#[error("WireGuard error {0}")]
3428
WireguardError(#[from] WireguardInterfaceError),
3529

36-
#[error("HTTP error")]
37-
HttpServer(String),
38-
3930
#[error("Invalid CA file. Error")]
4031
InvalidCaFile,
4132

src/gateway.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use std::{
22
collections::HashMap,
3-
net::{IpAddr, Ipv4Addr, SocketAddr},
43
path::PathBuf,
54
str::FromStr,
65
sync::{
@@ -48,7 +47,6 @@ use crate::{
4847
/// restarted in setup mode when a purge request arrives.
4948
pub async fn run_gateway_loop(
5049
config: Config,
51-
cert_dir: std::path::PathBuf,
5250
gateway: Arc<Mutex<Gateway>>,
5351
logs_rx: Arc<tokio::sync::Mutex<mpsc::Receiver<LogEntry>>>,
5452
mut tls_config: TlsConfig,
@@ -59,7 +57,7 @@ pub async fn run_gateway_loop(
5957
let (reset_tx, mut reset_rx) = oneshot::channel();
6058
// Build and start a gRPC server instance wired with the reset signal.
6159
let mut gateway_server =
62-
GatewayServer::new(Arc::clone(&gateway), cert_dir.clone(), reset_tx);
60+
GatewayServer::new(Arc::clone(&gateway), config.cert_dir.clone(), reset_tx);
6361
gateway_server.set_tls_config(tls_config.clone());
6462
let mut server_handle = tokio::spawn(gateway_server.start(config.clone()));
6563

@@ -87,7 +85,7 @@ pub async fn run_gateway_loop(
8785

8886
// Run setup server to obtain new TLS certs, then loop to restart gRPC.
8987
log::info!("Restarting setup server after purge request");
90-
tls_config = run_setup(&config, &cert_dir, Arc::clone(&logs_rx)).await?;
88+
tls_config = run_setup(&config, Arc::clone(&logs_rx)).await?;
9189
}
9290
// Server exited on its own (error or normal shutdown).
9391
result = &mut server_handle => {
@@ -609,7 +607,7 @@ impl GatewayServer {
609607
.map(|c| c.grpc_key_pem.clone());
610608

611609
// Build gRPC server.
612-
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), config.grpc_port);
610+
let addr = config.grpc_socket();
613611
info!("gRPC server is listening on {addr}");
614612
let mut builder = if let (Some(cert), Some(key)) = (grpc_cert, grpc_key) {
615613
let identity = Identity::from_pem(cert, key);
@@ -623,10 +621,6 @@ impl GatewayServer {
623621
builder
624622
.add_service(
625623
ServiceBuilder::new()
626-
// .layer(InterceptorLayer::new(CoreVersionInterceptor::new(
627-
// MIN_CORE_VERSION,
628-
// incompatible_components,
629-
// )))
630624
.layer(DefguardVersionLayer::new(Version::parse(VERSION)?))
631625
.service(gateway_server::GatewayServer::new(self)),
632626
)
@@ -636,7 +630,7 @@ impl GatewayServer {
636630
Ok(())
637631
}
638632

639-
pub fn set_tls_config(&mut self, tls_config: TlsConfig) {
633+
pub(crate) fn set_tls_config(&mut self, tls_config: TlsConfig) {
640634
if let Ok(mut gateway) = self.gateway.lock() {
641635
gateway.tls_config = Some(tls_config);
642636
}

src/main.rs

Lines changed: 31 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use defguard_gateway::{
1515
gateway::{Gateway, TlsConfig, run_gateway_loop, run_stats},
1616
init_syslog,
1717
logging::init_tracing,
18-
server::run_server,
18+
server::run_http_server,
1919
setup::run_setup,
2020
};
2121
use defguard_version::Version;
@@ -93,7 +93,7 @@ async fn main() -> Result<(), GatewayError> {
9393

9494
// Optionally, launch HTTP server to report gateway's health.
9595
if let Some(health_port) = config.health_port {
96-
tasks.spawn(run_server(
96+
tasks.spawn(run_http_server(
9797
health_port,
9898
config.http_bind_address,
9999
Arc::clone(&gateway.connected),
@@ -104,41 +104,40 @@ async fn main() -> Result<(), GatewayError> {
104104
let gateway = Arc::new(Mutex::new(gateway));
105105
tasks.spawn(run_stats(Arc::clone(&gateway), config.stats_period()));
106106

107-
let tls_config = if needs_setup {
108-
log::info!(
109-
"gRPC TLS certificates not found in {}. They will be generated during setup.",
110-
cert_dir.display()
111-
);
112-
run_setup(&config, cert_dir, Arc::clone(&logs_rx)).await?
113-
} else if let (Some(cert), Some(key)) = (grpc_cert, grpc_key) {
114-
log::info!(
115-
"Using existing gRPC TLS certificates from {}",
116-
cert_dir.display()
117-
);
118-
TlsConfig {
119-
grpc_cert_pem: cert,
120-
grpc_key_pem: key,
121-
}
122-
} else {
123-
return Err(GatewayError::SetupError(
124-
"gRPC TLS certificates are missing after setup".to_string(),
125-
));
126-
};
127-
128-
// Launch gRPC server (with purge-triggered setup loop).
129-
tasks.spawn(run_gateway_loop(
130-
config.clone(),
131-
cert_dir.clone(),
132-
gateway,
133-
Arc::clone(&logs_rx),
134-
tls_config,
135-
));
107+
// Clone for later.
108+
let post_down_clone = config.post_down.clone();
109+
110+
tasks.spawn(async move {
111+
let tls_config = if needs_setup {
112+
log::info!(
113+
"gRPC TLS certificates not found in {}. They will be generated during setup.",
114+
config.cert_dir.display()
115+
);
116+
run_setup(&config, Arc::clone(&logs_rx)).await?
117+
} else if let (Some(cert), Some(key)) = (grpc_cert, grpc_key) {
118+
log::info!(
119+
"Using existing gRPC TLS certificates from {}",
120+
config.cert_dir.display()
121+
);
122+
TlsConfig {
123+
grpc_cert_pem: cert,
124+
grpc_key_pem: key,
125+
}
126+
} else {
127+
return Err(GatewayError::SetupError(
128+
"gRPC TLS certificates are missing after setup".to_string(),
129+
));
130+
};
131+
132+
// Launch gRPC server (with purge-triggered setup loop).
133+
run_gateway_loop(config, gateway, Arc::clone(&logs_rx), tls_config).await
134+
});
136135

137136
while let Some(Ok(result)) = tasks.join_next().await {
138137
result?;
139138
}
140139

141-
if let Some(post_down) = &config.post_down {
140+
if let Some(post_down) = &post_down_clone {
142141
log::info!("Executing specified POST_DOWN command: {post_down}");
143142
execute_command(post_down)?;
144143
}

src/server.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ async fn healthcheck<'a>(
2121
}
2222
}
2323

24-
pub async fn run_server(
24+
/// Launch HTTP server with the health check.
25+
pub async fn run_http_server(
2526
http_port: u16,
2627
http_bind_address: Option<IpAddr>,
2728
connected: Arc<AtomicBool>,
@@ -35,9 +36,13 @@ pub async fn run_server(
3536
http_bind_address.unwrap_or(IpAddr::V4(Ipv4Addr::UNSPECIFIED)),
3637
http_port,
3738
);
38-
let listener = TcpListener::bind(&addr).await?;
39+
let listener = TcpListener::bind(&addr).await.inspect_err(|err| {
40+
error!("Failed to bind to {addr}: {err}");
41+
})?;
3942
info!("Health check listening on {addr}");
40-
serve(listener, app.into_make_service())
41-
.await
42-
.map_err(|err| GatewayError::HttpServer(err.to_string()))
43+
44+
// From axum docs: this future will never actually complete or return an error.
45+
let _ = serve(listener, app.into_make_service()).await;
46+
47+
Ok(())
4348
}

src/setup.rs

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
use std::{
2-
net::{IpAddr, Ipv4Addr, SocketAddr},
3-
path::Path,
4-
sync::{Arc, Mutex},
5-
};
1+
use std::sync::{Arc, Mutex};
62

73
use defguard_version::{Version, server::DefguardVersionLayer};
84
use tokio::{
@@ -28,9 +24,9 @@ type LogsReceiver = Arc<tokio::sync::Mutex<mpsc::Receiver<LogEntry>>>;
2824

2925
pub async fn run_setup(
3026
config: &Config,
31-
cert_dir: &Path,
3227
logs_rx: Arc<tokio::sync::Mutex<mpsc::Receiver<LogEntry>>>,
3328
) -> Result<TlsConfig, GatewayError> {
29+
let cert_dir = &config.cert_dir;
3430
let setup_server = GatewaySetupServer::new(logs_rx);
3531
let tls_config = setup_server.await_setup(config).await?;
3632

@@ -100,8 +96,7 @@ impl GatewaySetupServer {
10096
let mut server_config = None;
10197
let setup_rx = Arc::clone(&self.setup_rx);
10298

103-
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), config.grpc_port);
104-
99+
let addr = config.grpc_socket();
105100
info!("Starting Gateway setup server on {addr} and awaiting configuration from Core");
106101

107102
server_builder
@@ -338,11 +333,10 @@ impl gateway_setup_server::GatewaySetup for GatewaySetupServer {
338333
match defguard_certs::der_to_pem(&cert_der, defguard_certs::PemLabel::Certificate) {
339334
Ok(pem) => pem,
340335
Err(err) => {
341-
error!("Failed to convert certificate DER to PEM: {err}");
336+
let msg = format!("Failed to convert certificate DER to PEM: {err}");
337+
error!("{msg}");
342338
self.clear_setup_session();
343-
return Err(Status::internal(format!(
344-
"Failed to convert certificate DER to PEM: {err}"
345-
)));
339+
return Err(Status::internal(msg));
346340
}
347341
};
348342
debug!("Certificate processed successfully");
@@ -359,15 +353,11 @@ impl gateway_setup_server::GatewaySetup for GatewaySetupServer {
359353
if let Some(kp) = key_pair {
360354
kp
361355
} else {
362-
error!(
363-
"Key pair not found during Gateway setup. Key pair generation step might have \
364-
failed."
365-
);
356+
let msg = "Key pair not found during Gateway setup. Key pair generation step might \
357+
have failed.";
358+
error!("{msg}");
366359
self.clear_setup_session();
367-
return Err(Status::internal(
368-
"Key pair not found during Gateway setup. Key pair generation step might have \
369-
failed.",
370-
));
360+
return Err(Status::internal(msg));
371361
}
372362
};
373363

@@ -382,8 +372,9 @@ impl gateway_setup_server::GatewaySetup for GatewaySetupServer {
382372
};
383373

384374
sender.send(configuration).map_err(|_| {
385-
error!("Failed to send setup configuration through channel");
386-
Status::internal("Failed to send setup configuration through channel")
375+
let msg = "Failed to send setup configuration through channel";
376+
error!("{msg}");
377+
Status::internal(msg)
387378
})?;
388379

389380
debug!("Setup process completed successfully, cleaning up temporary session");

src/version.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use defguard_version::{Version, is_version_lower};
22

3-
const MIN_CORE_VERSION: Version = Version::new(1, 6, 0);
3+
const MIN_CORE_VERSION: Version = Version::new(2, 0, 0);
44

55
/// Checks if Defguard Core's version meets minimum version requirements.
66
pub(crate) fn is_core_version_supported(core_version: Option<&Version>) -> bool {

0 commit comments

Comments
 (0)