Skip to content

Commit bf5deb2

Browse files
authored
Gateway wizard (#257)
* gateway wizard 1 * cargo lock * lsb-release * Update proto * update defguard version
1 parent 780769e commit bf5deb2

8 files changed

Lines changed: 342 additions & 100 deletions

File tree

Cargo.lock

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

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ edition = "2024"
77
axum = "0.8"
88
base64 = "0.22"
99
clap = { version = "4.5", features = ["derive", "env"] }
10-
defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "640bae9a0aea1e11395f0a29fb8c84eeefd7f115" }
10+
defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "5be16525f5208739fd79384b30d8ac5056ffdb2f" }
1111
defguard_wireguard_rs = { git = "https://github.com/DefGuard/wireguard-rs", rev = "d0b01eabca015ea6c7ddf4e255a0228074684e96" }
1212
defguard_certs = { git = "https://github.com/DefGuard/defguard.git", rev = "290bdee718f51179c71e07f3bce3f8a0cbfb9379" }
1313
env_logger = "0.11"
@@ -35,6 +35,8 @@ tonic = { version = "0.14", default-features = false, features = [
3535
tracing = "0.1"
3636
tonic-prost = "0.14"
3737
tower = "0.5"
38+
chrono = "0.4.43"
39+
tracing-subscriber = "0.3.22"
3840

3941
[target.'cfg(target_os = "linux")'.dependencies]
4042
nftnl = { git = "https://github.com/DefGuard/nftnl-rs.git", rev = "1a1147271f43b9d7182a114bb056a5224c35d38f" }

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ RUN cargo build --release
77

88
FROM public.ecr.aws/docker/library/debian:13-slim
99
RUN apt-get update && apt-get -y --no-install-recommends install \
10-
iproute2 wireguard-tools sudo ca-certificates iptables ebtables nftables && \
10+
iproute2 wireguard-tools sudo ca-certificates iptables ebtables nftables lsb-release && \
1111
apt-get clean && rm -rf /var/lib/apt/lists/*
1212
WORKDIR /app
1313
COPY --from=builder /app/target/release/defguard-gateway /usr/local/bin

proto

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use syslog::{BasicLogger, Facility, Formatter3164};
2727
use tokio::sync::oneshot;
2828

2929
pub mod enterprise;
30+
pub mod logging;
3031
pub mod setup;
3132

3233
pub const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "+", env!("VERGEN_GIT_SHA"));

src/logging.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use defguard_version::Version;
2+
use tokio::sync::mpsc::Sender;
3+
use tracing::{Event, Subscriber};
4+
use tracing_subscriber::{Layer, layer::SubscriberExt, util::SubscriberInitExt};
5+
6+
use crate::proto::gateway::LogEntry;
7+
8+
pub fn init_tracing(own_version: &Version, level: &str, logs_tx: Option<Sender<LogEntry>>) {
9+
let subscriber = tracing_subscriber::registry();
10+
let subscriber =
11+
defguard_version::tracing::with_version_formatters(own_version, level, subscriber);
12+
13+
if let Some(tx) = logs_tx {
14+
let sender_layer = LogSenderLayer::new(tx);
15+
subscriber.with(sender_layer).init();
16+
} else {
17+
subscriber.init();
18+
}
19+
20+
info!("Tracing initialized");
21+
}
22+
23+
/// A tracing layer that sends log entries to a gRPC logs channel.
24+
pub struct LogSenderLayer {
25+
logs_tx: Sender<LogEntry>,
26+
}
27+
28+
impl LogSenderLayer {
29+
#[must_use]
30+
pub const fn new(logs_tx: Sender<LogEntry>) -> Self {
31+
Self { logs_tx }
32+
}
33+
}
34+
35+
impl<S> Layer<S> for LogSenderLayer
36+
where
37+
S: Subscriber,
38+
{
39+
fn on_event(&self, event: &Event<'_>, _ctx: tracing_subscriber::layer::Context<'_, S>) {
40+
if self.logs_tx.is_closed() {
41+
return;
42+
}
43+
44+
let mut visitor = LogVisitor::default();
45+
event.record(&mut visitor);
46+
47+
let entry = LogEntry {
48+
level: format!("{:?}", event.metadata().level()),
49+
target: event.metadata().target().to_string(),
50+
message: visitor.message,
51+
timestamp: chrono::Utc::now().to_rfc3339(),
52+
fields: visitor.fields,
53+
};
54+
55+
// Drop the buffer overflow error for now
56+
let _ = self.logs_tx.try_send(entry);
57+
}
58+
}
59+
60+
#[derive(Default)]
61+
struct LogVisitor {
62+
message: String,
63+
fields: std::collections::HashMap<String, String>,
64+
}
65+
66+
impl tracing::field::Visit for LogVisitor {
67+
fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
68+
if field.name() == "message" {
69+
self.message = format!("{value:?}");
70+
} else {
71+
self.fields
72+
.insert(field.name().to_string(), format!("{value:?}"));
73+
}
74+
}
75+
}

src/main.rs

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@ use defguard_gateway::{
1313
execute_command,
1414
gateway::{Gateway, GatewayServer, TlsConfig, run_stats},
1515
init_syslog,
16+
logging::init_tracing,
1617
server::run_server,
1718
setup::GatewaySetupServer,
1819
};
1920
use defguard_version::Version;
2021
#[cfg(not(any(target_os = "macos", target_os = "netbsd")))]
2122
use defguard_wireguard_rs::Kernel;
2223
use defguard_wireguard_rs::{Userspace, WGApi};
23-
use tokio::task::JoinSet;
24+
use tokio::{sync::mpsc, task::JoinSet};
2425

2526
#[tokio::main]
2627
async fn main() -> Result<(), GatewayError> {
@@ -35,15 +36,35 @@ async fn main() -> Result<(), GatewayError> {
3536
file.write_all(pid.to_string().as_bytes())?;
3637
}
3738

39+
let cert_dir = &config.cert_dir;
40+
if !cert_dir.exists() {
41+
tokio::fs::create_dir_all(cert_dir).await?;
42+
}
43+
44+
let (grpc_cert, grpc_key) = (
45+
read_to_string(cert_dir.join(GRPC_CERT_NAME)).ok(),
46+
read_to_string(cert_dir.join(GRPC_KEY_NAME)).ok(),
47+
);
48+
49+
let needs_setup = grpc_cert.is_none() || grpc_key.is_none();
50+
51+
// TODO: The channel size may need to be adjusted or some other approach should be used
52+
// to avoid dropping log messages.
53+
let (logs_tx, logs_rx) = if needs_setup {
54+
let (logs_tx, logs_rx) = mpsc::channel(200);
55+
(Some(logs_tx), Some(logs_rx))
56+
} else {
57+
(None, None)
58+
};
59+
3860
// setup logging
3961
if config.use_syslog {
4062
if let Err(error) = init_syslog(&config, pid) {
4163
log::error!("Unable to initialize syslog. Is the syslog daemon running?");
4264
return Err(error);
4365
}
4466
} else {
45-
let version = Version::parse(VERSION)?;
46-
defguard_version::tracing::init(version, &config.log_level)?;
67+
init_tracing(&Version::parse(VERSION)?, &config.log_level, logs_tx);
4768
}
4869

4970
if let Some(pre_up) = &config.pre_up {
@@ -86,28 +107,19 @@ async fn main() -> Result<(), GatewayError> {
86107
let gateway = Arc::new(Mutex::new(gateway));
87108
tasks.spawn(run_stats(Arc::clone(&gateway), config.stats_period()));
88109

89-
let cert_dir = &config.cert_dir;
90-
if !cert_dir.exists() {
91-
tokio::fs::create_dir_all(cert_dir).await?;
92-
}
93-
let tls_config = if let (Some(cert), Some(key)) = (
94-
read_to_string(cert_dir.join(GRPC_CERT_NAME)).ok(),
95-
read_to_string(cert_dir.join(GRPC_KEY_NAME)).ok(),
96-
) {
97-
log::info!(
98-
"Using existing gRPC TLS certificates from {}",
99-
cert_dir.display()
100-
);
101-
TlsConfig {
102-
grpc_cert_pem: cert,
103-
grpc_key_pem: key,
104-
}
105-
} else {
110+
let tls_config = if needs_setup {
106111
log::info!(
107112
"gRPC TLS certificates not found in {}. They will be generated during setup.",
108113
cert_dir.display()
109114
);
110-
let setup_server = GatewaySetupServer::default();
115+
116+
let Some(logs_rx) = logs_rx else {
117+
return Err(GatewayError::SetupError(
118+
"Logs receiver channel is missing during gateway setup".to_string(),
119+
));
120+
};
121+
122+
let setup_server = GatewaySetupServer::new(Arc::new(tokio::sync::Mutex::new(logs_rx)));
111123
let tls_config = setup_server.await_setup(config.clone()).await?;
112124

113125
let cert_path = cert_dir.join(GRPC_CERT_NAME);
@@ -120,6 +132,19 @@ async fn main() -> Result<(), GatewayError> {
120132
);
121133

122134
tls_config
135+
} else if let (Some(cert), Some(key)) = (grpc_cert, grpc_key) {
136+
log::info!(
137+
"Using existing gRPC TLS certificates from {}",
138+
cert_dir.display()
139+
);
140+
TlsConfig {
141+
grpc_cert_pem: cert,
142+
grpc_key_pem: key,
143+
}
144+
} else {
145+
return Err(GatewayError::SetupError(
146+
"gRPC TLS certificates are missing after setup".to_string(),
147+
));
123148
};
124149

125150
// Launch gRPC server.

0 commit comments

Comments
 (0)