diff --git a/Cargo.lock b/Cargo.lock index 58a8223..051f193 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -273,6 +273,22 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bcder" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c627747a6774aab38beb35990d88309481378558875a41da1a4b2e373c906ef0" +dependencies = [ + "bytes", + "smallvec", +] + [[package]] name = "bindgen" version = "0.69.4" @@ -401,12 +417,42 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "convert_case" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -447,6 +493,16 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "der-parser" version = "9.0.0" @@ -1398,6 +1454,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + [[package]] name = "parking_lot" version = "0.12.3" @@ -1427,6 +1489,16 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pem" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +dependencies = [ + "base64 0.22.1", + "serde", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -1739,20 +1811,29 @@ dependencies = [ ] [[package]] -name = "redis-async" -version = "0.17.2" +name = "redis" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e44d2172ccb44736798c4cdc8690fb0a3776a74daa502bfe124708ffc144909c" +checksum = "e0d7a6955c7511f60f3ba9e86c6d02b3c3f144f8c24b288d1f4e18074ab8bbec" dependencies = [ + "async-trait", "bytes", - "futures-channel", - "futures-sink", + "combine", "futures-util", - "log", - "pin-project", + "itoa", + "percent-encoding", + "pin-project-lite", + "rustls 0.22.4", + "rustls-native-certs", + "rustls-pemfile", + "rustls-pki-types", + "ryu", + "sha1_smol", "socket2", "tokio", + "tokio-rustls 0.25.0", "tokio-util", + "url", ] [[package]] @@ -1875,6 +1956,20 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +dependencies = [ + "log", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + [[package]] name = "rustls" version = "0.23.10" @@ -1890,6 +1985,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a88d6d420651b496bdd98684116959239430022a115c1240e6c3993be0b15fba" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pemfile" version = "2.1.2" @@ -1930,6 +2038,15 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -1942,6 +2059,29 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +dependencies = [ + "bitflags 2.5.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "1.0.23" @@ -2002,6 +2142,12 @@ dependencies = [ "digest", ] +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + [[package]] name = "sha2" version = "0.10.8" @@ -2028,6 +2174,15 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core", +] + [[package]] name = "siphasher" version = "0.3.11" @@ -2065,6 +2220,16 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -2290,13 +2455,38 @@ dependencies = [ "whoami", ] +[[package]] +name = "tokio-postgres-rustls" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04fb792ccd6bbcd4bba408eb8a292f70fc4a3589e5d793626f45190e6454b6ab" +dependencies = [ + "ring", + "rustls 0.23.10", + "tokio", + "tokio-postgres", + "tokio-rustls 0.26.0", + "x509-certificate", +] + +[[package]] +name = "tokio-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls 0.22.4", + "rustls-pki-types", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls", + "rustls 0.23.10", "rustls-pki-types", "tokio", ] @@ -2541,7 +2731,7 @@ dependencies = [ "prost", "prost-build", "rdkafka", - "redis-async", + "redis", "rlimit", "rustls-pemfile", "rustls-pki-types", @@ -2550,7 +2740,8 @@ dependencies = [ "tokio", "tokio-pg-mapper", "tokio-postgres", - "tokio-rustls", + "tokio-postgres-rustls", + "tokio-rustls 0.26.0", "tokio-stream", "tokio-tungstenite 0.23.0", "tonic", @@ -2965,6 +3156,25 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +[[package]] +name = "x509-certificate" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66534846dec7a11d7c50a74b7cdb208b9a581cad890b7866430d438455847c85" +dependencies = [ + "bcder", + "bytes", + "chrono", + "der", + "hex", + "pem", + "ring", + "signature", + "spki", + "thiserror", + "zeroize", +] + [[package]] name = "x509-parser" version = "0.16.0" diff --git a/Cargo.toml b/Cargo.toml index 7a20d60..a2f67e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,38 +5,42 @@ edition = "2021" [dependencies] serde = { version = "1.0.144", features = ["derive"] } -serde_json = "1.0.85" -env_logger = "0.11.3" -log = "0.4.20" +serde_json = { version = "1.0.85" } +env_logger = { version = "0.11.3" } +log = { version = "0.4.20" } tokio = { version = "1.34.0", features = ["full"] } tokio-stream = { version = "0.1.15", features = ["full"] } tokio-tungstenite = { version = "0.23.0" } -tokio-rustls = "0.26.0" -tokio-postgres = { version = "0.7.10", features = ["with-eui48-1"]} -tokio-pg-mapper = "0.2.0" -tungstenite = { version = "0.23.0"} +tokio-rustls = { version = "0.26.0" } +tokio-postgres = { version = "0.7.10", features = ["with-eui48-1"] } +tokio-postgres-rustls = { version = "0.12.0" } +tokio-pg-mapper = { version = "0.2.0" } +tungstenite = { version = "0.23.0" } futures-util = { version = "0.3.0", default-features = false } -futures-channel = "0.3.0" +futures-channel = { version = "0.3.0" } futures-executor = { version = "0.3.0", optional = true } -futures = "0.3.0" -rlimit = "0.10.1" -tonic = "0.11.0" -prost = "0.12" -rdkafka = "0.36.2" -eui48 = { version = "1.1.0", features = ["serde"]} +futures = { version = "0.3.0" } +rlimit = { version = "0.10.1" } +tonic = { version = "0.11.0" } +prost = { version = "0.12" } +rdkafka = { version = "0.36.2" } +eui48 = { version = "1.1.0", features = ["serde"] } uuid = { version = "1.6.1", features = ["serde"] } -redis-async = "0.17.2" -warp = "0.3.7" +redis = { version = "0.25.3", features = [ + "tokio-rustls-comp", + "tls-rustls-insecure", +] } +warp = { version = "0.3.7" } prometheus = { version = "0.13.4", features = ["process"] } -lazy_static = "1.4.0" +lazy_static = { version = "1.4.0" } petgraph = { version = "0.6.4", features = ["stable_graph"] } -flate2 = "1.0.28" -base64 = "0.22.0" -rustls-pemfile = "2.1.2" -rustls-pki-types = "1.7.0" -x509-parser = "0.16.0" -chrono = "0.4.38" -derive_more = "0.99.17" +flate2 = { version = "1.0.28" } +base64 = { version = "0.22.0" } +rustls-pemfile = { version = "2.1.2" } +rustls-pki-types = { version = "1.7.0" } +x509-parser = { version = "0.16.0" } +chrono = { version = "0.4.38" } +derive_more = { version = "0.99.17" } [build-dependencies] tonic-build = "0.11.0" diff --git a/README.md b/README.md index 4df275c..4385417 100644 --- a/README.md +++ b/README.md @@ -74,40 +74,50 @@ CGW_DB_USER - PSQL DB username (credentials) to use upon connect to CGW_DB_PASS - PSQL DB password (credentials) to use upon connect to DB 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_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 ``` Example of properly configured list of env variables to start CGW: ```console $ export | grep CGW -declare -x CGW_DB_HOST="localhost" # PSQL server is located at the local host +declare -x CGW_DB_HOST="localhost" # PSQL server is located at the local host 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_GRPC_LISTENING_IP="127.0.0.1" # Local default subnet is 127.0.0.1/24 +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_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" declare -x CGW_GRPC_PUBLIC_PORT="50051" declare -x CGW_ID="0" -declare -x CGW_KAFKA_HOST="localhost" # Kafka is located at the local host +declare -x CGW_KAFKA_HOST="localhost" # Kafka is located at the local host declare -x CGW_KAFKA_PORT="9092" declare -x CGW_LOG_LEVEL="debug" -declare -x CGW_REDIS_HOST="localhost" # Redis server can be found at the local host +declare -x CGW_REDIS_HOST="localhost" # Redis server can be found at the local host 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_METRICS_PORT="8080" -declare -x CGW_WSS_IP="0.0.0.0" # Accept WSS connections at all interfaces / subnets +declare -x CGW_WSS_IP="0.0.0.0" # Accept WSS connections at all interfaces / subnets declare -x CGW_WSS_PORT="15002" declare -x CGW_WSS_CAS="cas.pem" declare -x CGW_WSS_CERT="cert.pem" 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_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" ``` # Certificates -The CGW uses a number of certificates to provide security. +The CGW uses two different sets of certificate configuration: +1. AP/Switch connectivity (southbound) +2. Infrastructure connectivity (northbound) + +The AP/Switch connectivity uses a number of certificates to provide security (mTLS). There are 2 types of certificates required for a normal deployment: 1. Server certificates 2. Client certificates @@ -119,3 +129,7 @@ There are several environment variable to configure certificates path and names 2. CGW_WSS_KEY - CGW WSS Private Key 3. CGW_WSS_CAS - Chain certificates to validate client (root/issuer) 4. CGW_CERTS_PATH - path to certificates located on host machine + +The infrastructure connectivity use root certs store - the directory with trusted certificates +The environemt variable to configure certificates path: +1. CGW_NB_INFRA_CERTS_PATH - path to certificates located on host machine diff --git a/run_cgw.sh b/run_cgw.sh index 15f3d75..58adff8 100755 --- a/run_cgw.sh +++ b/run_cgw.sh @@ -36,6 +36,7 @@ DEFAULT_REDIS_PORT=6379 DEFAULT_METRICS_PORT=8080 CONTAINTER_CERTS_VOLUME="/etc/cgw/certs" +CONTAINTER_NB_INFRA_CERTS_VOLUME="/etc/cgw/nb_infra/certs" DEFAULT_ALLOW_CERT_MISMATCH="no" @@ -65,6 +66,15 @@ export CGW_REDIS_PORT="${CGW_REDIS_PORT:-$DEFAULT_REDIS_PORT}" 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}" + +if [ -z "${!CGW_REDIS_USERNAME}" ]; then + export CGW_REDIS_USERNAME="${CGW_REDIS_USERNAME}" +fi + +if [ -z "${!CGW_REDIS_PASSWORD}" ]; then + export CGW_REDIS_PASSWORD="${CGW_REDIS_PASSWORD}" +fi echo "Starting CGW..." echo "CGW LOG LEVEL : $CGW_LOG_LEVEL" @@ -84,34 +94,38 @@ echo "CGW REDIS HOST/PORT : $CGW_REDIS_HOST:$CGW_REDIS_PORT" 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" docker run \ - --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \ - -v $CGW_CERTS_PATH:$CONTAINTER_CERTS_VOLUME \ - -e CGW_LOG_LEVEL \ - -e CGW_ID \ - -e CGW_WSS_IP \ - -e CGW_WSS_PORT \ - -e DEFAULT_WSS_THREAD_NUM \ - -e CGW_WSS_CAS \ - -e CGW_WSS_CERT \ - -e CGW_WSS_KEY \ - -e CGW_GRPC_LISTENING_IP \ - -e CGW_GRPC_LISTENING_PORT \ - -e CGW_GRPC_PUBLIC_HOST \ - -e CGW_GRPC_PUBLIC_PORT \ - -e CGW_KAFKA_HOST \ - -e CGW_KAFKA_PORT \ - -e CGW_KAFKA_CONSUME_TOPIC \ - -e CGW_KAFKA_PRODUCE_TOPIC \ - -e CGW_DB_NAME \ - -e CGW_DB_HOST \ - -e CGW_DB_PORT \ - -e CGW_DB_USERNAME \ - -e CGW_DB_PASSWORD \ - -e CGW_REDIS_HOST \ - -e CGW_REDIS_PORT \ + --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \ + -v $CGW_CERTS_PATH:$CONTAINTER_CERTS_VOLUME \ + -v $CGW_NB_INFRA_CERTS_PATH:$CONTAINTER_NB_INFRA_CERTS_VOLUME \ + -e CGW_LOG_LEVEL \ + -e CGW_ID \ + -e CGW_WSS_IP \ + -e CGW_WSS_PORT \ + -e DEFAULT_WSS_THREAD_NUM \ + -e CGW_WSS_CAS \ + -e CGW_WSS_CERT \ + -e CGW_WSS_KEY \ + -e CGW_GRPC_LISTENING_IP \ + -e CGW_GRPC_LISTENING_PORT \ + -e CGW_GRPC_PUBLIC_HOST \ + -e CGW_GRPC_PUBLIC_PORT \ + -e CGW_KAFKA_HOST \ + -e CGW_KAFKA_PORT \ + -e CGW_KAFKA_CONSUME_TOPIC \ + -e CGW_KAFKA_PRODUCE_TOPIC \ + -e CGW_DB_NAME \ + -e CGW_DB_HOST \ + -e CGW_DB_PORT \ + -e CGW_DB_USERNAME \ + -e CGW_DB_PASSWORD \ + -e CGW_REDIS_HOST \ + -e CGW_REDIS_PORT \ + -e CGW_REDIS_USERNAME \ + -e CGW_REDIS_PASSWORD \ -e CGW_FEATURE_TOPOMAP_ENABLE \ - -e CGW_METRICS_PORT \ - -e CGW_ALLOW_CERT_MISMATCH \ + -e CGW_METRICS_PORT \ + -e CGW_ALLOW_CERT_MISMATCH \ -d -t --network=host --name $2 $1 ucentral-cgw diff --git a/src/cgw_app_args.rs b/src/cgw_app_args.rs new file mode 100644 index 0000000..5e6ce9f --- /dev/null +++ b/src/cgw_app_args.rs @@ -0,0 +1,472 @@ +use std::{env, net::Ipv4Addr, str::FromStr}; + +use crate::{ + cgw_errors::{Error, Result}, + AppCoreLogLevel, +}; + +const CGW_DEFAULT_ID: i32 = 0; +const CGW_DEFAULT_WSS_T_NUM: usize = 4; +const CGW_DEFAULT_LOG_LEVEL: AppCoreLogLevel = AppCoreLogLevel::Debug; +const CGW_DEFAULT_WSS_IP: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 0); +const CGW_DEFAULT_WSS_PORT: u16 = 15002; +const CGW_DEFAULT_WSS_CAS: &str = "cas.pem"; +const CGW_DEFAULT_WSS_CERT: &str = "cert.pem"; +const CGW_DEFAULT_WSS_KEY: &str = "key.pem"; +const CGW_DEFAULT_GRPC_LISTENING_IP: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 0); +const CGW_DEFAULT_GRPC_LISTENING_PORT: u16 = 50051; +const CGW_DEFAULT_GRPC_PUBLIC_HOST: &str = "localhost"; +const CGW_DEFAULT_GRPC_PUBLIC_PORT: u16 = 50051; +const CGW_DEFAULT_KAFKA_HOST: &str = "localhost"; +const CGW_DEFAULT_KAFKA_PORT: u16 = 9092; +const CGW_DEFAULT_KAFKA_CONSUME_TOPIC: &str = "CnC"; +const CGW_DEFAULT_KAFKA_PRODUCE_TOPIC: &str = "CnC_Res"; +const CGW_DEFAULT_DB_HOST: &str = "localhost"; +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_REDIS_HOST: &str = "localhost"; +const CGW_DEFAULT_REDIS_PORT: u16 = 6379; +const CGW_DEFAULT_ALLOW_CERT_MISMATCH: &str = "no"; +const CGW_DEFAULT_METRICS_PORT: u16 = 8080; +const CGW_DEFAULT_TOPOMAP_STATE: bool = false; + +pub struct CGWWSSArgs { + /// Number of thread in a threadpool dedicated for handling secure websocket connections + pub wss_t_num: usize, + /// IP to listen for incoming WSS connection + pub wss_ip: Ipv4Addr, + /// PORT to listen for incoming WSS connection + pub wss_port: u16, + /// WSS CAS certificate (contains root and issuer certificates) + pub wss_cas: String, + /// WSS certificate + pub wss_cert: String, + /// WSS private key + pub wss_key: String, + /// Allow Missmatch + pub allow_mismatch: bool, +} + +impl CGWWSSArgs { + fn parse() -> Result { + let wss_t_num: usize = match env::var("DEFAULT_WSS_THREAD_NUM") { + Ok(val) => match val.parse() { + Ok(v) => v, + Err(_e) => { + return Err(Error::AppArgsParser(format!( + "Failed to parse DEFAULT_WSS_THREAD_NUM! Invalid value: {}", + val + ))); + } + }, + Err(_) => CGW_DEFAULT_WSS_T_NUM, + }; + + let wss_ip: Ipv4Addr = match env::var("CGW_WSS_IP") { + Ok(val) => match Ipv4Addr::from_str(val.as_str()) { + Ok(v) => v, + Err(_e) => { + return Err(Error::AppArgsParser(format!( + "Failed to parse CGW_WSS_IP! Invalid value: {}", + val + ))); + } + }, + Err(_) => CGW_DEFAULT_WSS_IP, + }; + + let wss_port: u16 = match env::var("CGW_WSS_PORT") { + Ok(val) => match val.parse() { + Ok(v) => v, + Err(_e) => { + return Err(Error::AppArgsParser(format!( + "Failed to parse CGW_WSS_PORT! Invalid value: {}", + val + ))); + } + }, + Err(_) => CGW_DEFAULT_WSS_PORT, + }; + + let wss_cas: String = env::var("CGW_WSS_CAS").unwrap_or(CGW_DEFAULT_WSS_CAS.to_string()); + let wss_cert: String = env::var("CGW_WSS_CERT").unwrap_or(CGW_DEFAULT_WSS_CERT.to_string()); + let wss_key: String = env::var("CGW_WSS_KEY").unwrap_or(CGW_DEFAULT_WSS_KEY.to_string()); + + let mismatch: String = env::var("CGW_ALLOW_CERT_MISMATCH") + .unwrap_or(CGW_DEFAULT_ALLOW_CERT_MISMATCH.to_string()); + let allow_mismatch = mismatch == "yes"; + + Ok(CGWWSSArgs { + wss_t_num, + wss_ip, + wss_port, + wss_cas, + wss_cert, + wss_key, + allow_mismatch, + }) + } +} + +pub struct CGWGRPCArgs { + /// IP to listen for incoming GRPC connection + pub grpc_listening_ip: Ipv4Addr, + /// PORT to listen for incoming GRPC connection + pub grpc_listening_port: u16, + /// IP or hostname for Redis Record + pub grpc_public_host: String, + /// PORT for Redis record + pub grpc_public_port: u16, +} + +impl CGWGRPCArgs { + fn parse() -> Result { + let grpc_listening_ip: Ipv4Addr = match env::var("CGW_GRPC_LISTENING_IP") { + Ok(val) => match Ipv4Addr::from_str(val.as_str()) { + Ok(v) => v, + Err(_e) => { + return Err(Error::AppArgsParser(format!( + "Failed to parse CGW_GRPC_LISTENING_IP! Invalid value: {}", + val + ))); + } + }, + Err(_) => CGW_DEFAULT_GRPC_LISTENING_IP, + }; + + let grpc_listening_port: u16 = match env::var("CGW_GRPC_LISTENING_PORT") { + Ok(val) => match val.parse() { + Ok(v) => v, + Err(_e) => { + return Err(Error::AppArgsParser(format!( + "Failed to parse CGW_GRPC_LISTENING_PORT! Invalid value: {}", + val + ))); + } + }, + Err(_) => CGW_DEFAULT_GRPC_LISTENING_PORT, + }; + + let grpc_public_host: String = match env::var("CGW_GRPC_PUBLIC_HOST") { + Ok(val) => { + // 1. Try to parse variable into IpAddress + match Ipv4Addr::from_str(val.as_str()) { + // 2. If parsed - return IpAddress as String value + Ok(ip) => ip.to_string(), + // 3. If parse failed - probably hostname specified + Err(_e) => val, + } + } + // Env. variable is not setup - use default value + Err(_) => CGW_DEFAULT_GRPC_PUBLIC_HOST.to_string(), + }; + + let grpc_public_port: u16 = match env::var("CGW_GRPC_PUBLIC_PORT") { + Ok(val) => match val.parse() { + Ok(v) => v, + Err(_e) => { + return Err(Error::AppArgsParser(format!( + "Failed to parse CGW_GRPC_PUBLIC_PORT! Invalid value: {}", + val + ))); + } + }, + Err(_) => CGW_DEFAULT_GRPC_PUBLIC_PORT, + }; + + Ok(CGWGRPCArgs { + grpc_listening_ip, + grpc_listening_port, + grpc_public_host, + grpc_public_port, + }) + } +} + +pub struct CGWKafkaArgs { + /// IP or hostname to connect to KAFKA broker + pub kafka_host: String, + /// PORT to connect to KAFKA broker + pub kafka_port: u16, + /// KAFKA topic from where to consume messages + #[allow(unused)] + pub kafka_consume_topic: String, + /// KAFKA topic where to produce messages + #[allow(unused)] + pub kafka_produce_topic: String, +} + +impl CGWKafkaArgs { + fn parse() -> Result { + let kafka_host: String = match env::var("CGW_KAFKA_HOST") { + Ok(val) => { + // 1. Try to parse variable into IpAddress + match Ipv4Addr::from_str(val.as_str()) { + // 2. If parsed - return IpAddress as String value + Ok(ip) => ip.to_string(), + // 3. If parse failed - probably hostname specified + Err(_e) => val, + } + } + // Env. variable is not setup - use default value + Err(_) => CGW_DEFAULT_KAFKA_HOST.to_string(), + }; + + let kafka_port: u16 = match env::var("CGW_KAFKA_PORT") { + Ok(val) => match val.parse() { + Ok(v) => v, + Err(_e) => { + return Err(Error::AppArgsParser(format!( + "Failed to parse CGW_KAFKA_PORT! Invalid value: {}", + val + ))); + } + }, + Err(_) => CGW_DEFAULT_KAFKA_PORT, + }; + + let kafka_consume_topic: String = env::var("CGW_KAFKA_CONSUMER_TOPIC") + .unwrap_or(CGW_DEFAULT_KAFKA_CONSUME_TOPIC.to_string()); + let kafka_produce_topic: String = env::var("CGW_KAFKA_PRODUCER_TOPIC") + .unwrap_or(CGW_DEFAULT_KAFKA_PRODUCE_TOPIC.to_string()); + + Ok(CGWKafkaArgs { + kafka_host, + kafka_port, + kafka_consume_topic, + kafka_produce_topic, + }) + } +} + +pub struct CGWDBArgs { + /// IP or hostname to connect to DB (PSQL) + pub db_host: String, + /// PORT to connect to DB (PSQL) + pub db_port: u16, + /// DB name to connect to in DB (PSQL) + pub db_name: String, + /// DB user name use with connection to in DB (PSQL) + pub db_username: String, + /// DB user password use with connection to in DB (PSQL) + pub db_password: String, +} + +impl CGWDBArgs { + fn parse() -> Result { + let db_host: String = match env::var("CGW_DB_HOST") { + Ok(val) => { + // 1. Try to parse variable into IpAddress + match Ipv4Addr::from_str(val.as_str()) { + // 2. If parsed - return IpAddress as String value + Ok(ip) => ip.to_string(), + // 3. If parse failed - probably hostname specified + Err(_e) => val, + } + } + // Env. variable is not setup - use default value + Err(_) => CGW_DEFAULT_DB_HOST.to_string(), + }; + + let db_port: u16 = match env::var("CGW_DB_PORT") { + Ok(val) => match val.parse() { + Ok(v) => v, + Err(_e) => { + return Err(Error::AppArgsParser(format!( + "Failed to parse CGW_DB_PORT! Invalid value: {}", + val + ))); + } + }, + Err(_) => CGW_DEFAULT_DB_PORT, + }; + + let db_name: String = env::var("CGW_DB_NAME").unwrap_or(CGW_DEFAULT_DB_NAME.to_string()); + let db_username: String = + env::var("CGW_DB_USERNAME").unwrap_or(CGW_DEFAULT_DB_USERNAME.to_string()); + let db_password: String = + env::var("CGW_DB_PASSWORD").unwrap_or(CGW_DEFAULT_DB_PASSWORD.to_string()); + + Ok(CGWDBArgs { + db_host, + db_port, + db_name, + db_username, + db_password, + }) + } +} + +pub struct CGWRedisArgs { + /// IP or hostname to connect to REDIS + pub redis_host: String, + /// PORT to connect to REDIS + pub redis_port: u16, + /// REDIS username + pub redis_username: Option, + /// REDIS password + pub redis_password: Option, +} + +impl CGWRedisArgs { + fn parse() -> Result { + let redis_host: String = match env::var("CGW_REDIS_HOST") { + Ok(val) => { + // 1. Try to parse variable into IpAddress + match Ipv4Addr::from_str(val.as_str()) { + // 2. If parsed - return IpAddress as String value + Ok(ip) => ip.to_string(), + // 3. If parse failed - probably hostname specified + Err(_e) => val, + } + } + // Env. variable is not setup - use default value + Err(_) => CGW_DEFAULT_REDIS_HOST.to_string(), + }; + + let redis_port: u16 = match env::var("CGW_REDIS_PORT") { + Ok(val) => match val.parse() { + Ok(v) => v, + Err(_e) => { + return Err(Error::AppArgsParser(format!( + "Failed to parse CGW_REDIS_PORT! Invalid value: {}", + val + ))); + } + }, + Err(_) => CGW_DEFAULT_REDIS_PORT, + }; + + let redis_username: Option = match env::var("CGW_REDIS_USERNAME") { + Ok(username) => { + if username.is_empty() { + None + } else { + Some(username) + } + } + Err(_) => None, + }; + + let redis_password: Option = match env::var("CGW_REDIS_PASSWORD") { + Ok(password) => { + if password.is_empty() { + None + } else { + Some(password) + } + } + Err(_) => None, + }; + + Ok(CGWRedisArgs { + redis_host, + redis_port, + redis_username, + redis_password, + }) + } +} + +pub struct CGWMetricsArgs { + // PORT to connect to Metrics + pub metrics_port: u16, +} + +impl CGWMetricsArgs { + fn parse() -> Result { + let metrics_port: u16 = match env::var("CGW_METRICS_PORT") { + Ok(val) => match val.parse() { + Ok(v) => v, + Err(_e) => { + return Err(Error::AppArgsParser(format!( + "Failed to parse CGW_METRICS_PORT! Invalid value: {}", + val + ))); + } + }, + Err(_) => CGW_DEFAULT_METRICS_PORT, + }; + + Ok(CGWMetricsArgs { metrics_port }) + } +} + +pub struct AppArgs { + /// Loglevel of application + pub log_level: AppCoreLogLevel, + + /// CGW unique identifier (i32) + pub cgw_id: i32, + + /// Topomap featue status (enabled/disabled) + pub feature_topomap_enabled: bool, + + pub wss_args: CGWWSSArgs, + + pub grpc_args: CGWGRPCArgs, + + pub kafka_args: CGWKafkaArgs, + + pub db_args: CGWDBArgs, + + pub redis_args: CGWRedisArgs, + + pub metrics_args: CGWMetricsArgs, +} + +impl AppArgs { + pub fn parse() -> Result { + let log_level: AppCoreLogLevel = match env::var("CGW_LOG_LEVEL") { + Ok(val) => match val.parse() { + Ok(v) => v, + Err(_e) => { + return Err(Error::AppArgsParser(format!( + "Failed to parse CGW_LOG_LEVEL! Invalid value: {}", + val + ))); + } + }, + Err(_) => CGW_DEFAULT_LOG_LEVEL, + }; + + let cgw_id: i32 = match env::var("CGW_ID") { + Ok(val) => match val.parse() { + Ok(v) => v, + Err(_e) => { + return Err(Error::AppArgsParser(format!( + "Failed to parse CGW_ID! Invalid value: {}", + val + ))); + } + }, + Err(_) => CGW_DEFAULT_ID, + }; + + let feature_topomap_enabled: bool = match env::var("CGW_FEATURE_TOPOMAP_ENABLE") { + Ok(_) => true, + Err(_) => CGW_DEFAULT_TOPOMAP_STATE, + }; + + 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 metrics_args = CGWMetricsArgs::parse()?; + + Ok(AppArgs { + log_level, + cgw_id, + feature_topomap_enabled, + wss_args, + grpc_args, + kafka_args, + db_args, + redis_args, + metrics_args, + }) + } +} diff --git a/src/cgw_connection_server.rs b/src/cgw_connection_server.rs index 288ebf6..59f591c 100644 --- a/src/cgw_connection_server.rs +++ b/src/cgw_connection_server.rs @@ -187,7 +187,7 @@ impl CGWConnectionServer { pub async fn new(app_args: &AppArgs) -> Result> { let wss_runtime_handle = Arc::new( Builder::new_multi_thread() - .worker_threads(app_args.wss_t_num) + .worker_threads(app_args.wss_args.wss_t_num) .thread_name_fn(|| { static ATOMIC_ID: AtomicUsize = AtomicUsize::new(0); let id = ATOMIC_ID.fetch_add(1, Ordering::SeqCst); @@ -243,7 +243,8 @@ impl CGWConnectionServer { // Give NB API client a handle where it can do a TX (CLIENT -> CGW_SERVER) // RX is handled in internal_mbox of CGW_Server - let nb_api_c = match CGWNBApiClient::new(app_args, &nb_api_tx) { + let nb_api_c = match CGWNBApiClient::new(app_args.cgw_id, &app_args.kafka_args, &nb_api_tx) + { Ok(c) => c, Err(e) => { error!( @@ -272,7 +273,7 @@ impl CGWConnectionServer { }; let server = Arc::new(CGWConnectionServer { - allow_mismatch: app_args.allow_mismatch, + allow_mismatch: app_args.wss_args.allow_mismatch, local_cgw_id: app_args.cgw_id, connmap: CGWConnMap::new(), wss_rx_tx_runtime: wss_runtime_handle, @@ -489,7 +490,7 @@ impl CGWConnectionServer { CGWConnectionProcessorReqMsg::GroupIdChanged(new_gid); for mac in mac_list.iter() { - match connmap_r_lock.get(&mac) { + match connmap_r_lock.get(mac) { Some(c) => { let _ = c.send(msg.clone()); debug!("Notified {mac} about GID change (->{new_gid})"); diff --git a/src/cgw_db_accessor.rs b/src/cgw_db_accessor.rs index f888b30..6e4f3a9 100644 --- a/src/cgw_db_accessor.rs +++ b/src/cgw_db_accessor.rs @@ -1,5 +1,6 @@ -use crate::AppArgs; +use crate::cgw_app_args::CGWDBArgs; +use crate::cgw_tls::cgw_tls_create_db_connect; use crate::{ cgw_errors::{Error, Result}, cgw_metrics::{CGWMetrics, CGWMetricsHealthComponent, CGWMetricsHealthComponentStatus}, @@ -7,7 +8,7 @@ use crate::{ use eui48::MacAddress; -use tokio_postgres::{row::Row, Client, NoTls}; +use tokio_postgres::{row::Row, Client}; #[derive(Clone)] pub struct CGWDBInfra { @@ -51,21 +52,35 @@ pub struct CGWDBAccessor { } impl CGWDBAccessor { - pub async fn new(app_args: &AppArgs) -> Result { + pub async fn new(db_args: &CGWDBArgs) -> Result { let conn_str = format!( - "host={host} port={port} user={user} dbname={db} password={pass} connect_timeout=10", - host = app_args.db_host, - port = app_args.db_port, - user = app_args.db_username, - db = app_args.db_name, - pass = app_args.db_password + "sslmode={sslmode} host={host} port={port} user={user} dbname={db} password={pass} connect_timeout=10", + host = db_args.db_host, + port = db_args.db_port, + user = db_args.db_username, + db = db_args.db_name, + pass = db_args.db_password, + sslmode = "require", ); debug!( - "Trying to connect to DB ({}:{})...\nConn args {}", - app_args.db_host, app_args.db_port, conn_str + "Trying to connect to remote db ({}:{})...\nConn args {}", + db_args.db_host, db_args.db_port, conn_str ); - let (client, connection) = match tokio_postgres::connect(&conn_str, NoTls).await { + 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); diff --git a/src/cgw_errors.rs b/src/cgw_errors.rs index b93f50d..8bdcf6b 100644 --- a/src/cgw_errors.rs +++ b/src/cgw_errors.rs @@ -17,6 +17,8 @@ pub enum Error { Tls(String), + Redis(String), + UCentralParser(&'static str), UCentralMessagesQueue(&'static str), @@ -39,6 +41,9 @@ pub enum Error { #[from] TokioSync(tokio::sync::TryLockError), + #[from] + Tokiofs(tokio::fs::ReadDir), + #[from] IpAddressParse(std::net::AddrParseError), @@ -63,9 +68,6 @@ pub enum Error { #[from] InvalidUri(warp::http::uri::InvalidUri), - #[from] - RedisAsync(redis_async::error::Error), - #[from] StaticStr(&'static str), diff --git a/src/cgw_nb_api_listener.rs b/src/cgw_nb_api_listener.rs index 84147fc..815ffee 100644 --- a/src/cgw_nb_api_listener.rs +++ b/src/cgw_nb_api_listener.rs @@ -1,6 +1,6 @@ +use crate::cgw_app_args::CGWKafkaArgs; use crate::cgw_device::OldNew; use crate::cgw_ucentral_parser::CGWDeviceChange; -use crate::AppArgs; use crate::cgw_connection_server::{CGWConnectionNBAPIReqMsg, CGWConnectionNBAPIReqMsgOrigin}; use crate::cgw_errors::{Error, Result}; @@ -347,24 +347,21 @@ struct CGWCNCConsumer { } impl CGWCNCConsumer { - pub fn new(app_args: &AppArgs) -> Result { - let consum: CGWCNCConsumerType = Self::create_consumer(app_args)?; + pub fn new(cgw_id: i32, kafka_args: &CGWKafkaArgs) -> Result { + let consum: CGWCNCConsumerType = Self::create_consumer(cgw_id, kafka_args)?; Ok(CGWCNCConsumer { c: consum }) } - fn create_consumer(app_args: &AppArgs) -> Result { + fn create_consumer(cgw_id: i32, kafka_args: &CGWKafkaArgs) -> Result { let context = CustomContext; let consumer: CGWCNCConsumerType = match ClientConfig::new() .set("group.id", GROUP_ID) - .set( - "client.id", - GROUP_ID.to_string() + &app_args.cgw_id.to_string(), - ) - .set("group.instance.id", app_args.cgw_id.to_string()) + .set("client.id", GROUP_ID.to_string() + &cgw_id.to_string()) + .set("group.instance.id", cgw_id.to_string()) .set( "bootstrap.servers", - app_args.kafka_host.clone() + ":" + &app_args.kafka_port.to_string(), + kafka_args.kafka_host.clone() + ":" + &kafka_args.kafka_port.to_string(), ) .set("enable.partition.eof", "false") .set("session.timeout.ms", "6000") @@ -383,7 +380,7 @@ impl CGWCNCConsumer { debug!( "(consumer) (producer) Created lazy connection to kafka broker ({}:{})...", - app_args.kafka_host, app_args.kafka_port, + kafka_args.kafka_host, kafka_args.kafka_port, ); if let Err(e) = consumer.subscribe(&CONSUMER_TOPICS) { @@ -399,16 +396,16 @@ impl CGWCNCConsumer { } impl CGWCNCProducer { - pub fn new(app_args: &AppArgs) -> Result { - let prod: CGWCNCProducerType = Self::create_producer(app_args)?; + pub fn new(kafka_args: &CGWKafkaArgs) -> Result { + let prod: CGWCNCProducerType = Self::create_producer(kafka_args)?; Ok(CGWCNCProducer { p: prod }) } - fn create_producer(app_args: &AppArgs) -> Result { + fn create_producer(kafka_args: &CGWKafkaArgs) -> Result { let producer: FutureProducer = match ClientConfig::new() .set( "bootstrap.servers", - app_args.kafka_host.clone() + ":" + &app_args.kafka_port.to_string(), + kafka_args.kafka_host.clone() + ":" + &kafka_args.kafka_port.to_string(), ) .set("message.timeout.ms", "5000") .create() @@ -422,7 +419,7 @@ impl CGWCNCProducer { debug!( "(producer) Created lazy connection to kafka broker ({}:{})...", - app_args.kafka_host, app_args.kafka_port, + kafka_args.kafka_host, kafka_args.kafka_port, ); Ok(producer) @@ -438,7 +435,11 @@ pub struct CGWNBApiClient { } impl CGWNBApiClient { - pub fn new(app_args: &AppArgs, cgw_tx: &CGWConnectionServerMboxTx) -> Result> { + pub fn new( + cgw_id: i32, + kafka_args: &CGWKafkaArgs, + cgw_tx: &CGWConnectionServerMboxTx, + ) -> Result> { let working_runtime_h = Builder::new_multi_thread() .worker_threads(1) .thread_name("cgw-nb-api-l") @@ -449,11 +450,11 @@ impl CGWNBApiClient { let cl = Arc::new(CGWNBApiClient { working_runtime_handle: working_runtime_h, cgw_server_tx_mbox: cgw_tx.clone(), - prod: CGWCNCProducer::new(app_args)?, + prod: CGWCNCProducer::new(kafka_args)?, }); let cl_clone = cl.clone(); - let consumer: CGWCNCConsumer = CGWCNCConsumer::new(app_args)?; + let consumer: CGWCNCConsumer = CGWCNCConsumer::new(cgw_id, kafka_args)?; cl.working_runtime_handle.spawn(async move { loop { let cl_clone = cl_clone.clone(); diff --git a/src/cgw_remote_discovery.rs b/src/cgw_remote_discovery.rs index e9d06a3..a66b5dc 100644 --- a/src/cgw_remote_discovery.rs +++ b/src/cgw_remote_discovery.rs @@ -1,4 +1,5 @@ use crate::{ + cgw_app_args::CGWRedisArgs, cgw_db_accessor::{CGWDBAccessor, CGWDBInfra, CGWDBInfrastructureGroup}, cgw_device::{CGWDevice, CGWDeviceState}, cgw_devices_cache::CGWDevicesCache, @@ -8,6 +9,7 @@ use crate::{ CGWMetricsHealthComponentStatus, }, cgw_remote_client::CGWRemoteClient, + cgw_tls::cgw_read_root_certs_dir, AppArgs, }; @@ -15,9 +17,13 @@ use std::{ collections::HashMap, net::{Ipv4Addr, SocketAddr}, sync::Arc, + time::Duration, }; -use redis_async::resp_array; +use redis::{ + aio::MultiplexedConnection, Client, ConnectionInfo, RedisConnectionInfo, RedisResult, + TlsCertificates, ToRedisArgs, +}; use eui48::MacAddress; @@ -137,24 +143,48 @@ pub struct CGWRemoteIface { #[derive(Clone)] pub struct CGWRemoteDiscovery { db_accessor: Arc, - redis_client: redis_async::client::paired::PairedConnection, + redis_client: MultiplexedConnection, gid_to_cgw_cache: Arc>>, remote_cgws_map: Arc>>, local_shard_id: i32, } +async fn cgw_create_redis_client(redis_args: &CGWRedisArgs) -> Result { + 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, + }, + 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(); + + 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))), + } +} + impl CGWRemoteDiscovery { pub async fn new(app_args: &AppArgs) -> Result { debug!( "Trying to create redis db connection ({}:{})", - app_args.redis_host, app_args.redis_port + app_args.redis_args.redis_host, app_args.redis_args.redis_port ); - let redis_client = match redis_async::client::paired::paired_connect( - app_args.redis_host.clone(), - app_args.redis_port, - ) - .await - { + + let redis_client = match cgw_create_redis_client(&app_args.redis_args).await { Ok(c) => c, Err(e) => { error!( @@ -165,7 +195,24 @@ impl CGWRemoteDiscovery { } }; - let db_accessor = match CGWDBAccessor::new(app_args).await { + let redis_client = match redis_client + .get_multiplexed_tokio_connection_with_response_timeouts( + Duration::from_secs(1), + Duration::from_secs(5), + ) + .await + { + Ok(conn) => conn, + Err(e) => { + error!( + "Can't create CGW Remote Discovery client: Get Redis async connection failed ({})", + e + ); + return Err(Error::RemoteDiscovery("Redis client create failed")); + } + }; + + let db_accessor = match CGWDBAccessor::new(&app_args.db_args).await { Ok(c) => c, Err(e) => { error!( @@ -206,41 +253,34 @@ impl CGWRemoteDiscovery { { let redisdb_shard_info = CGWREDISDBShard { id: app_args.cgw_id, - server_host: app_args.grpc_public_host.clone(), - server_port: app_args.grpc_public_port, + server_host: app_args.grpc_args.grpc_public_host.clone(), + server_port: app_args.grpc_args.grpc_public_port, assigned_groups_num: 0i32, capacity: 1000i32, threshold: 50i32, }; let redis_req_data: Vec = redisdb_shard_info.into(); + let mut con = rc.redis_client.clone(); - if let Err(e) = rc - .redis_client - .send::(resp_array![ - "DEL", - format!("{REDIS_KEY_SHARD_ID_PREFIX}{}", app_args.cgw_id) - ]) - .await - { + let res: RedisResult<()> = redis::cmd("DEL") + .arg(format!("{REDIS_KEY_SHARD_ID_PREFIX}{}", app_args.cgw_id)) + .query_async(&mut con) + .await; + if res.is_err() { warn!( - "Failed to destroy record about shard in REDIS, first launch? ({:?})", - e + "Failed to destroy record about shard in REDIS, first launch? ({})", + res.err().unwrap() ); } - if let Err(e) = rc - .redis_client - .send::( - resp_array![ - "HSET", - format!("{REDIS_KEY_SHARD_ID_PREFIX}{}", app_args.cgw_id) - ] - .append(redis_req_data), - ) - .await - { - error!("Can't create CGW Remote Discovery client: Failed to create record about shard in REDIS: {:?}", e); + let res: RedisResult<()> = redis::cmd("HSET") + .arg(format!("{REDIS_KEY_SHARD_ID_PREFIX}{}", app_args.cgw_id)) + .arg(redis_req_data.to_redis_args()) + .query_async(&mut con) + .await; + if res.is_err() { + error!("Can't create CGW Remote Discovery client: Failed to create record about shard in REDIS: {}", res.err().unwrap()); return Err(Error::RemoteDiscovery( "Failed to create record about shard in REDIS", )); @@ -300,50 +340,41 @@ impl CGWRemoteDiscovery { // Clear hashmap lock.clear(); + let mut con = self.redis_client.clone(); - let redis_keys: Vec = match self - .redis_client - .send::>(resp_array!["KEYS", format!("{}*", REDIS_KEY_GID)]) + let redis_keys: Vec = match redis::cmd("KEYS") + .arg(format!("{REDIS_KEY_GID}*")) + .query_async(&mut con) .await { - Err(_) => { + Err(e) => { + error!("Failed to sync gid to cgw map:\n{}", e); return Err(Error::RemoteDiscovery("Failed to get KEYS list from REDIS")); } - Ok(r) => r, + Ok(keys) => keys, }; for key in redis_keys { - let gid: i32 = match self - .redis_client - .send::(resp_array!["HGET", &key, REDIS_KEY_GID_VALUE_GID]) + let gid: i32 = match redis::cmd("HGET") + .arg(&key) + .arg(REDIS_KEY_GID_VALUE_GID) + .query_async(&mut con) .await { - Ok(res) => { - match res.parse::() { - Ok(res) => res, - Err(e) => { - warn!("Found proper key '{key}' entry, but failed to parse GID from it:\n{e}"); - continue; - } - } - } + Ok(gid) => gid, Err(e) => { warn!("Found proper key '{key}' entry, but failed to fetch GID from it:\n{e}"); continue; } }; - let shard_id: i32 = match self - .redis_client - .send::(resp_array!["HGET", &key, REDIS_KEY_GID_VALUE_SHARD_ID]) + + let shard_id: i32 = match redis::cmd("HGET") + .arg(&key) + .arg(REDIS_KEY_GID_VALUE_SHARD_ID) + .query_async(&mut con) .await { - Ok(res) => match res.parse::() { - Ok(res) => res, - Err(e) => { - warn!("Found proper key '{key}' entry, but failed to parse SHARD_ID from it:\n{e}"); - continue; - } - }, + Ok(shard_id) => shard_id, Err(e) => { warn!("Found proper key '{key}' entry, but failed to fetch SHARD_ID from it:\n{e}"); continue; @@ -398,20 +429,27 @@ impl CGWRemoteDiscovery { // Clear hashmap lock.clear(); - let redis_keys: Vec = self - .redis_client - .send::>(resp_array![ - "KEYS", - format!("{}*", REDIS_KEY_SHARD_ID_PREFIX) - ]) - .await?; + let mut con = self.redis_client.clone(); + let redis_keys: Vec = match redis::cmd("KEYS") + .arg(format!("{REDIS_KEY_SHARD_ID_PREFIX}*")) + .query_async(&mut con) + .await + { + Ok(keys) => keys, + Err(e) => { + error!( + "Can't sync remote CGW map: Failed to get shard record in REDIS: {}", + e + ); + return Err(Error::RemoteDiscovery("Failed to get KEYS list from REDIS")); + } + }; for key in redis_keys { - match self - .redis_client - .send::>(resp_array!["HGETALL", &key]) - .await - { + let res: RedisResult> = + redis::cmd("HGETALL").arg(&key).query_async(&mut con).await; + + match res { Ok(res) => { let shrd: CGWREDISDBShard = CGWREDISDBShard::from(res); if shrd == CGWREDISDBShard::default() { @@ -463,14 +501,22 @@ impl CGWRemoteDiscovery { async fn increment_cgw_assigned_groups_num(&self, cgw_id: i32) -> Result<()> { debug!("Incrementing assigned groups num cgw_id_{cgw_id}"); - self.redis_client - .send::(resp_array![ - "HINCRBY", - format!("{}{cgw_id}", REDIS_KEY_SHARD_ID_PREFIX), - REDIS_KEY_SHARD_VALUE_ASSIGNED_G_NUM, - "1" - ]) - .await?; + let mut con = self.redis_client.clone(); + let res: RedisResult<()> = redis::cmd("HINCRBY") + .arg(format!("{}{cgw_id}", REDIS_KEY_SHARD_ID_PREFIX)) + .arg(REDIS_KEY_SHARD_VALUE_ASSIGNED_G_NUM) + .arg("1") + .query_async(&mut con) + .await; + if res.is_err() { + error!( + "Failed to increment assigned group number:\n{}", + res.err().unwrap() + ); + return Err(Error::RemoteDiscovery( + "Failed to increment assigned group number", + )); + } if cgw_id == self.local_shard_id { CGWMetrics::get_ref().change_counter( @@ -484,14 +530,22 @@ impl CGWRemoteDiscovery { async fn decrement_cgw_assigned_groups_num(&self, cgw_id: i32) -> Result<()> { debug!("Decrementing assigned groups num cgw_id_{cgw_id}"); - self.redis_client - .send::(resp_array![ - "HINCRBY", - format!("{}{cgw_id}", REDIS_KEY_SHARD_ID_PREFIX), - REDIS_KEY_SHARD_VALUE_ASSIGNED_G_NUM, - "-1" - ]) - .await?; + let mut con = self.redis_client.clone(); + let res: RedisResult<()> = redis::cmd("HINCRBY") + .arg(format!("{}{cgw_id}", REDIS_KEY_SHARD_ID_PREFIX)) + .arg(REDIS_KEY_SHARD_VALUE_ASSIGNED_G_NUM) + .arg("-1") + .query_async(&mut con) + .await; + if res.is_err() { + error!( + "Failed to decrement assigned group number:\n{}", + res.err().unwrap() + ); + return Err(Error::RemoteDiscovery( + "Failed to decrement assigned groups number", + )); + } if cgw_id == self.local_shard_id { CGWMetrics::get_ref().change_counter( @@ -548,16 +602,27 @@ impl CGWRemoteDiscovery { let dst_cgw_id: i32 = self.get_infra_group_cgw_assignee().await?; - self.redis_client - .send::(resp_array![ - "HSET", - format!("{REDIS_KEY_GID}{gid}"), - REDIS_KEY_GID_VALUE_GID, - gid.to_string(), - REDIS_KEY_GID_VALUE_SHARD_ID, - dst_cgw_id.to_string() - ]) - .await?; + let mut con = self.redis_client.clone(); + let res: RedisResult<()> = redis::cmd("HSET") + .arg(format!("{REDIS_KEY_GID}{gid}")) + .arg(REDIS_KEY_GID_VALUE_GID) + .arg(gid.to_string()) + .arg(REDIS_KEY_GID_VALUE_SHARD_ID) + .arg(dst_cgw_id.to_string()) + .query_async(&mut con) + .await; + + if res.is_err() { + error!( + "Failed to assign infra group {} to cgw {}:\n{}", + gid, + dst_cgw_id, + res.err().unwrap() + ); + return Err(Error::RemoteDiscovery( + "Failed to assign infra group to cgw", + )); + } self.gid_to_cgw_cache.write().await.insert(gid, dst_cgw_id); @@ -567,11 +632,24 @@ impl CGWRemoteDiscovery { } pub async fn deassign_infra_group_to_cgw(&self, gid: i32) -> Result<()> { - self.redis_client - .send::(resp_array!["DEL", format!("{REDIS_KEY_GID}{gid}")]) - .await?; + let mut con = self.redis_client.clone(); + let res: RedisResult<()> = redis::cmd("DEL") + .arg(format!("{REDIS_KEY_GID}{gid}")) + .query_async(&mut con) + .await; + + if res.is_err() { + error!( + "Failed to deassign infra group {}:\n{}", + gid, + res.err().unwrap() + ); + return Err(Error::RemoteDiscovery( + "Failed to deassign infra group to cgw", + )); + } - debug!("REDIS: deassigned gid{gid} from controlled CGW"); + debug!("REDIS: deassigned gid {gid} from controlled CGW"); self.gid_to_cgw_cache.write().await.remove(&gid); @@ -798,18 +876,19 @@ impl CGWRemoteDiscovery { // Clear local cache self.gid_to_cgw_cache.write().await.clear(); + let mut con = self.redis_client.clone(); for (cgw_id, _val) in self.remote_cgws_map.read().await.iter() { - if let Err(e) = self - .redis_client - .send::(resp_array![ - "HSET", - format!("{}{cgw_id}", REDIS_KEY_SHARD_ID_PREFIX), - REDIS_KEY_SHARD_VALUE_ASSIGNED_G_NUM, - "0" - ]) - .await - { - warn!("Failed to reset CGW{cgw_id} assigned group num count, e:{e}"); + let res: RedisResult<()> = redis::cmd("HSET") + .arg(format!("{}{cgw_id}", REDIS_KEY_SHARD_ID_PREFIX)) + .arg(REDIS_KEY_SHARD_VALUE_ASSIGNED_G_NUM) + .arg("0") + .query_async(&mut con) + .await; + if res.is_err() { + warn!( + "Failed to reset CGW{cgw_id} assigned group num count, e:{}", + res.err().unwrap() + ); } } @@ -834,12 +913,13 @@ impl CGWRemoteDiscovery { pub async fn cleanup_redis(&self) { debug!("Remove from Redis shard id {}", self.local_shard_id); // We are on de-init stage - ignore any errors on Redis clean-up - let _ = self - .redis_client - .send::(resp_array![ - "DEL", - format!("{REDIS_KEY_SHARD_ID_PREFIX}{}", self.local_shard_id) - ]) + let mut con = self.redis_client.clone(); + let _res: RedisResult<()> = redis::cmd("DEL") + .arg(format!( + "{REDIS_KEY_SHARD_ID_PREFIX}{}", + self.local_shard_id + )) + .query_async(&mut con) .await; } } diff --git a/src/cgw_remote_server.rs b/src/cgw_remote_server.rs index b559484..b064389 100644 --- a/src/cgw_remote_server.rs +++ b/src/cgw_remote_server.rs @@ -1,4 +1,4 @@ -use crate::AppArgs; +use crate::cgw_app_args::CGWGRPCArgs; pub mod cgw_remote { tonic::include_proto!("cgw.remote"); @@ -52,11 +52,11 @@ pub struct CGWRemoteServer { } impl CGWRemoteServer { - pub fn new(app_args: &AppArgs) -> Self { + pub fn new(cgw_id: i32, grpc_args: &CGWGRPCArgs) -> Self { let remote_cfg = CGWRemoteConfig::new( - app_args.cgw_id, - app_args.grpc_listening_ip, - app_args.grpc_listening_port, + cgw_id, + grpc_args.grpc_listening_ip, + grpc_args.grpc_listening_port, ); CGWRemoteServer { cfg: remote_cfg } } diff --git a/src/cgw_tls.rs b/src/cgw_tls.rs index 76715c7..7e011c3 100644 --- a/src/cgw_tls.rs +++ b/src/cgw_tls.rs @@ -1,10 +1,15 @@ +use crate::cgw_app_args::CGWWSSArgs; use crate::cgw_errors::{collect_results, Error, Result}; -use crate::AppArgs; use eui48::MacAddress; use rustls_pki_types::{CertificateDer, PrivateKeyDer}; +use std::fs; +use std::io::BufRead; +use std::path::Path; use std::{fs::File, io::BufReader, str::FromStr, sync::Arc}; use tokio::net::TcpStream; +use tokio_postgres_rustls::MakeRustlsConnect; +use tokio_rustls::rustls; use tokio_rustls::{ rustls::{server::WebPkiClientVerifier, RootCertStore, ServerConfig}, server::TlsStream, @@ -13,6 +18,7 @@ use tokio_rustls::{ use x509_parser::parse_x509_certificate; const CGW_TLS_CERTIFICATES_PATH: &str = "/etc/cgw/certs"; +const CGW_TLS_NB_INFRA_CERTS_PATH: &str = "/etc/cgw/nb_infra/certs"; pub async fn cgw_tls_read_certs(cert_file: &str) -> Result>> { let file = match File::open(cert_file) { @@ -103,9 +109,10 @@ pub async fn cgw_tls_get_cn_from_stream(stream: &TlsStream) -> Result Err(Error::Tls("Failed to read peer comman name!".to_string())) } -pub async fn cgw_tls_create_acceptor(args: &AppArgs) -> Result { + +pub async fn cgw_tls_create_acceptor(wss_args: &CGWWSSArgs) -> Result { // Read root/issuer certs. - let cas_path = format!("{}/{}", CGW_TLS_CERTIFICATES_PATH, args.wss_cas); + let cas_path = format!("{}/{}", CGW_TLS_CERTIFICATES_PATH, wss_args.wss_cas); let cas = match cgw_tls_read_certs(cas_path.as_str()).await { Ok(cas_pem) => cas_pem, Err(e) => { @@ -115,7 +122,7 @@ pub async fn cgw_tls_create_acceptor(args: &AppArgs) -> Result { }; // Read cert. - let cert_path = format!("{}/{}", CGW_TLS_CERTIFICATES_PATH, args.wss_cert); + let cert_path = format!("{}/{}", CGW_TLS_CERTIFICATES_PATH, wss_args.wss_cert); let mut cert = match cgw_tls_read_certs(cert_path.as_str()).await { Ok(cert_pem) => cert_pem, Err(e) => { @@ -126,7 +133,7 @@ pub async fn cgw_tls_create_acceptor(args: &AppArgs) -> Result { cert.extend(cas.clone()); // Read private key. - let key_path = format!("{}/{}", CGW_TLS_CERTIFICATES_PATH, args.wss_key); + let key_path = format!("{}/{}", CGW_TLS_CERTIFICATES_PATH, wss_args.wss_key); let key = match cgw_tls_read_private_key(key_path.as_str()).await { Ok(pkey) => pkey, Err(e) => { @@ -162,3 +169,53 @@ pub async fn cgw_tls_create_acceptor(args: &AppArgs) -> Result { // Create the TLS acceptor. Ok(TlsAcceptor::from(Arc::new(config))) } + +pub async fn cgw_read_root_certs_dir() -> Result> { + let mut certs_vec = Vec::new(); + + // Read the directory entries + for entry in fs::read_dir(Path::new(CGW_TLS_NB_INFRA_CERTS_PATH))? { + let entry = entry?; + let path = entry.path(); + + // Check if the entry is a file and has a .crt extension (or other extensions if needed) + if path.is_file() { + let extension = path.extension().and_then(|ext| ext.to_str()); + if extension == Some("crt") || extension == Some("pem") { + let cert_contents = fs::read(path)?; + certs_vec.extend(cert_contents); + } + } + } + + Ok(certs_vec) +} + +pub async fn cgw_get_root_certs_store() -> Result { + let certs = cgw_read_root_certs_dir().await?; + + let buf = &mut certs.as_slice() as &mut dyn BufRead; + let certs = rustls_pemfile::certs(buf); + let mut root_cert_store = rustls::RootCertStore::empty(); + for cert in certs.flatten() { + let _r = root_cert_store.add(cert); + } + + Ok(root_cert_store) +} + +pub async fn cgw_tls_create_db_connect() -> Result { + let root_store = match cgw_get_root_certs_store().await { + Ok(certs) => certs, + Err(e) => { + error!("{}", e.to_string()); + return Err(e); + } + }; + + let config = rustls::ClientConfig::builder() + .with_root_certificates(root_store) + .with_no_client_auth(); + + Ok(tokio_postgres_rustls::MakeRustlsConnect::new(config)) +} diff --git a/src/main.rs b/src/main.rs index a50a710..f8fbe26 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ #![warn(rust_2018_idioms)] +mod cgw_app_args; mod cgw_connection_processor; mod cgw_connection_server; mod cgw_db_accessor; @@ -23,6 +24,7 @@ extern crate log; #[macro_use] extern crate lazy_static; +use cgw_app_args::AppArgs; use tokio::{ net::TcpListener, runtime::{Builder, Handle, Runtime}, @@ -31,12 +33,7 @@ use tokio::{ time::{sleep, Duration}, }; -use std::{ - env, - net::{Ipv4Addr, SocketAddr}, - str::FromStr, - sync::Arc, -}; +use std::{env, net::SocketAddr, str::FromStr, sync::Arc}; use rlimit::{setrlimit, Resource}; @@ -70,369 +67,6 @@ impl FromStr for AppCoreLogLevel { } } -const CGW_DEFAULT_ID: i32 = 0; -const CGW_DEFAULT_WSS_T_NUM: usize = 4; -const CGW_DEFAULT_LOG_LEVEL: AppCoreLogLevel = AppCoreLogLevel::Debug; -const CGW_DEFAULT_WSS_IP: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 0); -const CGW_DEFAULT_WSS_PORT: u16 = 15002; -const CGW_DEFAULT_WSS_CAS: &str = "cas.pem"; -const CGW_DEFAULT_WSS_CERT: &str = "cert.pem"; -const CGW_DEFAULT_WSS_KEY: &str = "key.pem"; -const CGW_DEFAULT_GRPC_LISTENING_IP: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 0); -const CGW_DEFAULT_GRPC_LISTENING_PORT: u16 = 50051; -const CGW_DEFAULT_GRPC_PUBLIC_HOST: &str = "localhost"; -const CGW_DEFAULT_GRPC_PUBLIC_PORT: u16 = 50051; -const CGW_DEFAULT_KAFKA_HOST: &str = "localhost"; -const CGW_DEFAULT_KAFKA_PORT: u16 = 9092; -const CGW_DEFAULT_KAFKA_CONSUME_TOPIC: &str = "CnC"; -const CGW_DEFAULT_KAFKA_PRODUCE_TOPIC: &str = "CnC_Res"; -const CGW_DEFAULT_DB_HOST: &str = "localhost"; -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_REDIS_HOST: &str = "localhost"; -const CGW_DEFAULT_REDIS_PORT: u16 = 6379; -const CGW_DEFAULT_ALLOW_CERT_MISMATCH: &str = "no"; -const CGW_DEFAULT_METRICS_PORT: u16 = 8080; -const CGW_DEFAULT_TOPOMAP_STATE: bool = false; - -/// CGW server -pub struct AppArgs { - /// Loglevel of application - log_level: AppCoreLogLevel, - - /// CGW unique identifier (u64) - cgw_id: i32, - - /// Number of thread in a threadpool dedicated for handling secure websocket connections - wss_t_num: usize, - /// IP to listen for incoming WSS connection - wss_ip: Ipv4Addr, - /// PORT to listen for incoming WSS connection - wss_port: u16, - /// WSS CAS certificate (contains root and issuer certificates) - wss_cas: String, - /// WSS certificate - wss_cert: String, - /// WSS private key - wss_key: String, - - /// IP to listen for incoming GRPC connection - grpc_listening_ip: Ipv4Addr, - /// PORT to listen for incoming GRPC connection - grpc_listening_port: u16, - /// IP or hostname for Redis Record - grpc_public_host: String, - /// PORT for Redis record - grpc_public_port: u16, - - /// IP or hostname to connect to KAFKA broker - kafka_host: String, - /// PORT to connect to KAFKA broker - kafka_port: u16, - /// KAFKA topic from where to consume messages - #[allow(unused)] - kafka_consume_topic: String, - /// KAFKA topic where to produce messages - #[allow(unused)] - kafka_produce_topic: String, - - /// IP or hostname to connect to DB (PSQL) - db_host: String, - /// PORT to connect to DB (PSQL) - db_port: u16, - /// DB name to connect to in DB (PSQL) - db_name: String, - /// DB user name use with connection to in DB (PSQL) - db_username: String, - /// DB user password use with connection to in DB (PSQL) - db_password: String, - - /// IP or hostname to connect to REDIS - redis_host: String, - /// PORT to connect to REDIS - redis_port: u16, - - /// Allow Missmatch - allow_mismatch: bool, - - /// PORT to connect to Metrics - metrics_port: u16, - - /// Topomap featue status (enabled/disabled) - feature_topomap_enabled: bool, -} - -impl AppArgs { - fn parse() -> Result { - let log_level: AppCoreLogLevel = match env::var("CGW_LOG_LEVEL") { - Ok(val) => match val.parse() { - Ok(v) => v, - Err(_e) => { - return Err(Error::AppArgsParser(format!( - "Failed to parse CGW_LOG_LEVEL! Invalid value: {}", - val - ))); - } - }, - Err(_) => CGW_DEFAULT_LOG_LEVEL, - }; - - let cgw_id: i32 = match env::var("CGW_ID") { - Ok(val) => match val.parse() { - Ok(v) => v, - Err(_e) => { - return Err(Error::AppArgsParser(format!( - "Failed to parse CGW_ID! Invalid value: {}", - val - ))); - } - }, - Err(_) => CGW_DEFAULT_ID, - }; - - let wss_t_num: usize = match env::var("DEFAULT_WSS_THREAD_NUM") { - Ok(val) => match val.parse() { - Ok(v) => v, - Err(_e) => { - return Err(Error::AppArgsParser(format!( - "Failed to parse DEFAULT_WSS_THREAD_NUM! Invalid value: {}", - val - ))); - } - }, - Err(_) => CGW_DEFAULT_WSS_T_NUM, - }; - - let wss_ip: Ipv4Addr = match env::var("CGW_WSS_IP") { - Ok(val) => match Ipv4Addr::from_str(val.as_str()) { - Ok(v) => v, - Err(_e) => { - return Err(Error::AppArgsParser(format!( - "Failed to parse CGW_WSS_IP! Invalid value: {}", - val - ))); - } - }, - Err(_) => CGW_DEFAULT_WSS_IP, - }; - - let wss_port: u16 = match env::var("CGW_WSS_PORT") { - Ok(val) => match val.parse() { - Ok(v) => v, - Err(_e) => { - return Err(Error::AppArgsParser(format!( - "Failed to parse CGW_WSS_PORT! Invalid value: {}", - val - ))); - } - }, - Err(_) => CGW_DEFAULT_WSS_PORT, - }; - - let wss_cas: String = env::var("CGW_WSS_CAS").unwrap_or(CGW_DEFAULT_WSS_CAS.to_string()); - let wss_cert: String = env::var("CGW_WSS_CERT").unwrap_or(CGW_DEFAULT_WSS_CERT.to_string()); - let wss_key: String = env::var("CGW_WSS_KEY").unwrap_or(CGW_DEFAULT_WSS_KEY.to_string()); - - let grpc_listening_ip: Ipv4Addr = match env::var("CGW_GRPC_LISTENING_IP") { - Ok(val) => match Ipv4Addr::from_str(val.as_str()) { - Ok(v) => v, - Err(_e) => { - return Err(Error::AppArgsParser(format!( - "Failed to parse CGW_GRPC_LISTENING_IP! Invalid value: {}", - val - ))); - } - }, - Err(_) => CGW_DEFAULT_GRPC_LISTENING_IP, - }; - - let grpc_listening_port: u16 = match env::var("CGW_GRPC_LISTENING_PORT") { - Ok(val) => match val.parse() { - Ok(v) => v, - Err(_e) => { - return Err(Error::AppArgsParser(format!( - "Failed to parse CGW_GRPC_LISTENING_PORT! Invalid value: {}", - val - ))); - } - }, - Err(_) => CGW_DEFAULT_GRPC_LISTENING_PORT, - }; - - let grpc_public_host: String = match env::var("CGW_GRPC_PUBLIC_HOST") { - Ok(val) => { - // 1. Try to parse variable into IpAddress - match Ipv4Addr::from_str(val.as_str()) { - // 2. If parsed - return IpAddress as String value - Ok(ip) => ip.to_string(), - // 3. If parse failed - probably hostname specified - Err(_e) => val, - } - } - // Env. variable is not setup - use default value - Err(_) => CGW_DEFAULT_GRPC_PUBLIC_HOST.to_string(), - }; - - let grpc_public_port: u16 = match env::var("CGW_GRPC_PUBLIC_PORT") { - Ok(val) => match val.parse() { - Ok(v) => v, - Err(_e) => { - return Err(Error::AppArgsParser(format!( - "Failed to parse CGW_GRPC_PUBLIC_PORT! Invalid value: {}", - val - ))); - } - }, - Err(_) => CGW_DEFAULT_GRPC_PUBLIC_PORT, - }; - - let kafka_host: String = match env::var("CGW_KAFKA_HOST") { - Ok(val) => { - // 1. Try to parse variable into IpAddress - match Ipv4Addr::from_str(val.as_str()) { - // 2. If parsed - return IpAddress as String value - Ok(ip) => ip.to_string(), - // 3. If parse failed - probably hostname specified - Err(_e) => val, - } - } - // Env. variable is not setup - use default value - Err(_) => CGW_DEFAULT_KAFKA_HOST.to_string(), - }; - - let kafka_port: u16 = match env::var("CGW_KAFKA_PORT") { - Ok(val) => match val.parse() { - Ok(v) => v, - Err(_e) => { - return Err(Error::AppArgsParser(format!( - "Failed to parse CGW_KAFKA_PORT! Invalid value: {}", - val - ))); - } - }, - Err(_) => CGW_DEFAULT_KAFKA_PORT, - }; - - let kafka_consume_topic: String = env::var("CGW_KAFKA_CONSUMER_TOPIC") - .unwrap_or(CGW_DEFAULT_KAFKA_CONSUME_TOPIC.to_string()); - let kafka_produce_topic: String = env::var("CGW_KAFKA_PRODUCER_TOPIC") - .unwrap_or(CGW_DEFAULT_KAFKA_PRODUCE_TOPIC.to_string()); - - let db_host: String = match env::var("CGW_DB_HOST") { - Ok(val) => { - // 1. Try to parse variable into IpAddress - match Ipv4Addr::from_str(val.as_str()) { - // 2. If parsed - return IpAddress as String value - Ok(ip) => ip.to_string(), - // 3. If parse failed - probably hostname specified - Err(_e) => val, - } - } - // Env. variable is not setup - use default value - Err(_) => CGW_DEFAULT_DB_HOST.to_string(), - }; - - let db_port: u16 = match env::var("CGW_DB_PORT") { - Ok(val) => match val.parse() { - Ok(v) => v, - Err(_e) => { - return Err(Error::AppArgsParser(format!( - "Failed to parse CGW_DB_PORT! Invalid value: {}", - val - ))); - } - }, - Err(_) => CGW_DEFAULT_DB_PORT, - }; - - let db_name: String = env::var("CGW_DB_NAME").unwrap_or(CGW_DEFAULT_DB_NAME.to_string()); - let db_username: String = - env::var("CGW_DB_USERNAME").unwrap_or(CGW_DEFAULT_DB_USERNAME.to_string()); - let db_password: String = - env::var("CGW_DB_PASSWORD").unwrap_or(CGW_DEFAULT_DB_PASSWORD.to_string()); - - let redis_host: String = match env::var("CGW_REDIS_HOST") { - Ok(val) => { - // 1. Try to parse variable into IpAddress - match Ipv4Addr::from_str(val.as_str()) { - // 2. If parsed - return IpAddress as String value - Ok(ip) => ip.to_string(), - // 3. If parse failed - probably hostname specified - Err(_e) => val, - } - } - // Env. variable is not setup - use default value - Err(_) => CGW_DEFAULT_REDIS_HOST.to_string(), - }; - - let redis_port: u16 = match env::var("CGW_REDIS_PORT") { - Ok(val) => match val.parse() { - Ok(v) => v, - Err(_e) => { - return Err(Error::AppArgsParser(format!( - "Failed to parse CGW_REDIS_PORT! Invalid value: {}", - val - ))); - } - }, - 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"; - - let metrics_port: u16 = match env::var("CGW_METRICS_PORT") { - Ok(val) => match val.parse() { - Ok(v) => v, - Err(_e) => { - return Err(Error::AppArgsParser(format!( - "Failed to parse CGW_METRICS_PORT! Invalid value: {}", - val - ))); - } - }, - Err(_) => CGW_DEFAULT_METRICS_PORT, - }; - - let feature_topomap_enabled: bool = match env::var("CGW_FEATURE_TOPOMAP_ENABLE") { - Ok(_) => true, - Err(_) => CGW_DEFAULT_TOPOMAP_STATE, - }; - - Ok(AppArgs { - log_level, - cgw_id, - wss_t_num, - wss_ip, - wss_port, - wss_cas, - wss_cert, - wss_key, - grpc_listening_ip, - grpc_listening_port, - grpc_public_host, - grpc_public_port, - kafka_host, - kafka_port, - kafka_consume_topic, - kafka_produce_topic, - db_host, - db_port, - db_name, - db_username, - db_password, - redis_host, - redis_port, - allow_mismatch, - metrics_port, - feature_topomap_enabled, - }) - } -} - pub struct AppCore { cgw_server: Arc, main_runtime_handle: Arc, @@ -496,7 +130,7 @@ impl AppCore { let main_runtime_handle: Arc = self.main_runtime_handle.clone(); let core_clone = self.clone(); - let cgw_remote_server = CGWRemoteServer::new(&self.args); + let cgw_remote_server = CGWRemoteServer::new(self.args.cgw_id, &self.args.grpc_args); let cgw_srv_clone = self.cgw_server.clone(); let cgw_con_serv = self.cgw_server.clone(); self.grpc_server_runtime_handle.spawn(async move { @@ -527,12 +161,12 @@ async fn server_loop(app_core: Arc) -> Result<()> { debug!( "Starting WSS server, listening at {}:{}", - app_core.args.wss_ip, app_core.args.wss_port + app_core.args.wss_args.wss_ip, app_core.args.wss_args.wss_port ); // Bind the server's socket let sockaddress = SocketAddr::new( - std::net::IpAddr::V4(app_core.args.wss_ip), - app_core.args.wss_port, + std::net::IpAddr::V4(app_core.args.wss_args.wss_ip), + app_core.args.wss_args.wss_port, ); let listener: Arc = match TcpListener::bind(sockaddress).await { Ok(listener) => Arc::new(listener), @@ -548,7 +182,7 @@ async fn server_loop(app_core: Arc) -> Result<()> { } }; - let tls_acceptor = match cgw_tls_create_acceptor(&app_core.args).await { + let tls_acceptor = match cgw_tls_create_acceptor(&app_core.args.wss_args).await { Ok(acceptor) => acceptor, Err(e) => { error!("Failed to create TLS acceptor. Error: {}", e.to_string()); @@ -659,7 +293,9 @@ async fn main() -> Result<()> { // Make sure metrics are available any of the components // starts up; - CGWMetrics::get_ref().start(args.metrics_port).await?; + CGWMetrics::get_ref() + .start(args.metrics_args.metrics_port) + .await?; let app = Arc::new(AppCore::new(args).await?); app.run(shutdown_notify).await; diff --git a/utils/docker/certs/server.crt b/utils/docker/certs/server.crt new file mode 100644 index 0000000..ffeff23 --- /dev/null +++ b/utils/docker/certs/server.crt @@ -0,0 +1,58 @@ +-----BEGIN CERTIFICATE----- +MIIE+jCCAuKgAwIBAgIUKNl5fnws7cASsTTcKxmACp8L+mMwDQYJKoZIhvcNAQEL +BQAwDTELMAkGA1UEAwwCQ0EwHhcNMjQwNzE3MTYxODA1WhcNMjUwNzE3MTYxODA1 +WjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQC/DfDxcFkyH2tjS5hbEnDPF7cLFd03UsgiqPTgLwGdyYnNYURIGMxs +gl243FkycmYRz1OsEY1+zf71vi0XNulfdl83+1t+hZcEt1HGzXrO92smznHdDBL7 +1sqDWO00n1XSmWHo7J9qIWjuvKoBwE5lS25ghSzuZWP/P5WBvOa/2wUyfOdB33DV +8CiHCBjf1C2tp+sqw5kFKT0v7lSZTLnCMRH0VlzRc/1CHHrVn7VyvpN618QDJ4D2 +AjSxs+uGhlu7ppWYXdB34bVJT0ffT0KkPnOQ6pKJ8uLXxMpSfgATHERZfigXZ5yb +yTMx/Rwclil828icwowizmGs8AXrI7vk/g4t4JJ74yGIc0A+HKsQ2Gu193pmwQNH +pOctN7NMnF+Lvcqf7j3vIWCLVSJDXUom28VAr/egfr3K5xEfipaiePlAUV6PF90k +Ou3p/kHlNS9dkqx4tM1z6surq4znr3+oq4Ldr+1UNRnBLc/yT01IwdbD2NKPTSJI +yO9uybx0yU04zJC6ZhlZb+Z0kOtF1+T2jzbSjFKIlxoDBUlXonGVW4TXh5S1CUFH +FxyJfsPjocayOYikCb6chTj+9aiTaWJI8txLUO7ejvIINZRg7C1hgBobETePcbvZ +gRFQCbcQUh/6yRTVY+3aOPhf/DcKmb/eM9ijsQxsZaAYrOnu8xXkYQIDAQABo0sw +STAJBgNVHRMEAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAsGA1UdDwQEAwIFoDAa +BgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwDQYJKoZIhvcNAQELBQADggIBAJrs +4xelo+wV3kNoqztngdgAFnoNmxE7xN1c/XrdiIc/xY/xQz61lAUF5ouyy1ma/VB9 +a9/5exYWiXW8TpZM7CwccYk7ShDpoIZvUTj4YwNo2F9zlYRsgTjk1ekYz0PKsQGz +v8dF9xmUYn86bGITu+MlJ4konXdJ2riaV2Cx6LwErLXZ0mGxmHNQ/r49QsApEtFf +Y3P+2uYkmSV8UVvRTClyWoxVbojAyAXBv8K5/5/Yuq/NEdl36w6P+gVTXcK3eXy9 +2tktdHd1qAowQFAcdn5h+nmrnYMCdD3F3wu0CLDUFVunY6lQg5Gxsft3s95kbucT +qFrl1Fr1xOaU4pCLwMcUZ6sjURH1jAiSWMdtM1imRXuQDDFvD4DQAktg9UR5UTlM +0NFO1GXIMTA9y2MxQeZTUC7zEF7QRjXJ/9xW6VBK+1iFDyzYa+7iFb7Gsqeu5WHv +p5fiZe6JKN93XzzWcCWEg98IIS01yTGiygkyO2yWOeZ4xEWQuT7p4gi3e5oydBgC +kVsd6slWlJtDP7iGYOwJXucw6pvKMGYj7Ol4kxPosyIb9SV42cp52JAIgPLKTaHn +kmrwIfYV+Zn7xoX8FERg8/Oua0mOeeij9JREoYwm5XV52Fhv3p7qDSqe3HIEL6ft +SfHCEEAf2JEwQj9R+QWhPrWAeNPUqTeianr8Ks9e +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIE5DCCAsygAwIBAgIUZwOLM/yaHJBJ8XDvAxE/P3xDFqgwDQYJKoZIhvcNAQEL +BQAwDTELMAkGA1UEAwwCQ0EwHhcNMjQwNzE3MTYxNjAxWhcNMjUwNzE3MTYxNjAx +WjANMQswCQYDVQQDDAJDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +AKg69y4KkyMzE0WNPSlwHfF90dy+oqQ9OETVEeSkNx3ImDqx07tdv/z8QZRjFTSy +1OI7S3jSpcinTFlCCoa1mNcEe7Fut2u63YPENRj/lLZpPxo0S168A9yUjkN4c36u +zNyLirBxtqmuxp/4jJ4I0nyUN3eVpsdLztR7mMRm0vvaU5DjeevmtXvk4aiAwLov +uffnQhte9hGY/ZWfbG7EZ4+okMP7m6W6XWxaMShknzxWQONmCv8wkEA3yEUhvyzl +8abpj3SO5vaW6eHmr7sGXMDx6DHs3UCH9Bk3kBmylzttSRH3p4fBtYZle11dlC7N +Ks+QFijfTkN5kIHyuaUFieGKkzeHnc+ACJMUultTBdKwOCZMAk4IUkwG8G5LYozH +I2e5FRWlvUJS4WgF8Vy6dfCSjALlGES62JM0hc6hnqZmKR3A2xkxTM3vcZElq/w1 +Ibi6ezqbO5QmqBuoNGIgsphuF8cYtdla30FQumQpN2WuyWFkBUwkjMJDooWdQeJT +pAntBP+Gx+PjY5329uMf4Q0Q7JGBXO9LVeRGgav8sJDG4EadXzgtigP9nyjy1L3b +QHPkMoqPXYLSqSC2hxxbeiHPA2Kn10/Wx90MSbE81CS4LSoHVMSQ7B2pc3zHkcTn +PnQuQdDFzFKXeso6JCI66dPCBGnVHIhhfbjgplXUvlkNAgMBAAGjPDA6MAwGA1Ud +EwQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBS3FvhJKWuL/EX+hMJi6i37 +q9OZCjANBgkqhkiG9w0BAQsFAAOCAgEAU9sjWemPhkPPdDc4Ph280gvQKWfLhfx/ +pzWtKHmsMPgz4XglQRzj5yHFe5RW0dTUk6FGHgxaIXQaRoP1L2T8k2Z4GLjUNyXP +nUEoYgs4OpnZytg+rgHr3cu7qocM9lSi1ZoE4XomijJhe5okREU7tZvj1pHL1Zmn +TEUXIx8ktl4VDgDXiSS7QXQ9W2chs2gYyxxWeNUyHckURNzbCDnJlkuOBdsXV7eV +v7D60JHP2Pem7zrLhV/G8P7JaRVfY2ZTZB2lH7tKBEhYIpa5muG0JjjyoDkXyOfB +s6VX0xjt8ny+E7wklYJb3TQRJRkOFn08HvhV9Jycs2HvpjSsdHBjn+NbF1ea74TI +2brgW1GjR30+H5mJPsbr79Qriu0ibuwor/+u8UvPrEaeXpbUp+QJ0XAtzXNqrsV3 +yCYtIvDstOrxV7M3hYbZyoBiq92dgwVyMAydUMxMn9EAQBqfpen9Hh7pnET9ORNU +QcJumJDf4YUdAgB6qpTW54LhmhwQOm2zTGuMUdARYGmVNfZbd7HSW/JVJcYtFjus +obiozqvXQVuu1g52JmcpWAlxJLXSFABPifaCI7zy1+XZi4ppzSNKG4+N82QkM5xr +KyATGQnS67yAdYzjBz8bsA0vf72oVVOaWLxCmVpj25mf+G+HCYjnwtWlRgH64tBL +vQLbQZQgNHo= +-----END CERTIFICATE----- diff --git a/utils/docker/certs/server.key b/utils/docker/certs/server.key new file mode 100644 index 0000000..6427c71 --- /dev/null +++ b/utils/docker/certs/server.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +vLXrRz8CyZat8oyhEGQIsEHQaMxS6YQ= +-----END PRIVATE KEY----- diff --git a/utils/docker/certs/server_redis.key b/utils/docker/certs/server_redis.key new file mode 100644 index 0000000..6427c71 --- /dev/null +++ b/utils/docker/certs/server_redis.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +vLXrRz8CyZat8oyhEGQIsEHQaMxS6YQ= +-----END PRIVATE KEY----- diff --git a/utils/docker/docker-compose.yml b/utils/docker/docker-compose.yml index eda5b51..ad6183b 100644 --- a/utils/docker/docker-compose.yml +++ b/utils/docker/docker-compose.yml @@ -37,16 +37,32 @@ services: - "-c" - "max_connections=400" - "-c" - - "shared_buffers=20MB" + - "shared_buffers=20MB" + - "-c" + - "ssl=on" + - "-c" + - "ssl_cert_file=/var/lib/postgresql/certs/server.crt" + - "-c" + - "ssl_key_file=/var/lib/postgresql/certs/server.key" env_file: - postgresql.env restart: always volumes: - ./postgresql/init-db.sh:/docker-entrypoint-initdb.d/init-db.sh + - ./certs/:/var/lib/postgresql/certs/ redis: image: 'bitnami/redis:latest' ports: - "6379:6379" + volumes: + - ./certs:/usr/local/etc/certs environment: - ALLOW_EMPTY_PASSWORD=yes + - REDIS_PORT_NUMBER=0 + - REDIS_TLS_ENABLED=yes + - REDIS_TLS_PORT_NUMBER=6379 + - REDIS_TLS_CERT_FILE=/usr/local/etc/certs/server.crt + - REDIS_TLS_KEY_FILE=/usr/local/etc/certs/server_redis.key + - REDIS_TLS_CA_DIR=/usr/local/etc/certs + - REDIS_TLS_AUTH_CLIENTS=no