Skip to content

Commit 485243d

Browse files
committed
Add env var override to all postgresql configuration settings
Also consolidate host and port settings into single socket address settings.
1 parent 55b7f54 commit 485243d

File tree

4 files changed

+182
-130
lines changed

4 files changed

+182
-130
lines changed

rust/impls/src/postgres_store.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,11 +147,10 @@ impl PostgresPlaintextBackend {
147147
impl PostgresTlsBackend {
148148
/// Constructs a [`PostgresTlsBackend`] using `postgres_endpoint` for PostgreSQL connection information.
149149
pub async fn new(
150-
postgres_endpoint: &str, default_db: &str, vss_db: &str,
151-
additional_certificate: Option<Certificate>,
150+
postgres_endpoint: &str, default_db: &str, vss_db: &str, certificate: Option<Certificate>,
152151
) -> Result<Self, Error> {
153152
let mut builder = TlsConnector::builder();
154-
if let Some(cert) = additional_certificate {
153+
if let Some(cert) = certificate {
155154
builder.add_root_certificate(cert);
156155
}
157156
let connector = builder.build().map_err(|e| {

rust/server/src/main.rs

Lines changed: 50 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
#![deny(rustdoc::private_intra_doc_links)]
1010
#![deny(missing_docs)]
1111

12-
use std::net::SocketAddr;
13-
1412
use tokio::net::TcpListener;
1513
use tokio::signal::unix::SignalKind;
1614

@@ -20,37 +18,24 @@ use hyper_util::rt::TokioIo;
2018
use crate::vss_service::VssService;
2119
use api::auth::{Authorizer, NoopAuthorizer};
2220
use api::kv_store::KvStore;
23-
use auth_impls::{DecodingKey, JWTAuthorizer};
24-
use impls::postgres_store::{Certificate, PostgresPlaintextBackend, PostgresTlsBackend};
21+
use auth_impls::JWTAuthorizer;
22+
use impls::postgres_store::{PostgresPlaintextBackend, PostgresTlsBackend};
2523
use std::sync::Arc;
2624

2725
mod util;
2826
mod vss_service;
2927

30-
use util::config::{Config, ServerConfig};
31-
3228
fn main() {
3329
let args: Vec<String> = std::env::args().collect();
3430
if args.len() != 2 {
3531
eprintln!("Usage: {} <config-file-path>", args[0]);
3632
std::process::exit(1);
3733
}
3834

39-
let Config { server_config: ServerConfig { host, port, rsa_pub_file_path }, postgresql_config } =
40-
match util::config::load_config(&args[1]) {
41-
Ok(cfg) => cfg,
42-
Err(e) => {
43-
eprintln!("Failed to load configuration: {}", e);
44-
std::process::exit(1);
45-
},
46-
};
47-
let addr: SocketAddr = match format!("{}:{}", host, port).parse() {
48-
Ok(addr) => addr,
49-
Err(e) => {
50-
eprintln!("Invalid host/port configuration: {}", e);
51-
std::process::exit(1);
52-
},
53-
};
35+
let config = util::config::load_configuration(&args[1]).unwrap_or_else(|e| {
36+
eprintln!("Failed to load configuration: {}", e);
37+
std::process::exit(-1);
38+
});
5439

5540
let runtime = match tokio::runtime::Builder::new_multi_thread().enable_all().build() {
5641
Ok(runtime) => Arc::new(runtime),
@@ -69,73 +54,58 @@ fn main() {
6954
},
7055
};
7156

72-
let authorizer: Arc<dyn Authorizer> = if let Some(file_path) = rsa_pub_file_path {
73-
let rsa_pub_file = match std::fs::read(file_path) {
74-
Ok(pem) => pem,
75-
Err(e) => {
76-
println!("Failed to read RSA public key file: {}", e);
77-
std::process::exit(-1);
78-
},
57+
let authorizer: Arc<dyn Authorizer> =
58+
if let Some(rsa_public_key) = config.jwt_rsa_public_key {
59+
let jwt_authorizer = JWTAuthorizer::new(rsa_public_key).await;
60+
println!("Configured JWT authorizer");
61+
Arc::new(jwt_authorizer)
62+
} else {
63+
let noop_authorizer = NoopAuthorizer {};
64+
println!("No authentication method configured");
65+
Arc::new(noop_authorizer)
7966
};
80-
let rsa_public_key = match DecodingKey::from_rsa_pem(&rsa_pub_file) {
81-
Ok(pem) => pem,
82-
Err(e) => {
83-
println!("Failed to parse RSA public key file: {}", e);
84-
std::process::exit(-1);
85-
},
86-
};
87-
Arc::new(JWTAuthorizer::new(rsa_public_key).await)
88-
} else {
89-
Arc::new(NoopAuthorizer {})
90-
};
9167

92-
let endpoint = postgresql_config.to_postgresql_endpoint();
93-
let default_db = postgresql_config.default_database;
94-
let vss_db = postgresql_config.vss_database;
95-
let store: Arc<dyn KvStore> = if let Some(tls_config) = postgresql_config.tls {
96-
let addl_certificate = tls_config.ca_file.map(|file| {
97-
let certificate = match std::fs::read(&file) {
98-
Ok(cert) => cert,
99-
Err(e) => {
100-
println!("Failed to read certificate file: {}", e);
101-
std::process::exit(-1);
102-
},
103-
};
104-
match Certificate::from_pem(&certificate) {
105-
Ok(cert) => cert,
106-
Err(e) => {
107-
println!("Failed to parse certificate file: {}", e);
108-
std::process::exit(-1);
109-
},
110-
}
68+
let store: Arc<dyn KvStore> = if let Some(certificate) = config.tls_config {
69+
let postgres_tls_backend = PostgresTlsBackend::new(
70+
&config.postgresql_prefix,
71+
&config.default_db,
72+
&config.vss_db,
73+
certificate,
74+
)
75+
.await
76+
.unwrap_or_else(|e| {
77+
println!("Failed to start postgres TLS backend: {}", e);
78+
std::process::exit(-1);
11179
});
112-
let postgres_tls_backend =
113-
match PostgresTlsBackend::new(&endpoint, &default_db, &vss_db, addl_certificate)
114-
.await
115-
{
116-
Ok(backend) => backend,
117-
Err(e) => {
118-
println!("Failed to start postgres tls backend: {}", e);
119-
std::process::exit(-1);
120-
},
121-
};
80+
println!(
81+
"Connected to PostgreSQL TLS backend with DSN: {}/{}",
82+
config.postgresql_prefix, config.vss_db
83+
);
12284
Arc::new(postgres_tls_backend)
12385
} else {
124-
let postgres_plaintext_backend =
125-
match PostgresPlaintextBackend::new(&endpoint, &default_db, &vss_db).await {
126-
Ok(backend) => backend,
127-
Err(e) => {
128-
println!("Failed to start postgres plaintext backend: {}", e);
129-
std::process::exit(-1);
130-
},
131-
};
86+
let postgres_plaintext_backend = PostgresPlaintextBackend::new(
87+
&config.postgresql_prefix,
88+
&config.default_db,
89+
&config.vss_db,
90+
)
91+
.await
92+
.unwrap_or_else(|e| {
93+
println!("Failed to start postgres plaintext backend: {}", e);
94+
std::process::exit(-1);
95+
});
96+
println!(
97+
"Connected to PostgreSQL plaintext backend with DSN: {}/{}",
98+
config.postgresql_prefix, config.vss_db
99+
);
132100
Arc::new(postgres_plaintext_backend)
133101
};
134-
println!("Connected to PostgreSQL backend with DSN: {}/{}", endpoint, vss_db);
135102

136-
let rest_svc_listener =
137-
TcpListener::bind(&addr).await.expect("Failed to bind listening port");
138-
println!("Listening for incoming connections on {}", addr);
103+
let rest_svc_listener = TcpListener::bind(&config.bind_address).await.unwrap_or_else(|e| {
104+
println!("Failed to bind listening port: {}", e);
105+
std::process::exit(-1);
106+
});
107+
println!("Listening for incoming connections on {}", config.bind_address);
108+
139109
loop {
140110
tokio::select! {
141111
res = rest_svc_listener.accept() => {

rust/server/src/util/config.rs

Lines changed: 121 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,138 @@
1+
use auth_impls::DecodingKey;
2+
use impls::postgres_store::Certificate;
13
use serde::Deserialize;
4+
use std::net::SocketAddr;
25

36
#[derive(Deserialize)]
4-
pub(crate) struct Config {
5-
pub(crate) server_config: ServerConfig,
6-
pub(crate) postgresql_config: PostgreSQLConfig,
7+
struct Config {
8+
server_config: ServerConfig,
9+
postgresql_config: Option<PostgreSQLConfig>,
710
}
811

912
#[derive(Deserialize)]
10-
pub(crate) struct ServerConfig {
11-
pub(crate) host: String,
12-
pub(crate) port: u16,
13-
pub(crate) rsa_pub_file_path: Option<String>,
13+
struct ServerConfig {
14+
bind_address: SocketAddr,
15+
rsa_pub_file_path: Option<String>,
1416
}
1517

18+
// All fields can be overriden by their corresponding environment variables
1619
#[derive(Deserialize)]
17-
pub(crate) struct PostgreSQLConfig {
18-
pub(crate) username: Option<String>, // Optional in TOML, can be overridden by env
19-
pub(crate) password: Option<String>, // Optional in TOML, can be overridden by env
20-
pub(crate) host: String,
21-
pub(crate) port: u16,
22-
pub(crate) default_database: String,
23-
pub(crate) vss_database: String,
24-
pub(crate) tls: Option<TlsConfig>,
20+
struct PostgreSQLConfig {
21+
// VSS_POSTGRESQL_USERNAME
22+
username: Option<String>,
23+
// VSS_POSTGRESQL_PASSWORD
24+
password: Option<String>,
25+
// VSS_POSTGRESQL_ADDRESS
26+
address: Option<SocketAddr>,
27+
// VSS_POSTGRESQL_DEFAULT_DATABASE
28+
default_database: Option<String>,
29+
// VSS_POSTGRESQL_VSS_DATABASE
30+
vss_database: Option<String>,
31+
// Set VSS_POSTGRESQL_TLS=1 for tls: Some(TlsConfig { crt_file_path: None })
32+
// Set VSS_POSTGRESQL_CRT_FILE_PATH=ca.crt for tls: Some(TlsConfig { crt_file_path: String::from("ca.crt") })
33+
tls: Option<TlsConfig>,
2534
}
2635

2736
#[derive(Deserialize)]
28-
pub(crate) struct TlsConfig {
29-
pub(crate) ca_file: Option<String>,
37+
struct TlsConfig {
38+
crt_file_path: Option<String>,
3039
}
3140

32-
impl PostgreSQLConfig {
33-
pub(crate) fn to_postgresql_endpoint(&self) -> String {
34-
let username_env = std::env::var("VSS_POSTGRESQL_USERNAME");
35-
let username = username_env.as_ref()
36-
.ok()
37-
.or_else(|| self.username.as_ref())
38-
.expect("PostgreSQL database username must be provided in config or env var VSS_POSTGRESQL_USERNAME must be set.");
39-
let password_env = std::env::var("VSS_POSTGRESQL_PASSWORD");
40-
let password = password_env.as_ref()
41-
.ok()
42-
.or_else(|| self.password.as_ref())
43-
.expect("PostgreSQL database password must be provided in config or env var VSS_POSTGRESQL_PASSWORD must be set.");
44-
45-
format!("postgresql://{}:{}@{}:{}", username, password, self.host, self.port)
46-
}
41+
pub(crate) struct Configuration {
42+
pub(crate) bind_address: SocketAddr,
43+
pub(crate) jwt_rsa_public_key: Option<DecodingKey>,
44+
pub(crate) postgresql_prefix: String,
45+
pub(crate) default_db: String,
46+
pub(crate) vss_db: String,
47+
// The Some(None) variant maps to a TLS connection with no additional certificates
48+
pub(crate) tls_config: Option<Option<Certificate>>,
4749
}
4850

49-
pub(crate) fn load_config(config_path: &str) -> Result<Config, Box<dyn std::error::Error>> {
50-
let config_str = std::fs::read_to_string(config_path)?;
51-
let config: Config = toml::from_str(&config_str)?;
52-
Ok(config)
51+
fn load_postgresql_prefix(config: Option<&PostgreSQLConfig>) -> Result<String, String> {
52+
let username_env = std::env::var("VSS_POSTGRESQL_USERNAME").ok();
53+
let username = username_env.as_ref()
54+
.or(config.and_then(|c| c.username.as_ref()))
55+
.ok_or("PostgreSQL database username must be provided in config or env var VSS_POSTGRESQL_USERNAME must be set.")?;
56+
57+
let password_env = std::env::var("VSS_POSTGRESQL_PASSWORD").ok();
58+
let password = password_env.as_ref()
59+
.or(config.and_then(|c| c.password.as_ref()))
60+
.ok_or("PostgreSQL database password must be provided in config or env var VSS_POSTGRESQL_PASSWORD must be set.")?;
61+
62+
let address_env: Option<SocketAddr> =
63+
if let Some(addr) = std::env::var("VSS_POSTGRESQL_ADDRESS").ok() {
64+
let socket_addr = addr
65+
.parse()
66+
.map_err(|e| format!("Unable to parse postgresql address env var: {}", e))?;
67+
Some(socket_addr)
68+
} else {
69+
None
70+
};
71+
let address = address_env.as_ref()
72+
.or(config.and_then(|c| c.address.as_ref()))
73+
.ok_or("PostgreSQL service address must be provided in config or env var VSS_POSTGRESQL_ADDRESS must be set.")?;
74+
75+
Ok(format!("postgresql://{}:{}@{}", username, password, address))
76+
}
77+
78+
pub(crate) fn load_configuration(config_file_path: &str) -> Result<Configuration, String> {
79+
let config_file = std::fs::read_to_string(config_file_path)
80+
.map_err(|e| format!("Failed to read configuration file: {}", e))?;
81+
let Config {
82+
server_config: ServerConfig { bind_address, rsa_pub_file_path },
83+
postgresql_config,
84+
} = toml::from_str(&config_file)
85+
.map_err(|e| format!("Failed to parse configuration file: {}", e))?;
86+
87+
let jwt_rsa_public_key = if let Some(file_path) = rsa_pub_file_path {
88+
let rsa_pub_file = std::fs::read(file_path)
89+
.map_err(|e| format!("Failed to read RSA public key file: {}", e))?;
90+
let rsa_public_key = DecodingKey::from_rsa_pem(&rsa_pub_file)
91+
.map_err(|e| format!("Failed to parse RSA public key file: {}", e))?;
92+
Some(rsa_public_key)
93+
} else {
94+
None
95+
};
96+
97+
let postgresql_prefix = load_postgresql_prefix(postgresql_config.as_ref())?;
98+
99+
let default_db_env = std::env::var("VSS_POSTGRESQL_DEFAULT_DATABASE").ok();
100+
let default_db = default_db_env
101+
.or(postgresql_config.as_ref().and_then(|c| c.default_database.clone()))
102+
.ok_or(String::from("PostgreSQL default database name must be provided in config or env var VSS_POSTGRESQL_DEFAULT_DATABASE must be set."))?;
103+
104+
let vss_db_env = std::env::var("VSS_POSTGRESQL_VSS_DATABASE").ok();
105+
let vss_db = vss_db_env
106+
.or(postgresql_config.as_ref().and_then(|c| c.vss_database.clone()))
107+
.ok_or(String::from("PostgreSQL vss database name must be provided in config or env var VSS_POSTGRESQL_VSS_DATABASE must be set."))?;
108+
109+
let crt_file_path_env = std::env::var("VSS_POSTGRESQL_CRT_FILE_PATH").ok();
110+
let crt_file_path = crt_file_path_env.or(postgresql_config
111+
.as_ref()
112+
.and_then(|c| c.tls.as_ref())
113+
.and_then(|tls| tls.crt_file_path.clone()));
114+
let certificate = if let Some(file_path) = crt_file_path {
115+
let crt_file = std::fs::read(&file_path)
116+
.map_err(|e| format!("Failed to read certificate file: {}", e))?;
117+
let certificate = Certificate::from_pem(&crt_file)
118+
.map_err(|e| format!("Failed to parse certificate file: {}", e))?;
119+
Some(certificate)
120+
} else {
121+
None
122+
};
123+
124+
let tls_config_env = std::env::var("VSS_POSTGRESQL_TLS").ok();
125+
let tls_config = (certificate.is_some()
126+
|| tls_config_env.is_some()
127+
|| postgresql_config.and_then(|c| c.tls).is_some())
128+
.then_some(certificate);
129+
130+
Ok(Configuration {
131+
bind_address,
132+
jwt_rsa_public_key,
133+
postgresql_prefix,
134+
default_db,
135+
vss_db,
136+
tls_config,
137+
})
53138
}

rust/server/vss-server-config.toml

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
[server_config]
2-
host = "127.0.0.1"
3-
port = 8080
4-
# rsa_pub_file_path = "rsa_public_key.pem" # Uncomment to verify JWT tokens in the HTTP Authorization header
2+
bind_address = "127.0.0.1:8080"
3+
# rsa_pub_file_path = "rsa_public_key.pem" # Uncomment to verify JWT tokens in the HTTP Authorization header
54

65
[postgresql_config]
7-
username = "postgres" # Optional in TOML, can be overridden by env var `VSS_POSTGRESQL_USERNAME`
8-
password = "postgres" # Optional in TOML, can be overridden by env var `VSS_POSTGRESQL_PASSWORD`
9-
host = "localhost"
10-
port = 5432
11-
default_database = "postgres"
12-
vss_database = "vss"
13-
# tls = { } # Uncomment to make TLS connections to the postgres database using your machine's PKI
14-
# tls = { ca_file = "ca.pem" } # Uncomment to make TLS connections to the postgres database with an additional root certificate
6+
username = "postgres" # Optional in TOML, can be overridden by env var `VSS_POSTGRESQL_USERNAME`
7+
password = "postgres" # Optional in TOML, can be overridden by env var `VSS_POSTGRESQL_PASSWORD`
8+
address = "127.0.0.1:5432" # Optional in TOML, can be overridden by env var `VSS_POSTGRESQL_ADDRESS`
9+
default_database = "postgres" # Optional in TOML, can be overridden by env var `VSS_POSTGRESQL_DEFAULT_DATABASE`
10+
vss_database = "vss" # Optional in TOML, can be overridden by env var `VSS_POSTGRESQL_VSS_DATABASE`
11+
# tls = { } # Uncomment, or set env var `VSS_POSTGRESQL_TLS=1` to make TLS connections to the postgres database using your machine's PKI
12+
# tls = { crt_file_path = "ca.crt" } # Uncomment, or set env var `VSS_POSTGRESQL_CRT_FILE_PATH=ca.crt` to make TLS connections to the postgres database with an additional root certificate

0 commit comments

Comments
 (0)