Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TLS for postgres connections in tests #208

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
31 changes: 31 additions & 0 deletions backend/scripts/generate_test_certs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/usr/bin/env bash

# SPDX-FileCopyrightText: 2024 Phoenix R&D GmbH <[email protected]>
#
# SPDX-License-Identifier: AGPL-3.0-or-later

echo "Test directory: $TEST_CERT_DIR_NAME"

# Check if directory exists
if [ -d "$TEST_CERT_DIR_NAME" ]; then
echo "Directory $TEST_CERT_DIR_NAME already exists. Skipping certificate generation."
else
echo "Directory $TEST_CERT_DIR_NAME does not exist. Creating directory and generating certificates."

# Create directory
mkdir -p "$TEST_CERT_DIR_NAME"

# Generate CA private key and self-signed certificate
openssl req -new -x509 -days 36500 -nodes -out "$TEST_CERT_DIR_NAME/root.crt" -keyout "$TEST_CERT_DIR_NAME/root.key" -subj "/CN=Test Root CA"

# Generate server private key and certificate signing request (CSR)
openssl req -new -nodes -out "$TEST_CERT_DIR_NAME/server.csr" -keyout "$TEST_CERT_DIR_NAME/server.key" -subj "/CN=test.postgres.server"

# Sign the server certificate with the CA certificate
openssl x509 -req -in "$TEST_CERT_DIR_NAME/server.csr" -CA "$TEST_CERT_DIR_NAME/root.crt" -CAkey "$TEST_CERT_DIR_NAME/root.key" -CAcreateserial -out "$TEST_CERT_DIR_NAME/server.crt" -days 36500

# Set permissions for the server key
chmod 600 "$TEST_CERT_DIR_NAME/server.key"

echo "Certificates and configuration file generated in $TEST_CERT_DIR_NAME."
fi
27 changes: 3 additions & 24 deletions backend/scripts/init_db.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,31 +25,10 @@ DB_NAME="${POSTGRES_DB:=phnx_db}"
DB_PORT="${POSTGRES_PORT:=5432}"

# Name of the directory for the test certs
TEST_CERT_DIR_NAME="./test_certs"
export TEST_CERT_DIR_NAME="./test_certs"

# Check if directory exists
if [ -d "$TEST_CERT_DIR_NAME" ]; then
echo "Directory $TEST_CERT_DIR_NAME already exists. Skipping certificate generation."
else
echo "Directory $TEST_CERT_DIR_NAME does not exist. Creating directory and generating certificates."

# Create directory
mkdir -p "$TEST_CERT_DIR_NAME"

# Generate CA private key and self-signed certificate
openssl req -new -x509 -days 36500 -nodes -out "$TEST_CERT_DIR_NAME/root.crt" -keyout "$TEST_CERT_DIR_NAME/root.key" -subj "/CN=Test Root CA"

# Generate server private key and certificate signing request (CSR)
openssl req -new -nodes -out "$TEST_CERT_DIR_NAME/server.csr" -keyout "$TEST_CERT_DIR_NAME/server.key" -subj "/CN=test.postgres.server"

# Sign the server certificate with the CA certificate
openssl x509 -req -in "$TEST_CERT_DIR_NAME/server.csr" -CA "$TEST_CERT_DIR_NAME/root.crt" -CAkey "$TEST_CERT_DIR_NAME/root.key" -CAcreateserial -out "$TEST_CERT_DIR_NAME/server.crt" -days 36500

# Set permissions for the server key
chmod 600 "$TEST_CERT_DIR_NAME/server.key"

echo "Certificates and configuration file generated in $TEST_CERT_DIR_NAME."
fi
SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")"
bash "$SCRIPT_DIR/generate_test_certs.sh"

if [[ -z "${SKIP_DOCKER}" ]]
then
Expand Down
25 changes: 17 additions & 8 deletions backend/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,25 +46,34 @@ impl DatabaseSettings {
fn add_tls_mode(&self, mut connection_string: String) -> String {
if let Some(ref ca_cert_path) = self.cacertpath {
connection_string.push_str(&format!("?sslmode=verify-ca&sslrootcert={}", ca_cert_path));
} else {
tracing::warn!(
"No CA certificate path set for database connection. TLS will not be enabled."
);
}
connection_string
}

/// Compose the base connection string without the database name.
fn base_connection_string(&self) -> String {
format!(
"postgres://{}:{}@{}:{}",
self.username, self.password, self.host, self.port
)
}

/// Get the connection string for the database.
pub fn connection_string(&self) -> String {
let connection_string = format!(
"postgres://{}:{}@{}:{}/{}",
self.username, self.password, self.host, self.port, self.name
);
let mut connection_string = self.base_connection_string();
connection_string.push('/');
connection_string.push_str(&self.name);
self.add_tls_mode(connection_string)
}

/// Get the connection string for the database without the database name.
/// Enables TLS by default.
pub fn connection_string_without_database(&self) -> String {
let connection_string = format!(
"postgres://{}:{}@{}:{}",
self.username, self.password, self.host, self.port
);
let connection_string = self.base_connection_string();
self.add_tls_mode(connection_string)
}
}
83 changes: 83 additions & 0 deletions test_harness/src/docker/container/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// SPDX-FileCopyrightText: 2024 Phoenix R&D GmbH <[email protected]>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

use super::Container;

pub struct ContainerBuilder {
image: String,
name: String,
env: Vec<String>,
hostname: Option<String>,
network: Option<String>,
port: Option<String>,
run_parameters: Vec<String>,
detach: bool,
volumes: Vec<String>,
}

impl ContainerBuilder {
pub fn new(image: &str, name: &str) -> Self {
Self {
image: image.to_string(),
name: name.to_string(),
env: Vec::new(),
hostname: None,
network: None,
port: None,
run_parameters: Vec::new(),
detach: false,
volumes: Vec::new(),
}
}

pub fn with_env(mut self, env: &str) -> Self {
self.env.push(env.to_string());
self
}

pub fn with_hostname(mut self, hostname: &str) -> Self {
self.hostname = Some(hostname.to_string());
self
}

pub fn with_network(mut self, network: &str) -> Self {
self.network = Some(network.to_string());
self
}

pub fn with_port(mut self, port: &str) -> Self {
self.port = Some(port.to_string());
self
}

pub fn with_run_parameters(mut self, parameters: &[&str]) -> Self {
self.run_parameters
.extend(parameters.iter().map(|p| p.to_string()));
self
}

pub fn with_detach(mut self, detach: bool) -> Self {
self.detach = detach;
self
}

pub fn with_volume(mut self, volume: &str) -> Self {
self.volumes.push(volume.to_string());
self
}

pub fn build(self) -> Container {
Container {
image: self.image,
name: self.name,
env: self.env,
hostname: self.hostname,
network: self.network,
port: self.port,
run_parameters: self.run_parameters,
detach: self.detach,
volumes: self.volumes,
}
}
}
53 changes: 53 additions & 0 deletions test_harness/src/docker/container/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// SPDX-FileCopyrightText: 2024 Phoenix R&D GmbH <[email protected]>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

use std::process::{Child, Command};

pub mod builder;

pub(super) struct Container {
image: String,
name: String,
env: Vec<String>,
hostname: Option<String>,
network: Option<String>,
port: Option<String>,
run_parameters: Vec<String>,
detach: bool,
volumes: Vec<String>,
}

impl Container {
pub(super) fn builder(image: &str, name: &str) -> builder::ContainerBuilder {
builder::ContainerBuilder::new(image, name)
}

pub(super) fn run(&self) -> Child {
let mut command = Command::new("docker");
command.arg("run");
for env_variable in &self.env {
command.args(["--env", env_variable]);
}
for volume in &self.volumes {
command.args(["--volume", volume]);
}
if let Some(network_name) = &self.network {
command.args(["--network", network_name]);
}
if let Some(hostname) = &self.hostname {
command.args(["--hostname", hostname]);
}
command.args(["--name", &self.name]);
if let Some(port) = &self.port {
command.args(["-p", port.to_string().as_str()]);
}
command.args(["--rm"]);
if self.detach {
command.args(["-d"]);
}
command.args([&self.image]);
command.args(&self.run_parameters);
command.spawn().unwrap()
}
}
Loading
Loading