diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index f0f95075..16c39c35 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -74,7 +74,7 @@ jobs: cache-to: type=registry,mode=max,ref=${{ env.GHCR_REPO }}:cache-${{ matrix.tag }}-${{ env.SAFE_REF }} - name: Scan image with Trivy - uses: aquasecurity/trivy-action@0.33.1 + uses: aquasecurity/trivy-action@0.34.1 with: image-ref: "${{ env.GHCR_REPO }}:${{ github.sha }}-${{ matrix.tag }}" format: "table" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4d79ae06..4c414950 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,7 @@ jobs: submodules: recursive - name: Scan code with Trivy - uses: aquasecurity/trivy-action@0.33.1 + uses: aquasecurity/trivy-action@0.34.1 with: scan-type: 'fs' scan-ref: '.' diff --git a/.github/workflows/sbom.yml b/.github/workflows/sbom.yml index 9f2fc8bb..d0fbc4a1 100644 --- a/.github/workflows/sbom.yml +++ b/.github/workflows/sbom.yml @@ -33,7 +33,7 @@ jobs: submodules: recursive - name: Create SBOM with Trivy - uses: aquasecurity/trivy-action@0.33.1 + uses: aquasecurity/trivy-action@0.34.1 with: scan-type: 'fs' format: 'spdx-json' @@ -43,7 +43,7 @@ jobs: scanners: "vuln" - name: Create docker image SBOM with Trivy - uses: aquasecurity/trivy-action@0.33.1 + uses: aquasecurity/trivy-action@0.34.1 with: image-ref: "ghcr.io/defguard/gateway:${{ steps.vars.outputs.VERSION }}" scan-type: 'image' @@ -53,7 +53,7 @@ jobs: scanners: "vuln" - name: Create security advisory file with Trivy - uses: aquasecurity/trivy-action@0.33.1 + uses: aquasecurity/trivy-action@0.34.1 with: scan-type: 'fs' format: 'json' @@ -63,7 +63,7 @@ jobs: scanners: "vuln" - name: Create docker image security advisory file with Trivy - uses: aquasecurity/trivy-action@0.33.1 + uses: aquasecurity/trivy-action@0.34.1 with: image-ref: "ghcr.io/defguard/gateway:${{ steps.vars.outputs.VERSION }}" scan-type: 'image' diff --git a/Cargo.lock b/Cargo.lock index 5aebee51..445485e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -427,6 +427,7 @@ dependencies = [ "iana-time-zone", "js-sys", "num-traits", + "serde", "wasm-bindgen", "windows-link", ] @@ -710,12 +711,12 @@ dependencies = [ [[package]] name = "defguard_certs" version = "0.0.0" -source = "git+https://github.com/DefGuard/defguard.git?rev=290bdee718f51179c71e07f3bce3f8a0cbfb9379#290bdee718f51179c71e07f3bce3f8a0cbfb9379" +source = "git+https://github.com/DefGuard/defguard.git?rev=9c6cbd5108470f9c8dc9b4ee740a9a08f071468c#9c6cbd5108470f9c8dc9b4ee740a9a08f071468c" dependencies = [ "base64", + "chrono", "rcgen", "rustls-pki-types", - "serde", "sqlx", "thiserror 2.0.18", "time", @@ -725,7 +726,7 @@ dependencies = [ [[package]] name = "defguard_version" version = "0.0.0" -source = "git+https://github.com/DefGuard/defguard.git?rev=5be16525f5208739fd79384b30d8ac5056ffdb2f#5be16525f5208739fd79384b30d8ac5056ffdb2f" +source = "git+https://github.com/DefGuard/defguard.git?rev=9c6cbd5108470f9c8dc9b4ee740a9a08f071468c#9c6cbd5108470f9c8dc9b4ee740a9a08f071468c" dependencies = [ "axum", "http", diff --git a/Cargo.toml b/Cargo.toml index b0f1d550..2f4bb851 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,10 +6,11 @@ edition = "2024" [dependencies] axum = "0.8" base64 = "0.22" +chrono = "0.4" clap = { version = "4.5", features = ["derive", "env"] } -defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "5be16525f5208739fd79384b30d8ac5056ffdb2f" } +defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "9c6cbd5108470f9c8dc9b4ee740a9a08f071468c" } defguard_wireguard_rs = { git = "https://github.com/DefGuard/wireguard-rs", rev = "d0b01eabca015ea6c7ddf4e255a0228074684e96" } -defguard_certs = { git = "https://github.com/DefGuard/defguard.git", rev = "290bdee718f51179c71e07f3bce3f8a0cbfb9379" } +defguard_certs = { git = "https://github.com/DefGuard/defguard.git", rev = "9c6cbd5108470f9c8dc9b4ee740a9a08f071468c" } env_logger = "0.11" gethostname = "1.0" ipnetwork = "0.21" @@ -32,11 +33,10 @@ tonic = { version = "0.14", default-features = false, features = [ "tls-native-roots", "tls-ring", ] } -tracing = "0.1" tonic-prost = "0.14" tower = "0.5" -chrono = "0.4.43" -tracing-subscriber = "0.3.22" +tracing = "0.1" +tracing-subscriber = "0.3" [target.'cfg(target_os = "linux")'.dependencies] nftnl = { git = "https://github.com/DefGuard/nftnl-rs.git", rev = "f30cbf9c0e081aed7d8d8740eabdf07109a70373" } diff --git a/src/main.rs b/src/main.rs index 69889118..3aba3423 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +#[cfg(unix)] +use std::{fs::Permissions, os::unix::fs::PermissionsExt}; use std::{ fs::{File, read_to_string}, io::Write, @@ -39,6 +41,8 @@ async fn main() -> Result<(), GatewayError> { let cert_dir = &config.cert_dir; if !cert_dir.exists() { tokio::fs::create_dir_all(cert_dir).await?; + #[cfg(unix)] + tokio::fs::set_permissions(cert_dir, Permissions::from_mode(0o700)).await?; } let (grpc_cert, grpc_key) = ( diff --git a/src/setup.rs b/src/setup.rs index 7e8d0bf7..75568fa6 100644 --- a/src/setup.rs +++ b/src/setup.rs @@ -1,10 +1,15 @@ use std::{ net::{IpAddr, Ipv4Addr, SocketAddr}, + path::Path, sync::{Arc, Mutex}, }; use defguard_version::{Version, server::DefguardVersionLayer}; -use tokio::sync::{mpsc, oneshot}; +use tokio::{ + fs::OpenOptions, + io::AsyncWriteExt, + sync::{mpsc, oneshot}, +}; use tokio_stream::wrappers::UnboundedReceiverStream; use tonic::{Request, Response, Status, transport::Server}; use tower::ServiceBuilder; @@ -23,16 +28,32 @@ type LogsReceiver = Arc>>; pub async fn run_setup( config: &Config, - cert_dir: &std::path::Path, + cert_dir: &Path, logs_rx: Arc>>, ) -> Result { let setup_server = GatewaySetupServer::new(logs_rx); - let tls_config = setup_server.await_setup(config.clone()).await?; + let tls_config = setup_server.await_setup(config).await?; let cert_path = cert_dir.join(GRPC_CERT_NAME); let key_path = cert_dir.join(GRPC_KEY_NAME); - tokio::fs::write(cert_path, &tls_config.grpc_cert_pem).await?; - tokio::fs::write(key_path, &tls_config.grpc_key_pem).await?; + // Certificate and its key will be accessed only to this process's user. + let mut options = OpenOptions::new(); + options.write(true).create(true).truncate(true); + #[cfg(unix)] + options.mode(0o600); // rw------- + // Write certificate to a file. + options + .clone() + .open(cert_path) + .await? + .write_all(tls_config.grpc_cert_pem.as_bytes()) + .await?; + // Write key to a file. + options + .open(key_path) + .await? + .write_all(tls_config.grpc_key_pem.as_bytes()) + .await?; log::info!( "Generated gRPC TLS certificates have been saved to {}", cert_dir.display() @@ -74,7 +95,7 @@ impl GatewaySetupServer { } } - pub async fn await_setup(&self, config: Config) -> Result { + pub async fn await_setup(&self, config: &Config) -> Result { let mut server_builder = Server::builder(); let mut server_config = None; let setup_rx = Arc::clone(&self.setup_rx); @@ -86,10 +107,6 @@ impl GatewaySetupServer { server_builder .add_service( ServiceBuilder::new() - // .layer(InterceptorLayer::new(CoreVersionInterceptor::new( - // MIN_CORE_VERSION, - // incompatible_components, - // ))) .layer(DefguardVersionLayer::new(Version::parse(VERSION)?)) .service(gateway_setup_server::GatewaySetupServer::new(self.clone())), ) @@ -251,7 +268,7 @@ impl gateway_setup_server::GatewaySetup for GatewaySetupServer { }; debug!("Key pair created"); - let subject_alt_names = vec![setup_info.cert_hostname]; + let subject_alt_names = [setup_info.cert_hostname]; debug!("Preparing Certificate Signing Request for hostname: {subject_alt_names:?}",); let csr = match defguard_certs::Csr::new( @@ -274,7 +291,10 @@ impl gateway_setup_server::GatewaySetup for GatewaySetupServer { self.key_pair .lock() - .expect("Failed to acquire lock on key pair during gateway setup when trying to store generated key pair") + .expect( + "Failed to acquire lock on key pair during gateway setup when trying to store \ + generated key pair", + ) .replace(key_pair); debug!("Encoding Certificate Signing Request for transmission"); @@ -331,17 +351,22 @@ impl gateway_setup_server::GatewaySetup for GatewaySetupServer { let key_pair = self .key_pair .lock() - .expect("Failed to acquire lock on key pair during gateway setup when trying to receive certificate") + .expect( + "Failed to acquire lock on key pair during gateway setup when trying to \ + receive certificate", + ) .take(); if let Some(kp) = key_pair { kp } else { error!( - "Key pair not found during Gateway setup. Key pair generation step might have failed." + "Key pair not found during Gateway setup. Key pair generation step might have \ + failed." ); self.clear_setup_session(); return Err(Status::internal( - "Key pair not found during Gateway setup. Key pair generation step might have failed.", + "Key pair not found during Gateway setup. Key pair generation step might have \ + failed.", )); } };