Skip to content

Commit

Permalink
Make TLS connection to Redis/PSQL optional
Browse files Browse the repository at this point in the history
  • Loading branch information
SviatoslavBoichuk committed Aug 12, 2024
1 parent c0237bc commit d9efb9a
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 48 deletions.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,20 @@ CGW_DB_HOST - IP or hostname of remote database server to connect to
CGW_DB_PORT - PORT of remote database server to connect to
CGW_DB_USER - PSQL DB username (credentials) to use upon connect to DB
CGW_DB_PASS - PSQL DB password (credentials) to use upon connect to DB
CGW_DB_TLS - Utilize TLS connection with DB server
CGW_REDIS_HOST - IP or hostname of remote redis-db server to connect to
CGW_REDIS_PORT - PORT of remote redis-db server to connect to
CGW_REDIS_USERNAME - REDIS username (credentials) to use upon connect to
CGW_REDIS_PASSWORD - REDIS password (credentials) to use upon connect to
CGW_REDIS_TLS - Utilize TLS connection with REDIS server
CGW_LOG_LEVEL - Log level to start CGW application with (debug, info)
CGW_METRICS_PORT - PORT of metrics to connect to
CGW_CERTS_PATH - Path to certificates located on host machine
CGW_ALLOW_CERT_MISMATCH - Allow client certificate CN and device MAC address mismatch (used for OWLS)
CGW_NB_INFRA_CERTS_DIR - Path to NB infrastructure (Redis, PostgreSQL)certificates located on host machine
CGW_NB_INFRA_CERTS_DIR - Path to NB infrastructure (Redis, PostgreSQL) certificates located on host machine
CGW_NB_INFRA_TLS - Utilize TLS connection with NB infrastructure (Redis, PostgreSQL)
If set enabled - the CGW_DB_TLS and CGW_REDIS_TLS values will be ignored and
the TLS connection will be used for Redis and PostgreSQL connection
```

Example of properly configured list of env variables to start CGW:
Expand All @@ -90,6 +95,7 @@ declare -x CGW_DB_HOST="localhost" # PSQL server is locate
declare -x CGW_DB_PORT="5432"
declare -x CGW_DB_USERNAME="cgw" # PSQL login credentials (username) default 'cgw' will be used
declare -x CGW_DB_PASS="123" # PSQL login credentials (password) default '123' will be used
declare -x CGW_DB_TLS="no"
declare -x CGW_GRPC_LISTENING_IP="127.0.0.1" # Local default subnet is 127.0.0.1/24
declare -x CGW_GRPC_LISTENING_PORT="50051"
declare -x CGW_GRPC_PUBLIC_HOST="localhost"
Expand All @@ -102,6 +108,7 @@ declare -x CGW_REDIS_HOST="localhost" # Redis server can be f
declare -x CGW_REDIS_PORT="6379"
declare -x CGW_REDIS_USERNAME="cgw" # REDIS login credentials (username) - optional
declare -x CGW_REDIS_PASSWORD="123" # REDIS login credentials (password) - optional
declare -x CGW_REDIS_TLS="no"
declare -x CGW_METRICS_PORT="8080"
declare -x CGW_WSS_IP="0.0.0.0" # Accept WSS connections at all interfaces / subnets
declare -x CGW_WSS_PORT="15002"
Expand All @@ -111,6 +118,7 @@ declare -x CGW_WSS_KEY="key.pem"
declare -x CGW_CERTS_PATH="/etc/ssl/certs" # Path to certificates located on host machine
declare -x CGW_ALLOW_CERT_MISMATCH="no" # Allow client certificate CN and device MAC address mismatch
declare -x CGW_NB_INFRA_CERTS_PATH="/etc/nb_infra_certs"
declare -x CGW_NB_INFRA_TLS="no"
```
# Certificates
The CGW uses two different sets of certificate configuration:
Expand Down
12 changes: 12 additions & 0 deletions run_cgw.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,17 @@ DEFAULT_DB_PORT=5432
DEFAULT_DB_NAME="cgw"
DEFAULT_DB_USER="cgw"
DEFAULT_DB_PASW="123"
DEFAULT_DB_TLS="no"

DEFAULT_REDIS_HOST="localhost"
DEFAULT_REDIS_PORT=6379
DEFAULT_REDIS_TLS="no"

DEFAULT_METRICS_PORT=8080

CONTAINTER_CERTS_VOLUME="/etc/cgw/certs"
CONTAINTER_NB_INFRA_CERTS_VOLUME="/etc/cgw/nb_infra/certs"
DEFAULT_NB_INFRA_TLS="no"

DEFAULT_ALLOW_CERT_MISMATCH="no"

Expand All @@ -61,12 +64,15 @@ export CGW_DB_PORT="${CGW_DB_PORT:-$DEFAULT_DB_PORT}"
export CGW_DB_NAME="${CGW_DB_NAME:-$DEFAULT_DB_NAME}"
export CGW_DB_USERNAME="${CGW_DB_USER:-$DEFAULT_DB_USER}"
export CGW_DB_PASSWORD="${CGW_DB_PASS:-$DEFAULT_DB_PASW}"
export CGW_DB_TLS="${CGW_DB_TLS:-$DEFAULT_DB_TLS}"
export CGW_REDIS_HOST="${CGW_REDIS_HOST:-$DEFAULT_REDIS_HOST}"
export CGW_REDIS_PORT="${CGW_REDIS_PORT:-$DEFAULT_REDIS_PORT}"
export CGW_REDIS_TLS="${CGW_REDIS_TLS:-$DEFAULT_REDIS_TLS}"
export CGW_METRICS_PORT="${CGW_METRICS_PORT:-$DEFAULT_METRICS_PORT}"
export CGW_CERTS_PATH="${CGW_CERTS_PATH:-$DEFAULT_CERTS_PATH}"
export CGW_ALLOW_CERT_MISMATCH="${CGW_ALLOW_CERT_MISMATCH:-$DEFAULT_ALLOW_CERT_MISMATCH}"
export CGW_NB_INFRA_CERTS_PATH="${CGW_NB_INFRA_CERTS_PATH:-$DEFAULT_CERTS_PATH}"
export CGW_NB_INFRA_TLS="${CGW_NB_INFRA_TLS:-$DEFAULT_NB_INFRA_TLS}"

if [ -z "${!CGW_REDIS_USERNAME}" ]; then
export CGW_REDIS_USERNAME="${CGW_REDIS_USERNAME}"
Expand All @@ -90,11 +96,14 @@ echo "CGW KAFKA HOST/PORT : $CGW_KAFKA_HOST:$CGW_KAFKA_PORT"
echo "CGW KAFKA TOPIC : $CGW_KAFKA_CONSUME_TOPIC:$CGW_KAFKA_PRODUCE_TOPIC"
echo "CGW DB NAME : $CGW_DB_NAME"
echo "CGW DB HOST/PORT : $CGW_DB_HOST:$CGW_DB_PORT"
echo "CGW DB TLS : $CGW_DB_TLS"
echo "CGW REDIS HOST/PORT : $CGW_REDIS_HOST:$CGW_REDIS_PORT"
echo "CGW REDIS TLS : $CGW_REDIS_TLS"
echo "CGW METRICS PORT : $CGW_METRICS_PORT"
echo "CGW CERTS PATH : $CGW_CERTS_PATH"
echo "CGW ALLOW CERT MISMATCH : $CGW_ALLOW_CERT_MISMATCH"
echo "CGW NB INFRA CERTS PATH : $CGW_NB_INFRA_CERTS_PATH"
echo "CGW NB INFRA TLS : $CGW_NB_INFRA_TLS"

docker run \
--cap-add=SYS_PTRACE --security-opt seccomp=unconfined \
Expand All @@ -121,11 +130,14 @@ docker run \
-e CGW_DB_PORT \
-e CGW_DB_USERNAME \
-e CGW_DB_PASSWORD \
-e CGW_DB_TLS \
-e CGW_REDIS_HOST \
-e CGW_REDIS_PORT \
-e CGW_REDIS_USERNAME \
-e CGW_REDIS_PASSWORD \
-e CGW_REDIS_TLS \
-e CGW_FEATURE_TOPOMAP_ENABLE \
-e CGW_METRICS_PORT \
-e CGW_ALLOW_CERT_MISMATCH \
-e CGW_NB_INFRA_TLS \
-d -t --network=host --name $2 $1 ucentral-cgw
33 changes: 31 additions & 2 deletions src/cgw_app_args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@ const CGW_DEFAULT_DB_PORT: u16 = 6379;
const CGW_DEFAULT_DB_NAME: &str = "cgw";
const CGW_DEFAULT_DB_USERNAME: &str = "cgw";
const CGW_DEFAULT_DB_PASSWORD: &str = "123";
const CGW_DEFAULT_DB_TLS: &str = "no";
const CGW_DEFAULT_REDIS_HOST: &str = "localhost";
const CGW_DEFAULT_REDIS_PORT: u16 = 6379;
const CGW_DEFAULT_REDIS_TLS: &str = "no";
const CGW_DEFAULT_ALLOW_CERT_MISMATCH: &str = "no";
const CGW_DEFAULT_METRICS_PORT: u16 = 8080;
const CGW_DEFAULT_TOPOMAP_STATE: bool = false;
const CGW_DEFAULT_NB_INFRA_TLS: &str = "no";

pub struct CGWWSSArgs {
/// Number of thread in a threadpool dedicated for handling secure websocket connections
Expand Down Expand Up @@ -252,6 +255,8 @@ pub struct CGWDBArgs {
pub db_username: String,
/// DB user password use with connection to in DB (PSQL)
pub db_password: String,
/// Utilize TLS connection with DB server
pub db_tls: bool,
}

impl CGWDBArgs {
Expand Down Expand Up @@ -289,12 +294,16 @@ impl CGWDBArgs {
let db_password: String =
env::var("CGW_DB_PASSWORD").unwrap_or(CGW_DEFAULT_DB_PASSWORD.to_string());

let db_tls_var: String = env::var("CGW_DB_TLS").unwrap_or(CGW_DEFAULT_DB_TLS.to_string());
let db_tls = db_tls_var == "yes";

Ok(CGWDBArgs {
db_host,
db_port,
db_name,
db_username,
db_password,
db_tls,
})
}
}
Expand All @@ -308,6 +317,8 @@ pub struct CGWRedisArgs {
pub redis_username: Option<String>,
/// REDIS password
pub redis_password: Option<String>,
/// Utilize TLS connection with DB server
pub redis_tls: bool,
}

impl CGWRedisArgs {
Expand Down Expand Up @@ -361,11 +372,16 @@ impl CGWRedisArgs {
Err(_) => None,
};

let redis_tls_var: String =
env::var("CGW_REDIS_TLS").unwrap_or(CGW_DEFAULT_REDIS_TLS.to_string());
let redis_tls = redis_tls_var == "yes";

Ok(CGWRedisArgs {
redis_host,
redis_port,
redis_username,
redis_password,
redis_tls,
})
}
}
Expand Down Expand Up @@ -404,6 +420,9 @@ pub struct AppArgs {
/// Topomap featue status (enabled/disabled)
pub feature_topomap_enabled: bool,

/// Utilize TLS connection with NB infrastructure (Redis, PostgreSQL)
pub nb_infra_tls: bool,

pub wss_args: CGWWSSArgs,

pub grpc_args: CGWGRPCArgs,
Expand Down Expand Up @@ -450,17 +469,27 @@ impl AppArgs {
Err(_) => CGW_DEFAULT_TOPOMAP_STATE,
};

let nb_infra_tls_var: String =
env::var("CGW_NB_INFRA_TLS").unwrap_or(CGW_DEFAULT_NB_INFRA_TLS.to_string());
let nb_infra_tls = nb_infra_tls_var == "yes";

let wss_args = CGWWSSArgs::parse()?;
let grpc_args = CGWGRPCArgs::parse()?;
let kafka_args = CGWKafkaArgs::parse()?;
let db_args = CGWDBArgs::parse()?;
let redis_args = CGWRedisArgs::parse()?;
let mut db_args = CGWDBArgs::parse()?;
let mut redis_args = CGWRedisArgs::parse()?;
let metrics_args = CGWMetricsArgs::parse()?;

if nb_infra_tls {
redis_args.redis_tls = nb_infra_tls;
db_args.db_tls = nb_infra_tls;
}

Ok(AppArgs {
log_level,
cgw_id,
feature_topomap_enabled,
nb_infra_tls,
wss_args,
grpc_args,
kafka_args,
Expand Down
96 changes: 64 additions & 32 deletions src/cgw_db_accessor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::{

use eui48::MacAddress;

use tokio_postgres::NoTls;
use tokio_postgres::{row::Row, Client};

#[derive(Clone)]
Expand Down Expand Up @@ -60,46 +61,77 @@ impl CGWDBAccessor {
user = db_args.db_username,
db = db_args.db_name,
pass = db_args.db_password,
sslmode = "require",
sslmode = match db_args.db_tls {
true => "require",
false => "disable",
}
);
debug!(
"Trying to connect to remote db ({}:{})...\nConn args {}",
db_args.db_host, db_args.db_port, conn_str
);

let tls = match cgw_tls_create_db_connect().await {
Ok(tls_connect) => tls_connect,
Err(e) => {
error!(
"Failed to build TLS connection with remote DB, reason: {}",
e.to_string()
);
return Err(Error::DbAccessor(
"Failed to build TLS connection with remote DB",
));
}
};
let client: Client;
if db_args.db_tls {
let tls = match cgw_tls_create_db_connect().await {
Ok(tls_connect) => tls_connect,
Err(e) => {
error!(
"Failed to build TLS connection with remote DB, reason: {}",
e.to_string()
);
return Err(Error::DbAccessor(
"Failed to build TLS connection with remote DB",
));
}
};

let (client, connection) = match tokio_postgres::connect(&conn_str, tls).await {
Ok((cl, conn)) => (cl, conn),
Err(e) => {
error!("Failed to establish connection with DB, reason: {:?}", e);
return Err(Error::DbAccessor("Failed to establish connection with DB"));
}
};
let (db_client, connection) = match tokio_postgres::connect(&conn_str, tls).await {
Ok((cl, conn)) => (cl, conn),
Err(e) => {
error!("Failed to establish connection with DB, reason: {:?}", e);
return Err(Error::DbAccessor("Failed to establish connection with DB"));
}
};

tokio::spawn(async move {
if let Err(e) = connection.await {
let err_msg = format!("Connection to DB broken: {}", e);
error!("{}", err_msg);
CGWMetrics::get_ref()
.change_component_health_status(
CGWMetricsHealthComponent::DBConnection,
CGWMetricsHealthComponentStatus::NotReady(err_msg),
)
.await;
}
});
tokio::spawn(async move {
if let Err(e) = connection.await {
let err_msg = format!("Connection to DB broken: {}", e);
error!("{}", err_msg);
CGWMetrics::get_ref()
.change_component_health_status(
CGWMetricsHealthComponent::DBConnection,
CGWMetricsHealthComponentStatus::NotReady(err_msg),
)
.await;
}
});

client = db_client;
} else {
let (db_client, connection) = match tokio_postgres::connect(&conn_str, NoTls).await {
Ok((cl, conn)) => (cl, conn),
Err(e) => {
error!("Failed to establish connection with DB, reason: {:?}", e);
return Err(Error::DbAccessor("Failed to establish connection with DB"));
}
};

tokio::spawn(async move {
if let Err(e) = connection.await {
let err_msg = format!("Connection to DB broken: {}", e);
error!("{}", err_msg);
CGWMetrics::get_ref()
.change_component_health_status(
CGWMetricsHealthComponent::DBConnection,
CGWMetricsHealthComponentStatus::NotReady(err_msg),
)
.await;
}
});

client = db_client;
}

tokio::spawn(async move {
CGWMetrics::get_ref()
Expand Down
40 changes: 27 additions & 13 deletions src/cgw_remote_discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,29 +151,43 @@ pub struct CGWRemoteDiscovery {

async fn cgw_create_redis_client(redis_args: &CGWRedisArgs) -> Result<Client> {
let redis_client_info = ConnectionInfo {
addr: redis::ConnectionAddr::TcpTls {
host: redis_args.redis_host.clone(),
port: redis_args.redis_port,
insecure: true,
tls_params: None,
addr: match redis_args.redis_tls {
true => redis::ConnectionAddr::TcpTls {
host: redis_args.redis_host.clone(),
port: redis_args.redis_port,
insecure: true,
tls_params: None,
},
false => {
redis::ConnectionAddr::Tcp(redis_args.redis_host.clone(), redis_args.redis_port)
}
},

redis: RedisConnectionInfo {
username: redis_args.redis_username.clone(),
password: redis_args.redis_password.clone(),
..Default::default()
},
};

let root_cert = cgw_read_root_certs_dir().await.ok();
match redis_args.redis_tls {
true => {
let root_cert = cgw_read_root_certs_dir().await.ok();

let tls_certs: TlsCertificates = TlsCertificates {
client_tls: None,
root_cert,
};
let tls_certs: TlsCertificates = TlsCertificates {
client_tls: None,
root_cert,
};

match redis::Client::build_with_tls(redis_client_info, tls_certs) {
Ok(client) => Ok(client),
Err(e) => Err(Error::Redis(format!("Failed to start Redis Client: {}", e))),
match redis::Client::build_with_tls(redis_client_info, tls_certs) {
Ok(client) => Ok(client),
Err(e) => Err(Error::Redis(format!("Failed to start Redis Client: {}", e))),
}
}
false => match redis::Client::open(redis_client_info) {
Ok(client) => Ok(client),
Err(e) => Err(Error::Redis(format!("Failed to start Redis Client: {}", e))),
},
}
}

Expand Down

0 comments on commit d9efb9a

Please sign in to comment.