Skip to content

Commit 5a7b6b1

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 41a5a8a commit 5a7b6b1

File tree

3 files changed

+174
-109
lines changed

3 files changed

+174
-109
lines changed

rust/server/src/main.rs

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

12-
use std::net::SocketAddr;
1312
use std::sync::Arc;
1413

1514
use tokio::net::TcpListener;
@@ -22,7 +21,6 @@ use api::auth::{Authorizer, NoopAuthorizer};
2221
use api::kv_store::KvStore;
2322
use auth_impls::JWTAuthorizer;
2423
use impls::postgres_store::{PostgresPlaintextBackend, PostgresTlsBackend};
25-
use util::config::{Config, ServerConfig};
2624
use vss_service::VssService;
2725

2826
mod util;
@@ -35,21 +33,10 @@ fn main() {
3533
std::process::exit(1);
3634
}
3735

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

5441
let runtime = match tokio::runtime::Builder::new_multi_thread().enable_all().build() {
5542
Ok(runtime) => Arc::new(runtime),
@@ -68,67 +55,63 @@ fn main() {
6855
},
6956
};
7057

71-
let rsa_pem_env = match std::env::var("VSS_JWT_RSA_PEM") {
72-
Ok(env) => Some(env),
73-
Err(std::env::VarError::NotPresent) => None,
74-
Err(e) => {
75-
println!("Failed to load the VSS_JWT_RSA_PEM env var: {}", e);
76-
std::process::exit(-1);
77-
},
78-
};
79-
let rsa_pem = rsa_pem_env.or(jwt_auth_config.and_then(|config| config.rsa_pem));
80-
let authorizer: Arc<dyn Authorizer> = if let Some(pem) = rsa_pem {
81-
let authorizer = match JWTAuthorizer::new(pem.as_str()).await {
58+
let authorizer: Arc<dyn Authorizer> = if let Some(rsa_pem) = config.rsa_pem {
59+
let jwt_authorizer = match JWTAuthorizer::new(&rsa_pem).await {
8260
Ok(auth) => auth,
8361
Err(e) => {
8462
println!("Failed to configure JWT authorizer: {}", e);
8563
std::process::exit(-1);
8664
},
8765
};
8866
println!("Configured JWT authorizer with RSA public key");
89-
Arc::new(authorizer)
67+
Arc::new(jwt_authorizer)
9068
} else {
91-
println!("No JWT authentication method configured");
92-
Arc::new(NoopAuthorizer {})
69+
let noop_authorizer = NoopAuthorizer {};
70+
println!("No authentication method configured");
71+
Arc::new(noop_authorizer)
9372
};
9473

95-
let postgresql_config =
96-
postgresql_config.expect("PostgreSQLConfig must be defined in config file.");
97-
let endpoint = postgresql_config.to_postgresql_endpoint();
98-
let default_db = postgresql_config.default_database;
99-
let vss_db = postgresql_config.vss_database;
100-
let store: Arc<dyn KvStore> = if let Some(tls_config) = postgresql_config.tls {
101-
let postgres_tls_backend = match PostgresTlsBackend::new(
102-
&endpoint,
103-
&default_db,
104-
&vss_db,
105-
tls_config.crt_pem.as_deref(),
74+
let store: Arc<dyn KvStore> = if let Some(crt_pem) = config.tls_config {
75+
let postgres_tls_backend = PostgresTlsBackend::new(
76+
&config.postgresql_prefix,
77+
&config.default_db,
78+
&config.vss_db,
79+
crt_pem.as_deref(),
10680
)
10781
.await
108-
{
109-
Ok(backend) => backend,
110-
Err(e) => {
111-
println!("Failed to start postgres tls backend: {}", e);
112-
std::process::exit(-1);
113-
},
114-
};
82+
.unwrap_or_else(|e| {
83+
println!("Failed to start postgres TLS backend: {}", e);
84+
std::process::exit(-1);
85+
});
86+
println!(
87+
"Connected to PostgreSQL TLS backend with DSN: {}/{}",
88+
config.postgresql_prefix, config.vss_db
89+
);
11590
Arc::new(postgres_tls_backend)
11691
} else {
117-
let postgres_plaintext_backend =
118-
match PostgresPlaintextBackend::new(&endpoint, &default_db, &vss_db).await {
119-
Ok(backend) => backend,
120-
Err(e) => {
121-
println!("Failed to start postgres plaintext backend: {}", e);
122-
std::process::exit(-1);
123-
},
124-
};
92+
let postgres_plaintext_backend = PostgresPlaintextBackend::new(
93+
&config.postgresql_prefix,
94+
&config.default_db,
95+
&config.vss_db,
96+
)
97+
.await
98+
.unwrap_or_else(|e| {
99+
println!("Failed to start postgres plaintext backend: {}", e);
100+
std::process::exit(-1);
101+
});
102+
println!(
103+
"Connected to PostgreSQL plaintext backend with DSN: {}/{}",
104+
config.postgresql_prefix, config.vss_db
105+
);
125106
Arc::new(postgres_plaintext_backend)
126107
};
127-
println!("Connected to PostgreSQL backend with DSN: {}/{}", endpoint, vss_db);
128108

129-
let rest_svc_listener =
130-
TcpListener::bind(&addr).await.expect("Failed to bind listening port");
131-
println!("Listening for incoming connections on {}", addr);
109+
let rest_svc_listener = TcpListener::bind(&config.bind_address).await.unwrap_or_else(|e| {
110+
println!("Failed to bind listening port: {}", e);
111+
std::process::exit(-1);
112+
});
113+
println!("Listening for incoming connections on {}", config.bind_address);
114+
132115
loop {
133116
tokio::select! {
134117
res = rest_svc_listener.accept() => {

rust/server/src/util/config.rs

Lines changed: 122 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,142 @@
11
use serde::Deserialize;
2+
use std::net::SocketAddr;
23

34
#[derive(Deserialize)]
4-
pub(crate) struct Config {
5-
pub(crate) server_config: ServerConfig,
6-
pub(crate) jwt_auth_config: Option<JwtAuthConfig>,
7-
pub(crate) postgresql_config: Option<PostgreSQLConfig>,
5+
struct Config {
6+
server_config: ServerConfig,
7+
jwt_auth_config: Option<JwtAuthConfig>,
8+
postgresql_config: Option<PostgreSQLConfig>,
89
}
910

1011
#[derive(Deserialize)]
11-
pub(crate) struct ServerConfig {
12-
pub(crate) host: String,
13-
pub(crate) port: u16,
12+
struct ServerConfig {
13+
bind_address: SocketAddr,
1414
}
1515

1616
#[derive(Deserialize)]
17-
pub(crate) struct JwtAuthConfig {
18-
pub(crate) rsa_pem: Option<String>,
17+
struct JwtAuthConfig {
18+
rsa_pem: Option<String>,
1919
}
2020

21+
const JWT_RSA_PEM_VAR: &str = "VSS_JWT_RSA_PEM";
22+
const PSQL_USER_VAR: &str = "VSS_PSQL_USERNAME";
23+
const PSQL_PASS_VAR: &str = "VSS_PSQL_PASSWORD";
24+
const PSQL_ADDR_VAR: &str = "VSS_PSQL_ADDRESS";
25+
const PSQL_DB_VAR: &str = "VSS_PSQL_DEFAULT_DB";
26+
const PSQL_VSS_DB_VAR: &str = "VSS_PSQL_VSS_DB";
27+
const PSQL_TLS_VAR: &str = "VSS_PSQL_TLS";
28+
const PSQL_CERT_PEM_VAR: &str = "VSS_PSQL_CRT_PEM";
29+
2130
#[derive(Deserialize)]
22-
pub(crate) struct PostgreSQLConfig {
23-
pub(crate) username: Option<String>, // Optional in TOML, can be overridden by env
24-
pub(crate) password: Option<String>, // Optional in TOML, can be overridden by env
25-
pub(crate) host: String,
26-
pub(crate) port: u16,
27-
pub(crate) default_database: String,
28-
pub(crate) vss_database: String,
29-
pub(crate) tls: Option<TlsConfig>,
31+
struct PostgreSQLConfig {
32+
username: Option<String>,
33+
password: Option<String>,
34+
address: Option<SocketAddr>,
35+
default_database: Option<String>,
36+
vss_database: Option<String>,
37+
tls: Option<TlsConfig>,
3038
}
3139

3240
#[derive(Deserialize)]
33-
pub(crate) struct TlsConfig {
34-
pub(crate) crt_pem: Option<String>,
41+
struct TlsConfig {
42+
crt_pem: Option<String>,
3543
}
3644

37-
impl PostgreSQLConfig {
38-
pub(crate) fn to_postgresql_endpoint(&self) -> String {
39-
let username_env = std::env::var("VSS_POSTGRESQL_USERNAME");
40-
let username = username_env.as_ref()
41-
.ok()
42-
.or_else(|| self.username.as_ref())
43-
.expect("PostgreSQL database username must be provided in config or env var VSS_POSTGRESQL_USERNAME must be set.");
44-
let password_env = std::env::var("VSS_POSTGRESQL_PASSWORD");
45-
let password = password_env.as_ref()
46-
.ok()
47-
.or_else(|| self.password.as_ref())
48-
.expect("PostgreSQL database password must be provided in config or env var VSS_POSTGRESQL_PASSWORD must be set.");
49-
50-
format!("postgresql://{}:{}@{}:{}", username, password, self.host, self.port)
51-
}
45+
pub(crate) struct Configuration {
46+
pub(crate) bind_address: SocketAddr,
47+
pub(crate) rsa_pem: Option<String>,
48+
pub(crate) postgresql_prefix: String,
49+
pub(crate) default_db: String,
50+
pub(crate) vss_db: String,
51+
pub(crate) tls_config: Option<Option<String>>,
5252
}
5353

54-
pub(crate) fn load_config(config_path: &str) -> Result<Config, Box<dyn std::error::Error>> {
55-
let config_str = std::fs::read_to_string(config_path)?;
56-
let config: Config = toml::from_str(&config_str)?;
57-
Ok(config)
54+
pub(crate) fn load_configuration(config_file_path: &str) -> Result<Configuration, String> {
55+
let config_file = std::fs::read_to_string(config_file_path)
56+
.map_err(|e| format!("Failed to read configuration file: {}", e))?;
57+
let Config { server_config: ServerConfig { bind_address }, jwt_auth_config, postgresql_config } =
58+
toml::from_str(&config_file)
59+
.map_err(|e| format!("Failed to parse configuration file: {}", e))?;
60+
61+
macro_rules! read_env {
62+
($env_var:expr) => {
63+
match std::env::var($env_var) {
64+
Ok(env) => Some(env),
65+
Err(std::env::VarError::NotPresent) => None,
66+
Err(e) => {
67+
return Err(format!(
68+
"Failed to load the {} environment variable: {}",
69+
$env_var, e
70+
))
71+
},
72+
}
73+
};
74+
}
75+
76+
macro_rules! read_config {
77+
($env:expr, $config: expr, $item: expr, $var_name: expr) => {
78+
$env.or($config).ok_or(format!(
79+
"{} must be provided in the configuration file or the environment variable {} must be set.",
80+
$item, $var_name
81+
))?
82+
};
83+
}
84+
85+
let rsa_pem_env = read_env!(JWT_RSA_PEM_VAR);
86+
let rsa_pem = rsa_pem_env.or(jwt_auth_config.and_then(|config| config.rsa_pem));
87+
88+
let username_env = read_env!(PSQL_USER_VAR);
89+
let password_env = read_env!(PSQL_PASS_VAR);
90+
let address_env: Option<SocketAddr> = read_env!(PSQL_ADDR_VAR)
91+
.map(|address| {
92+
address.parse().map_err(|e| {
93+
format!("Unable to parse the postgresql address environment variable: {}", e)
94+
})
95+
})
96+
.transpose()?;
97+
let default_db_env = read_env!(PSQL_DB_VAR);
98+
let vss_db_env = read_env!(PSQL_VSS_DB_VAR);
99+
let tls_config_env = read_env!(PSQL_TLS_VAR);
100+
let crt_pem_env = read_env!(PSQL_CERT_PEM_VAR);
101+
102+
let (
103+
username_config,
104+
password_config,
105+
address_config,
106+
default_db_config,
107+
vss_db_config,
108+
tls_config,
109+
) = match postgresql_config {
110+
Some(c) => (
111+
c.username,
112+
c.password,
113+
c.address,
114+
c.default_database,
115+
c.vss_database,
116+
c.tls.map(|tls| tls.crt_pem),
117+
),
118+
None => (None, None, None, None, None, None),
119+
};
120+
121+
let username =
122+
read_config!(username_env, username_config, "PostgreSQL database username", PSQL_USER_VAR);
123+
let password =
124+
read_config!(password_env, password_config, "PostgreSQL database password", PSQL_PASS_VAR);
125+
let address =
126+
read_config!(address_env, address_config, "PostgreSQL service address", PSQL_ADDR_VAR);
127+
let default_db = read_config!(
128+
default_db_env,
129+
default_db_config,
130+
"PostgreSQL default database name",
131+
PSQL_DB_VAR
132+
);
133+
let vss_db =
134+
read_config!(vss_db_env, vss_db_config, "PostgreSQL vss database name", PSQL_VSS_DB_VAR);
135+
136+
let tls_config =
137+
crt_pem_env.map(|pem| Some(pem)).or(tls_config_env.map(|_| None)).or(tls_config);
138+
139+
let postgresql_prefix = format!("postgresql://{}:{}@{}", username, password, address);
140+
141+
Ok(Configuration { bind_address, rsa_pem, postgresql_prefix, default_db, vss_db, tls_config })
58142
}

rust/server/vss-server-config.toml

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
[server_config]
2-
host = "127.0.0.1"
3-
port = 8080
2+
bind_address = "127.0.0.1:8080"
43

54
# Uncomment the table below to verify JWT tokens in the HTTP Authorization header against the given RSA public key,
65
# can be overridden by env var `VSS_JWT_RSA_PEM`
@@ -11,16 +10,15 @@ port = 8080
1110
# """
1211

1312
[postgresql_config]
14-
username = "postgres" # Optional in TOML, can be overridden by env var `VSS_POSTGRESQL_USERNAME`
15-
password = "postgres" # Optional in TOML, can be overridden by env var `VSS_POSTGRESQL_PASSWORD`
16-
host = "localhost"
17-
port = 5432
18-
default_database = "postgres"
19-
vss_database = "vss"
13+
username = "postgres" # Optional in TOML, can be overridden by env var `VSS_PSQL_USERNAME`
14+
password = "postgres" # Optional in TOML, can be overridden by env var `VSS_PSQL_PASSWORD`
15+
address = "127.0.0.1:5432" # Optional in TOML, can be overridden by env var `VSS_PSQL_ADDRESS`
16+
default_database = "postgres" # Optional in TOML, can be overridden by env var `VSS_PSQL_DEFAULT_DB`
17+
vss_database = "vss" # Optional in TOML, can be overridden by env var `VSS_PSQL_VSS_DB`
2018

21-
# [postgresql_config.tls] # Uncomment to make TLS connections to the postgres database
19+
# [postgresql_config.tls] # Uncomment, or set env var `VSS_PSQL_TLS` to make TLS connections to the postgres database
2220
#
23-
# Uncomment the lines below to add a root certificate to your trusted root certificates
21+
# Uncomment the lines below, or set `VSS_PSQL_CRT_PEM`, to add a root certificate to your trusted root certificates
2422
#
2523
# crt_pem = """
2624
# -----BEGIN CERTIFICATE-----

0 commit comments

Comments
 (0)