Added function to retrieve CN name from peer certificate

This commit is contained in:
Sviatoslav Boichuk
2024-05-22 13:00:27 +03:00
parent af7773a044
commit e6394a714d
6 changed files with 91 additions and 18 deletions

View File

@@ -35,6 +35,7 @@ flate2 = "1.0.28"
base64 = "0.22.0"
rustls-pemfile = "2.1.2"
rustls-pki-types = "1.7.0"
x509-parser = "0.16.0"
[build-dependencies]
tonic-build = "0.10"

View File

@@ -33,6 +33,8 @@ DEFAULT_REDIS_DB_PORT=6379
CONTAINTER_CERTS_VOLUME="/etc/cgw/certs"
DEFAULT_ALLOW_CERT_MISMATCH="no"
export CGW_LOG_LEVEL="${CGW_LOG_LEVEL:-$DEFAULT_LOG_LEVEL}"
export CGW_ID="${CGW_ID:-$DEFAULT_ID}"
export CGW_WSS_IP="${CGW_WSS_IP:-$DEFAULT_WSS_IP}"
@@ -55,22 +57,24 @@ export CGW_DB_PASSWORD="${CGW_DB_PASS:-$DEFAULT_DB_PASW}"
export CGW_REDIS_IP="${CGW_REDIS_DB_IP:-$DEFAULT_REDIS_DB_IP}"
export CGW_REDIS_PORT="${CGW_REDIS_DB_PORT:-$DEFAULT_REDIS_DB_PORT}"
export CGW_CERTS_PATH="${CGW_CERTS_PATH:-$DEFAULT_CERTS_PATH}"
export CGW_ALLOW_CERT_MISMATCH="${CGW_ALLOW_CERT_MISMATCH:-$DEFAULT_ALLOW_CERT_MISMATCH}"
echo "Starting CGW..."
echo "CGW LOG LEVEL : $CGW_LOG_LEVEL"
echo "CGW ID : $CGW_ID"
echo "CGW WSS THREAD NUM: $CGW_WSS_THREAD_NUM"
echo "CGW WSS IP/PORT : $CGW_WSS_IP:$CGW_WSS_PORT"
echo "CGW WSS CAS : $CGW_WSS_CAS"
echo "CGW WSS CERT : $CGW_WSS_CERT"
echo "CGW WSS KEY : $CGW_WSS_KEY"
echo "CGW GRPC : $CGW_GRPC_IP:$CGW_GRPC_PORT"
echo "CGW KAFKA IP/PORT : $CGW_KAFKA_IP:$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 IP/PORT : $CGW_DB_IP:$CGW_DB_PORT"
echo "CGW REDIS : $CGW_REDIS_IP:$CGW_REDIS_PORT"
echo "CGW CERTS PATH : $CGW_CERTS_PATH"
echo "CGW LOG LEVEL : $CGW_LOG_LEVEL"
echo "CGW ID : $CGW_ID"
echo "CGW WSS THREAD NUM : $CGW_WSS_THREAD_NUM"
echo "CGW WSS IP/PORT : $CGW_WSS_IP:$CGW_WSS_PORT"
echo "CGW WSS CAS : $CGW_WSS_CAS"
echo "CGW WSS CERT : $CGW_WSS_CERT"
echo "CGW WSS KEY : $CGW_WSS_KEY"
echo "CGW GRPC : $CGW_GRPC_IP:$CGW_GRPC_PORT"
echo "CGW KAFKA IP/PORT : $CGW_KAFKA_IP:$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 IP/PORT : $CGW_DB_IP:$CGW_DB_PORT"
echo "CGW REDIS : $CGW_REDIS_IP:$CGW_REDIS_PORT"
echo "CGW CERTS PATH : $CGW_CERTS_PATH"
echo "CGW ALLOW CERT MISMATCH: $CGW_ALLOW_CERT_MISMATCH"
docker run \
-v $CGW_CERTS_PATH:$CONTAINTER_CERTS_VOLUME \
@@ -95,4 +99,5 @@ docker run \
-e CGW_DB_PASSWORD \
-e CGW_REDIS_IP \
-e CGW_REDIS_PORT \
-e CGW_ALLOW_CERT_MISMATCH \
-d -t --network=host --name $2 $1 ucentral-cgw

View File

@@ -71,7 +71,7 @@ impl CGWConnectionProcessor {
conn_processor
}
pub async fn start(mut self, tls_stream: TlsStream<TcpStream>) {
pub async fn start(mut self, tls_stream: TlsStream<TcpStream>, client_cn: String, allow_mismatch: bool) {
let ws_stream = tokio_tungstenite::accept_async(tls_stream)
.await
.expect("error during the websocket handshake occurred");
@@ -126,6 +126,26 @@ impl CGWConnectionProcessor {
}
};
let device_serial = eui48::MacAddress::parse_str(&evt.serial).unwrap();
let device_cn = eui48::MacAddress::parse_str(client_cn.as_str()).unwrap();
if !allow_mismatch {
if device_serial != device_cn {
error!(
"The client MAC address {} and clinet certificate CN {} check failed!",
device_serial.to_hex_string(),
device_cn.to_hex_string()
);
return;
} else {
debug!(
"The client MAC address {} and clinet certificate CN {} chech passed!",
device_serial.to_hex_string(),
device_cn.to_hex_string()
);
}
}
debug!("Done Parse Connect Event");
let mut caps: CGWDeviceCapabilities = Default::default();

View File

@@ -1,6 +1,7 @@
use crate::cgw_device::{
cgw_detect_device_chages, CGWDevice, CGWDeviceCapabilities, CGWDeviceState, OldNew,
};
use crate::cgw_tls::cgw_tls_get_cn_from_stream;
use crate::cgw_ucentral_messages_queue_manager::{
CGWUCentralMessagesQueueItem, CGW_MESSAGES_QUEUE,
};
@@ -28,7 +29,6 @@ use tokio::{
},
time::{sleep, Duration},
};
use tokio_rustls::server::TlsStream;
use std::sync::atomic::{AtomicUsize, Ordering};
@@ -90,6 +90,9 @@ pub enum CGWConnectionNBAPIReqMsg {
}
pub struct CGWConnectionServer {
// Allow client certificate mismatch
allow_mismatch: bool,
local_cgw_id: i32,
// CGWConnectionServer write into this mailbox,
// and other correspondig Server task Reads RX counterpart
@@ -234,6 +237,7 @@ impl CGWConnectionServer {
let nb_api_c = CGWNBApiClient::new(app_args, &nb_api_tx);
let server = Arc::new(CGWConnectionServer {
allow_mismatch: app_args.allow_mismatch,
local_cgw_id: app_args.cgw_id,
connmap: CGWConnMap::new(),
wss_rx_tx_runtime: wss_runtime_handle,
@@ -1090,19 +1094,30 @@ impl CGWConnectionServer {
// Only ACK connection. We will either drop it or accept it once processor starts
// (we'll handle it via "mailbox" notify handle in process_internal_mbox)
let server_clone = self.clone();
let mut client_cn: String = String::new();
self.wss_rx_tx_runtime.spawn(async move {
// Accept the TLS connection.
let tls_stream = match tls_acceptor.accept(socket).await {
Ok(a) => a,
Ok(a) => match cgw_tls_get_cn_from_stream(&a).await {
Some(cn) => {
client_cn = cn;
a
}
None => {
warn!("Failed to get client certificate CN!");
return;
}
},
Err(e) => {
warn!("Err {e}");
return;
}
};
let allow_mismatch = server_clone.allow_mismatch;
let conn_processor = CGWConnectionProcessor::new(server_clone, conn_idx, addr);
conn_processor.start(tls_stream).await;
conn_processor.start(tls_stream, client_cn, allow_mismatch).await;
});
}
}

View File

@@ -3,6 +3,9 @@ use std::{
fs::File,
io::{BufReader, Error, ErrorKind},
};
use tokio::net::TcpStream;
use tokio_rustls::server::TlsStream;
use x509_parser::parse_x509_certificate;
pub async fn cgw_tls_read_certs(cert_file: &str) -> Result<Vec<CertificateDer<'static>>, Error> {
let file = match File::open(cert_file) {
@@ -28,3 +31,24 @@ pub async fn cgw_tls_read_private_key(
Err(err) => return Err(err),
};
}
pub async fn cgw_tls_get_cn_from_stream(stream: &TlsStream<TcpStream>) -> Option<String> {
if let Some(certs) = stream.get_ref().1.peer_certificates() {
let first_cert = certs.get(0).unwrap();
match parse_x509_certificate(first_cert.as_ref()) {
Ok(parsed_cert) => {
for rdn in parsed_cert.1.subject().iter_common_name() {
if let Ok(cn) = rdn.as_str() {
return Some(cn.to_owned());
}
}
}
Err(_) => {
return None;
}
}
}
None
}

View File

@@ -91,6 +91,7 @@ const CGW_DEFAULT_DB_USERNAME: &str = "cgw";
const CGW_DEFAULT_DB_PASSWORD: &str = "123";
const CGW_DEFAULT_REDIS_IP: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1);
const CGW_DEFAULT_REDIS_PORT: u16 = 5432;
const CGW_DEFAULT_ALLOW_CERT_MISMATCH: &str = "no";
const CGW_CERTIFICATES_PATH: &str = "/etc/cgw/certs";
@@ -146,6 +147,9 @@ pub struct AppArgs {
redis_db_ip: Ipv4Addr,
/// PORT to connect to DB (REDIS)
redis_db_port: u16,
/// Allow Missmatch
allow_mismatch: bool,
}
impl AppArgs {
@@ -230,6 +234,9 @@ impl AppArgs {
Err(_) => CGW_DEFAULT_REDIS_PORT,
};
let mismatch: String = env::var("CGW_ALLOW_CERT_MISMATCH").unwrap_or(CGW_DEFAULT_ALLOW_CERT_MISMATCH.to_string());
let allow_mismatch = mismatch == "yes";
AppArgs {
log_level,
cgw_id,
@@ -252,6 +259,7 @@ impl AppArgs {
db_password,
redis_db_ip,
redis_db_port,
allow_mismatch,
}
}
}