feat: add TLS support for mysql backend (#6979)

* refactor: move etcd tls code to `common-meta`

Signed-off-by: WenyXu <wenymedia@gmail.com>

* refactor: move postgre pool logic to `utils::postgre`

Signed-off-by: WenyXu <wenymedia@gmail.com>

* feat: setup mysql ssl options

Signed-off-by: WenyXu <wenymedia@gmail.com>

* feat: add test for mysql backend with tls

Signed-off-by: WenyXu <wenymedia@gmail.com>

* refactor: simplify certs generation

Signed-off-by: WenyXu <wenymedia@gmail.com>

* chore: apply suggestions

Signed-off-by: WenyXu <wenymedia@gmail.com>

---------

Signed-off-by: WenyXu <wenymedia@gmail.com>
This commit is contained in:
Weny Xu
2025-09-16 21:46:37 +08:00
committed by GitHub
parent 85c1a91bae
commit 0a959f9920
32 changed files with 749 additions and 580 deletions

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
CERT_DIR="${1:-$(dirname "$0")/../tests-integration/fixtures/pgsql-certs}"
CERT_DIR="${1:-$(dirname "$0")/../tests-integration/fixtures/certs}"
DAYS="${2:-365}"
mkdir -p "${CERT_DIR}"
@@ -10,13 +10,13 @@ cd "${CERT_DIR}"
echo "Generating CA certificate..."
openssl req -new -x509 -days "${DAYS}" -nodes -text \
-out root.crt -keyout root.key \
-subj "/CN=PostgresRootCA"
-subj "/CN=GreptimeDBRootCA"
echo "Generating server certificate..."
openssl req -new -nodes -text \
-out server.csr -keyout server.key \
-subj "/CN=postgres"
-subj "/CN=greptime"
openssl x509 -req -in server.csr -text -days "${DAYS}" \
-CA root.crt -CAkey root.key -CAcreateserial \
@@ -36,6 +36,6 @@ rm -f *.csr
echo "TLS certificates generated successfully in ${CERT_DIR}"
chmod 600 root.key
chmod 600 client.key
chmod 600 server.key
chmod 644 root.key
chmod 644 client.key
chmod 644 server.key

View File

@@ -19,8 +19,8 @@ use common_error::ext::BoxedError;
use common_meta::kv_backend::KvBackendRef;
use common_meta::kv_backend::chroot::ChrootKvBackend;
use common_meta::kv_backend::etcd::EtcdStore;
use meta_srv::bootstrap::create_etcd_client_with_tls;
use meta_srv::metasrv::BackendImpl;
use meta_srv::utils::etcd::create_etcd_client_with_tls;
use servers::tls::{TlsMode, TlsOption};
use crate::error::{EmptyStoreAddrsSnafu, UnsupportedMemoryBackendSnafu};
@@ -116,9 +116,13 @@ impl StoreConfig {
BackendImpl::PostgresStore => {
let table_name = &self.meta_table_name;
let tls_config = self.tls_config();
let pool = meta_srv::bootstrap::create_postgres_pool(store_addrs, tls_config)
.await
.map_err(BoxedError::new)?;
let pool = meta_srv::utils::postgres::create_postgres_pool(
store_addrs,
None,
tls_config,
)
.await
.map_err(BoxedError::new)?;
let schema_name = self.meta_schema_name.as_deref();
Ok(common_meta::kv_backend::rds::PgStore::with_pg_pool(
pool,
@@ -132,9 +136,11 @@ impl StoreConfig {
#[cfg(feature = "mysql_kvbackend")]
BackendImpl::MysqlStore => {
let table_name = &self.meta_table_name;
let pool = meta_srv::bootstrap::create_mysql_pool(store_addrs)
.await
.map_err(BoxedError::new)?;
let tls_config = self.tls_config();
let pool =
meta_srv::utils::mysql::create_mysql_pool(store_addrs, tls_config.as_ref())
.await
.map_err(BoxedError::new)?;
Ok(common_meta::kv_backend::rds::MySqlStore::with_mysql_pool(
pool,
table_name,

View File

@@ -752,7 +752,6 @@ pub enum Error {
location: Location,
},
#[cfg(feature = "pg_kvbackend")]
#[snafu(display("Failed to load TLS certificate from path: {}", path))]
LoadTlsCertificate {
path: String,
@@ -1181,13 +1180,14 @@ impl ErrorExt for Error {
| InvalidRole { .. }
| EmptyDdlTasks { .. } => StatusCode::InvalidArguments,
LoadTlsCertificate { .. } => StatusCode::Internal,
#[cfg(feature = "pg_kvbackend")]
PostgresExecution { .. }
| CreatePostgresPool { .. }
| GetPostgresConnection { .. }
| PostgresTransaction { .. }
| PostgresTlsConfig { .. }
| LoadTlsCertificate { .. }
| InvalidTlsConfig { .. } => StatusCode::Internal,
#[cfg(feature = "mysql_kvbackend")]
MySqlExecution { .. } | CreateMySqlPool { .. } | MySqlTransaction { .. } => {

View File

@@ -13,15 +13,17 @@
// limitations under the License.
use std::any::Any;
use std::fs;
use std::sync::Arc;
use common_telemetry::info;
use common_telemetry::{debug, info};
use etcd_client::{
Client, DeleteOptions, GetOptions, PutOptions, Txn, TxnOp, TxnOpResponse, TxnResponse,
Certificate, Client, DeleteOptions, GetOptions, Identity, PutOptions, TlsOptions, Txn, TxnOp,
TxnOpResponse, TxnResponse,
};
use snafu::{ResultExt, ensure};
use crate::error::{self, Error, Result};
use crate::error::{self, Error, LoadTlsCertificateSnafu, Result};
use crate::kv_backend::txn::{Txn as KvTxn, TxnResponse as KvTxnResponse};
use crate::kv_backend::{KvBackend, KvBackendRef, TxnService};
use crate::metrics::METRIC_META_TXN_REQUEST;
@@ -451,8 +453,76 @@ impl TryFrom<DeleteRangeRequest> for Delete {
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum TlsMode {
#[default]
Disable,
Require,
}
/// TLS configuration for Etcd connections.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TlsOption {
pub mode: TlsMode,
pub cert_path: String,
pub key_path: String,
pub ca_cert_path: String,
}
/// Creates a Etcd [`TlsOptions`] from a [`TlsOption`].
///
/// This function builds the TLS options for etcd client connections based on the provided
/// [`TlsOption`]. It supports disabling TLS, setting a custom CA certificate, and configuring
/// client identity for mutual TLS authentication.
///
/// Note: All TlsMode variants except [`TlsMode::Disable`] will be treated as enabling TLS.
pub fn create_etcd_tls_options(tls_config: &TlsOption) -> Result<Option<TlsOptions>> {
// If TLS mode is disabled, return None to indicate no TLS configuration.
if matches!(tls_config.mode, TlsMode::Disable) {
return Ok(None);
}
info!("Creating etcd TLS with mode: {:?}", tls_config.mode);
// Start with default TLS options.
let mut etcd_tls_opts = TlsOptions::new();
// If a CA certificate path is provided, load the CA certificate and add it to the options.
if !tls_config.ca_cert_path.is_empty() {
debug!("Using CA certificate from {}", tls_config.ca_cert_path);
let ca_cert_pem = fs::read(&tls_config.ca_cert_path).context(LoadTlsCertificateSnafu {
path: &tls_config.ca_cert_path,
})?;
let ca_cert = Certificate::from_pem(ca_cert_pem);
etcd_tls_opts = etcd_tls_opts.ca_certificate(ca_cert);
}
// If both client certificate and key paths are provided, load them and set the client identity.
if !tls_config.cert_path.is_empty() && !tls_config.key_path.is_empty() {
info!("Loading client certificate for mutual TLS");
debug!(
"Using client certificate from {} and key from {}",
tls_config.cert_path, tls_config.key_path
);
let cert_pem = fs::read(&tls_config.cert_path).context(LoadTlsCertificateSnafu {
path: &tls_config.cert_path,
})?;
let key_pem = fs::read(&tls_config.key_path).context(LoadTlsCertificateSnafu {
path: &tls_config.key_path,
})?;
let identity = Identity::from_pem(cert_pem, key_pem);
etcd_tls_opts = etcd_tls_opts.identity(identity);
}
// Always enable native TLS roots for additional trust anchors.
etcd_tls_opts = etcd_tls_opts.with_native_roots();
Ok(Some(etcd_tls_opts))
}
#[cfg(test)]
mod tests {
use etcd_client::ConnectOptions;
use super::*;
#[test]
@@ -555,6 +625,8 @@ mod tests {
test_txn_compare_not_equal, test_txn_one_compare_op, text_txn_multi_compare_op,
unprepare_kv,
};
use crate::maybe_skip_etcd_tls_integration_test;
use crate::test_util::etcd_certs_dir;
async fn build_kv_backend() -> Option<EtcdStore> {
let endpoints = std::env::var("GT_ETCD_ENDPOINTS").unwrap_or_default();
@@ -654,4 +726,41 @@ mod tests {
test_txn_compare_not_equal(&kv_backend).await;
}
}
async fn create_etcd_client_with_tls(endpoints: &[String], tls_config: &TlsOption) -> Client {
let endpoints = endpoints
.iter()
.map(|s| s.trim())
.filter(|s| !s.is_empty())
.collect::<Vec<_>>();
let connect_options =
ConnectOptions::new().with_tls(create_etcd_tls_options(tls_config).unwrap().unwrap());
Client::connect(&endpoints, Some(connect_options))
.await
.unwrap()
}
#[tokio::test]
async fn test_create_etcd_client_with_mtls_and_ca() {
maybe_skip_etcd_tls_integration_test!();
let endpoints = std::env::var("GT_ETCD_TLS_ENDPOINTS")
.unwrap()
.split(',')
.map(|s| s.to_string())
.collect::<Vec<_>>();
let cert_dir = etcd_certs_dir();
let tls_config = TlsOption {
mode: TlsMode::Require,
ca_cert_path: cert_dir.join("ca.crt").to_string_lossy().to_string(),
cert_path: cert_dir.join("client.crt").to_string_lossy().to_string(),
key_path: cert_dir
.join("client-key.pem")
.to_string_lossy()
.to_string(),
};
let mut client = create_etcd_client_with_tls(&endpoints, &tls_config).await;
let _ = client.get(b"hello", None).await.unwrap();
}
}

View File

@@ -573,6 +573,7 @@ impl MySqlStore {
#[cfg(test)]
mod tests {
use common_telemetry::init_default_ut_logging;
use sqlx::mysql::{MySqlConnectOptions, MySqlSslMode};
use super::*;
use crate::kv_backend::test::{
@@ -584,6 +585,7 @@ mod tests {
text_txn_multi_compare_op, unprepare_kv,
};
use crate::maybe_skip_mysql_integration_test;
use crate::test_util::test_certs_dir;
async fn build_mysql_kv_backend(table_name: &str) -> Option<MySqlStore> {
init_default_ut_logging();
@@ -711,4 +713,71 @@ mod tests {
test_txn_compare_less(&kv_backend).await;
test_txn_compare_not_equal(&kv_backend).await;
}
#[tokio::test]
async fn test_mysql_with_tls() {
common_telemetry::init_default_ut_logging();
maybe_skip_mysql_integration_test!();
let endpoint = std::env::var("GT_MYSQL_ENDPOINTS").unwrap();
let opts = endpoint
.parse::<MySqlConnectOptions>()
.unwrap()
.ssl_mode(MySqlSslMode::Required);
let pool = MySqlPool::connect_with(opts).await.unwrap();
sqlx::query("SELECT 1").execute(&pool).await.unwrap();
}
#[tokio::test]
async fn test_mysql_with_mtls() {
common_telemetry::init_default_ut_logging();
maybe_skip_mysql_integration_test!();
let endpoint = std::env::var("GT_MYSQL_ENDPOINTS").unwrap();
let certs_dir = test_certs_dir();
let opts = endpoint
.parse::<MySqlConnectOptions>()
.unwrap()
.ssl_mode(MySqlSslMode::Required)
.ssl_client_cert(certs_dir.join("client.crt").to_string_lossy().to_string())
.ssl_client_key(certs_dir.join("client.key").to_string_lossy().to_string());
let pool = MySqlPool::connect_with(opts).await.unwrap();
sqlx::query("SELECT 1").execute(&pool).await.unwrap();
}
#[tokio::test]
async fn test_mysql_with_tls_verify_ca() {
common_telemetry::init_default_ut_logging();
maybe_skip_mysql_integration_test!();
let endpoint = std::env::var("GT_MYSQL_ENDPOINTS").unwrap();
let certs_dir = test_certs_dir();
let opts = endpoint
.parse::<MySqlConnectOptions>()
.unwrap()
.ssl_mode(MySqlSslMode::VerifyCa)
.ssl_ca(certs_dir.join("root.crt").to_string_lossy().to_string())
.ssl_client_cert(certs_dir.join("client.crt").to_string_lossy().to_string())
.ssl_client_key(certs_dir.join("client.key").to_string_lossy().to_string());
let pool = MySqlPool::connect_with(opts).await.unwrap();
sqlx::query("SELECT 1").execute(&pool).await.unwrap();
}
#[tokio::test]
async fn test_mysql_with_tls_verify_ident() {
common_telemetry::init_default_ut_logging();
maybe_skip_mysql_integration_test!();
let endpoint = std::env::var("GT_MYSQL_ENDPOINTS").unwrap();
let certs_dir = test_certs_dir();
let opts = endpoint
.parse::<MySqlConnectOptions>()
.unwrap()
.ssl_mode(MySqlSslMode::VerifyIdentity)
.ssl_ca(certs_dir.join("root.crt").to_string_lossy().to_string())
.ssl_client_cert(certs_dir.join("client.crt").to_string_lossy().to_string())
.ssl_client_key(certs_dir.join("client.key").to_string_lossy().to_string());
let pool = MySqlPool::connect_with(opts).await.unwrap();
sqlx::query("SELECT 1").execute(&pool).await.unwrap();
}
}

View File

@@ -903,7 +903,7 @@ mod tests {
test_txn_compare_less, test_txn_compare_not_equal, test_txn_one_compare_op,
text_txn_multi_compare_op, unprepare_kv,
};
use crate::test_util::pgsql_certs_dir;
use crate::test_util::test_certs_dir;
use crate::{maybe_skip_postgres_integration_test, maybe_skip_postgres15_integration_test};
async fn build_pg_kv_backend(table_name: &str) -> Option<PgStore> {
@@ -1020,7 +1020,7 @@ mod tests {
async fn test_pg_with_mtls() {
common_telemetry::init_default_ut_logging();
maybe_skip_postgres_integration_test!();
let certs_dir = pgsql_certs_dir();
let certs_dir = test_certs_dir();
let endpoints = std::env::var("GT_POSTGRES_ENDPOINTS").unwrap();
let tls_connector = create_postgres_tls_connector(&TlsOption {
mode: TlsMode::Require,
@@ -1043,7 +1043,7 @@ mod tests {
async fn test_pg_verify_ca() {
common_telemetry::init_default_ut_logging();
maybe_skip_postgres_integration_test!();
let certs_dir = pgsql_certs_dir();
let certs_dir = test_certs_dir();
let endpoints = std::env::var("GT_POSTGRES_ENDPOINTS").unwrap();
let tls_connector = create_postgres_tls_connector(&TlsOption {
mode: TlsMode::VerifyCa,
@@ -1066,7 +1066,7 @@ mod tests {
async fn test_pg_verify_full() {
common_telemetry::init_default_ut_logging();
maybe_skip_postgres_integration_test!();
let certs_dir = pgsql_certs_dir();
let certs_dir = test_certs_dir();
let endpoints = std::env::var("GT_POSTGRES_ENDPOINTS").unwrap();
let tls_connector = create_postgres_tls_connector(&TlsOption {
mode: TlsMode::VerifyFull,

View File

@@ -301,6 +301,22 @@ macro_rules! maybe_skip_postgres15_integration_test {
};
}
#[macro_export]
/// Skip the test if the environment variable `GT_ETCD_TLS_ENDPOINTS` is not set.
///
/// The format of the environment variable is:
/// ```text
/// GT_ETCD_TLS_ENDPOINTS=localhost:9092,localhost:9093
/// ```
macro_rules! maybe_skip_etcd_tls_integration_test {
() => {
if std::env::var("GT_ETCD_TLS_ENDPOINTS").is_err() {
common_telemetry::warn!("The etcd with tls endpoints is empty, skipping the test");
return;
}
};
}
/// Returns the directory of the etcd TLS certs.
pub fn etcd_certs_dir() -> PathBuf {
let project_path = env!("CARGO_MANIFEST_DIR");
@@ -311,12 +327,12 @@ pub fn etcd_certs_dir() -> PathBuf {
.join("etcd-tls-certs")
}
/// Returns the directory of the pgsql TLS certs.
pub fn pgsql_certs_dir() -> PathBuf {
/// Returns the directory of the test certs.
pub fn test_certs_dir() -> PathBuf {
let project_path = env!("CARGO_MANIFEST_DIR");
let project_path = PathBuf::from(project_path);
let base = project_path.ancestors().nth(3).unwrap();
base.join("tests-integration")
.join("fixtures")
.join("pgsql-certs")
.join("certs")
}

View File

@@ -21,45 +21,23 @@ use api::v1::meta::procedure_service_server::ProcedureServiceServer;
use api::v1::meta::store_server::StoreServer;
use common_base::Plugins;
use common_config::Configurable;
#[cfg(feature = "pg_kvbackend")]
use common_error::ext::BoxedError;
#[cfg(any(feature = "pg_kvbackend", feature = "mysql_kvbackend"))]
use common_meta::distributed_time_constants::META_LEASE_SECS;
use common_meta::kv_backend::chroot::ChrootKvBackend;
use common_meta::kv_backend::etcd::EtcdStore;
use common_meta::kv_backend::memory::MemoryKvBackend;
#[cfg(feature = "mysql_kvbackend")]
use common_meta::kv_backend::rds::MySqlStore;
#[cfg(feature = "pg_kvbackend")]
use common_meta::kv_backend::rds::PgStore;
#[cfg(feature = "pg_kvbackend")]
use common_meta::kv_backend::rds::postgres::create_postgres_tls_connector;
#[cfg(feature = "pg_kvbackend")]
use common_meta::kv_backend::rds::postgres::{TlsMode as PgTlsMode, TlsOption as PgTlsOption};
use common_meta::kv_backend::{KvBackendRef, ResettableKvBackendRef};
use common_telemetry::info;
#[cfg(feature = "pg_kvbackend")]
use deadpool_postgres::{Config, Runtime};
use either::Either;
use etcd_client::{Client, ConnectOptions};
use servers::configurator::ConfiguratorRef;
use servers::export_metrics::ExportMetricsTask;
use servers::http::{HttpServer, HttpServerBuilder};
use servers::metrics_handler::MetricsHandler;
use servers::server::Server;
use servers::tls::TlsOption;
#[cfg(any(feature = "pg_kvbackend", feature = "mysql_kvbackend"))]
use snafu::OptionExt;
use snafu::ResultExt;
#[cfg(feature = "mysql_kvbackend")]
use sqlx::mysql::MySqlConnectOptions;
#[cfg(feature = "mysql_kvbackend")]
use sqlx::mysql::MySqlPool;
use tokio::net::TcpListener;
use tokio::sync::mpsc::{self, Receiver, Sender};
use tokio::sync::{Mutex, oneshot};
#[cfg(feature = "pg_kvbackend")]
use tokio_postgres::NoTls;
use tonic::codec::CompressionEncoding;
use tonic::transport::server::{Router, TcpIncoming};
@@ -67,10 +45,6 @@ use crate::cluster::{MetaPeerClientBuilder, MetaPeerClientRef};
#[cfg(any(feature = "pg_kvbackend", feature = "mysql_kvbackend"))]
use crate::election::CANDIDATE_LEASE_SECS;
use crate::election::etcd::EtcdElection;
#[cfg(feature = "mysql_kvbackend")]
use crate::election::rds::mysql::MySqlElection;
#[cfg(feature = "pg_kvbackend")]
use crate::election::rds::postgres::PgElection;
use crate::metasrv::builder::MetasrvBuilder;
use crate::metasrv::{
BackendImpl, ElectionRef, Metasrv, MetasrvOptions, SelectTarget, SelectorRef,
@@ -82,6 +56,7 @@ use crate::selector::round_robin::RoundRobinSelector;
use crate::selector::weight_compute::RegionNumsBasedWeightCompute;
use crate::service::admin;
use crate::service::admin::admin_axum_router;
use crate::utils::etcd::create_etcd_client_with_tls;
use crate::{Result, error};
pub struct MetasrvInstance {
@@ -306,8 +281,11 @@ pub async fn metasrv_builder(
use std::time::Duration;
use common_meta::distributed_time_constants::POSTGRES_KEEP_ALIVE_SECS;
use common_meta::kv_backend::rds::PgStore;
use deadpool_postgres::Config;
use crate::election::rds::postgres::ElectionPgClient;
use crate::election::rds::postgres::{ElectionPgClient, PgElection};
use crate::utils::postgres::create_postgres_pool;
let candidate_lease_ttl = Duration::from_secs(CANDIDATE_LEASE_SECS);
let execution_timeout = Duration::from_secs(META_LEASE_SECS);
@@ -319,8 +297,8 @@ pub async fn metasrv_builder(
cfg.keepalives = Some(true);
cfg.keepalives_idle = Some(Duration::from_secs(POSTGRES_KEEP_ALIVE_SECS));
// We use a separate pool for election since we need a different session keep-alive idle time.
let pool =
create_postgres_pool_with(&opts.store_addrs, cfg, opts.backend_tls.clone()).await?;
let pool = create_postgres_pool(&opts.store_addrs, Some(cfg), opts.backend_tls.clone())
.await?;
let election_client = ElectionPgClient::new(
pool,
@@ -340,7 +318,8 @@ pub async fn metasrv_builder(
)
.await?;
let pool = create_postgres_pool(&opts.store_addrs, opts.backend_tls.clone()).await?;
let pool =
create_postgres_pool(&opts.store_addrs, None, opts.backend_tls.clone()).await?;
let kv_backend = PgStore::with_pg_pool(
pool,
opts.meta_schema_name.as_deref(),
@@ -356,9 +335,12 @@ pub async fn metasrv_builder(
(None, BackendImpl::MysqlStore) => {
use std::time::Duration;
use crate::election::rds::mysql::ElectionMysqlClient;
use common_meta::kv_backend::rds::MySqlStore;
let pool = create_mysql_pool(&opts.store_addrs).await?;
use crate::election::rds::mysql::{ElectionMysqlClient, MySqlElection};
use crate::utils::mysql::create_mysql_pool;
let pool = create_mysql_pool(&opts.store_addrs, opts.backend_tls.as_ref()).await?;
let kv_backend =
MySqlStore::with_mysql_pool(pool, &opts.meta_table_name, opts.max_txn_ops)
.await
@@ -366,7 +348,7 @@ pub async fn metasrv_builder(
// Since election will acquire a lock of the table, we need a separate table for election.
let election_table_name = opts.meta_table_name.clone() + "_election";
// We use a separate pool for election since we need a different session keep-alive idle time.
let pool = create_mysql_pool(&opts.store_addrs).await?;
let pool = create_mysql_pool(&opts.store_addrs, opts.backend_tls.as_ref()).await?;
let execution_timeout = Duration::from_secs(META_LEASE_SECS);
let statement_timeout = Duration::from_secs(META_LEASE_SECS);
let idle_session_timeout = Duration::from_secs(META_LEASE_SECS);
@@ -452,260 +434,3 @@ pub(crate) fn build_default_meta_peer_client(
// Safety: all required fields set at initialization
.unwrap()
}
pub async fn create_etcd_client(store_addrs: &[String]) -> Result<Client> {
create_etcd_client_with_tls(store_addrs, None).await
}
fn build_connection_options(tls_config: Option<&TlsOption>) -> Result<Option<ConnectOptions>> {
use std::fs;
use common_telemetry::debug;
use etcd_client::{Certificate, ConnectOptions, Identity, TlsOptions};
use servers::tls::TlsMode;
// If TLS options are not provided, return None
let Some(tls_config) = tls_config else {
return Ok(None);
};
// If TLS is disabled, return None
if matches!(tls_config.mode, TlsMode::Disable) {
return Ok(None);
}
info!("Creating etcd client with TLS mode: {:?}", tls_config.mode);
let mut etcd_tls_opts = TlsOptions::new();
// Set CA certificate if provided
if !tls_config.ca_cert_path.is_empty() {
debug!("Using CA certificate from {}", tls_config.ca_cert_path);
let ca_cert_pem = fs::read(&tls_config.ca_cert_path).context(error::FileIoSnafu {
path: &tls_config.ca_cert_path,
})?;
let ca_cert = Certificate::from_pem(ca_cert_pem);
etcd_tls_opts = etcd_tls_opts.ca_certificate(ca_cert);
}
// Set client identity (cert + key) if both are provided
if !tls_config.cert_path.is_empty() && !tls_config.key_path.is_empty() {
debug!(
"Using client certificate from {} and key from {}",
tls_config.cert_path, tls_config.key_path
);
let cert_pem = fs::read(&tls_config.cert_path).context(error::FileIoSnafu {
path: &tls_config.cert_path,
})?;
let key_pem = fs::read(&tls_config.key_path).context(error::FileIoSnafu {
path: &tls_config.key_path,
})?;
let identity = Identity::from_pem(cert_pem, key_pem);
etcd_tls_opts = etcd_tls_opts.identity(identity);
}
// Enable native TLS roots for additional trust anchors
etcd_tls_opts = etcd_tls_opts.with_native_roots();
Ok(Some(ConnectOptions::new().with_tls(etcd_tls_opts)))
}
pub async fn create_etcd_client_with_tls(
store_addrs: &[String],
tls_config: Option<&TlsOption>,
) -> Result<Client> {
let etcd_endpoints = store_addrs
.iter()
.map(|x| x.trim())
.filter(|x| !x.is_empty())
.collect::<Vec<_>>();
let connect_options = build_connection_options(tls_config)?;
Client::connect(&etcd_endpoints, connect_options)
.await
.context(error::ConnectEtcdSnafu)
}
#[cfg(feature = "pg_kvbackend")]
/// Converts servers::tls::TlsOption to postgres::TlsOption to avoid circular dependencies
fn convert_tls_option(tls_option: &TlsOption) -> PgTlsOption {
let mode = match tls_option.mode {
servers::tls::TlsMode::Disable => PgTlsMode::Disable,
servers::tls::TlsMode::Prefer => PgTlsMode::Prefer,
servers::tls::TlsMode::Require => PgTlsMode::Require,
servers::tls::TlsMode::VerifyCa => PgTlsMode::VerifyCa,
servers::tls::TlsMode::VerifyFull => PgTlsMode::VerifyFull,
};
PgTlsOption {
mode,
cert_path: tls_option.cert_path.clone(),
key_path: tls_option.key_path.clone(),
ca_cert_path: tls_option.ca_cert_path.clone(),
watch: tls_option.watch,
}
}
#[cfg(feature = "pg_kvbackend")]
/// Creates a pool for the Postgres backend with optional TLS.
///
/// It only use first store addr to create a pool.
pub async fn create_postgres_pool(
store_addrs: &[String],
tls_config: Option<TlsOption>,
) -> Result<deadpool_postgres::Pool> {
create_postgres_pool_with(store_addrs, Config::new(), tls_config).await
}
#[cfg(feature = "pg_kvbackend")]
/// Creates a pool for the Postgres backend with config and optional TLS.
///
/// It only use first store addr to create a pool, and use the given config to create a pool.
pub async fn create_postgres_pool_with(
store_addrs: &[String],
mut cfg: Config,
tls_config: Option<TlsOption>,
) -> Result<deadpool_postgres::Pool> {
let postgres_url = store_addrs.first().context(error::InvalidArgumentsSnafu {
err_msg: "empty store addrs",
})?;
cfg.url = Some(postgres_url.to_string());
let pool = if let Some(tls_config) = tls_config {
let pg_tls_config = convert_tls_option(&tls_config);
let tls_connector =
create_postgres_tls_connector(&pg_tls_config).map_err(|e| error::Error::Other {
source: BoxedError::new(e),
location: snafu::Location::new(file!(), line!(), 0),
})?;
cfg.create_pool(Some(Runtime::Tokio1), tls_connector)
.context(error::CreatePostgresPoolSnafu)?
} else {
cfg.create_pool(Some(Runtime::Tokio1), NoTls)
.context(error::CreatePostgresPoolSnafu)?
};
Ok(pool)
}
#[cfg(feature = "mysql_kvbackend")]
async fn setup_mysql_options(store_addrs: &[String]) -> Result<MySqlConnectOptions> {
let mysql_url = store_addrs.first().context(error::InvalidArgumentsSnafu {
err_msg: "empty store addrs",
})?;
// Avoid `SET` commands in sqlx
let opts: MySqlConnectOptions = mysql_url
.parse()
.context(error::ParseMySqlUrlSnafu { mysql_url })?;
let opts = opts
.no_engine_substitution(false)
.pipes_as_concat(false)
.timezone(None)
.set_names(false);
Ok(opts)
}
#[cfg(feature = "mysql_kvbackend")]
pub async fn create_mysql_pool(store_addrs: &[String]) -> Result<MySqlPool> {
let opts = setup_mysql_options(store_addrs).await?;
let pool = MySqlPool::connect_with(opts)
.await
.context(error::CreateMySqlPoolSnafu)?;
Ok(pool)
}
#[cfg(test)]
mod tests {
use servers::tls::TlsMode;
use super::*;
#[tokio::test]
async fn test_create_etcd_client_tls_without_certs() {
let endpoints: Vec<String> = match std::env::var("GT_ETCD_TLS_ENDPOINTS") {
Ok(endpoints_str) => endpoints_str
.split(',')
.map(|s| s.trim().to_string())
.collect(),
Err(_) => return,
};
let tls_config = TlsOption {
mode: TlsMode::Require,
ca_cert_path: String::new(),
cert_path: String::new(),
key_path: String::new(),
watch: false,
};
let _client = create_etcd_client_with_tls(&endpoints, Some(&tls_config))
.await
.unwrap();
}
#[tokio::test]
async fn test_create_etcd_client_tls_with_client_certs() {
let endpoints: Vec<String> = match std::env::var("GT_ETCD_TLS_ENDPOINTS") {
Ok(endpoints_str) => endpoints_str
.split(',')
.map(|s| s.trim().to_string())
.collect(),
Err(_) => return,
};
let cert_dir = std::env::current_dir()
.unwrap()
.join("tests-integration")
.join("fixtures")
.join("etcd-tls-certs");
if cert_dir.join("client.crt").exists() && cert_dir.join("client-key.pem").exists() {
let tls_config = TlsOption {
mode: TlsMode::Require,
ca_cert_path: String::new(),
cert_path: cert_dir.join("client.crt").to_string_lossy().to_string(),
key_path: cert_dir
.join("client-key.pem")
.to_string_lossy()
.to_string(),
watch: false,
};
let _client = create_etcd_client_with_tls(&endpoints, Some(&tls_config))
.await
.unwrap();
}
}
#[tokio::test]
async fn test_create_etcd_client_tls_with_full_certs() {
let endpoints: Vec<String> = match std::env::var("GT_ETCD_TLS_ENDPOINTS") {
Ok(endpoints_str) => endpoints_str
.split(',')
.map(|s| s.trim().to_string())
.collect(),
Err(_) => return,
};
let cert_dir = std::env::current_dir()
.unwrap()
.join("tests-integration")
.join("fixtures")
.join("etcd-tls-certs");
if cert_dir.join("ca.crt").exists()
&& cert_dir.join("client.crt").exists()
&& cert_dir.join("client-key.pem").exists()
{
let tls_config = TlsOption {
mode: TlsMode::Require,
ca_cert_path: cert_dir.join("ca.crt").to_string_lossy().to_string(),
cert_path: cert_dir.join("client.crt").to_string_lossy().to_string(),
key_path: cert_dir
.join("client-key.pem")
.to_string_lossy()
.to_string(),
watch: false,
};
let _client = create_etcd_client_with_tls(&endpoints, Some(&tls_config))
.await
.unwrap();
}
}
}

View File

@@ -984,8 +984,8 @@ mod tests {
use common_telemetry::init_default_ut_logging;
use super::*;
use crate::bootstrap::create_mysql_pool;
use crate::error;
use crate::utils::mysql::create_mysql_pool;
async fn create_mysql_client(
table_name: Option<&str>,
@@ -1000,7 +1000,7 @@ mod tests {
}
.fail();
}
let pool = create_mysql_pool(&[endpoint]).await.unwrap();
let pool = create_mysql_pool(&[endpoint], None).await.unwrap();
let mut client = ElectionMysqlClient::new(
pool,
execution_timeout,

View File

@@ -826,8 +826,8 @@ mod tests {
use common_meta::maybe_skip_postgres_integration_test;
use super::*;
use crate::bootstrap::create_postgres_pool;
use crate::error;
use crate::utils::postgres::create_postgres_pool;
async fn create_postgres_client(
table_name: Option<&str>,
@@ -842,7 +842,7 @@ mod tests {
}
.fail();
}
let pool = create_postgres_pool(&[endpoint], None).await.unwrap();
let pool = create_postgres_pool(&[endpoint], None, None).await.unwrap();
let mut pg_client = ElectionPgClient::new(
pool,
execution_timeout,

View File

@@ -981,6 +981,14 @@ pub enum Error {
#[snafu(source)]
source: common_meta::error::Error,
},
#[snafu(display("Failed to build tls options"))]
BuildTlsOptions {
#[snafu(implicit)]
location: Location,
#[snafu(source)]
source: common_meta::error::Error,
},
}
impl Error {
@@ -1116,6 +1124,7 @@ impl ErrorExt for Error {
| Error::InitDdlManager { source, .. }
| Error::InitReconciliationManager { source, .. } => source.status_code(),
Error::BuildTlsOptions { source, .. } => source.status_code(),
Error::Other { source, .. } => source.status_code(),
Error::NoEnoughAvailableNode { .. } => StatusCode::RuntimeResourcesExhausted,

View File

@@ -12,7 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
pub mod etcd;
pub mod insert_forwarder;
#[cfg(feature = "mysql_kvbackend")]
pub mod mysql;
#[cfg(feature = "pg_kvbackend")]
pub mod postgres;
#[macro_export]
macro_rules! define_ticker {

View File

@@ -0,0 +1,56 @@
// Copyright 2023 Greptime Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use common_meta::kv_backend::etcd::create_etcd_tls_options;
use etcd_client::{Client, ConnectOptions};
use servers::tls::{TlsMode, TlsOption};
use snafu::ResultExt;
use crate::error::{self, BuildTlsOptionsSnafu, Result};
/// Creates an etcd client with TLS configuration.
pub async fn create_etcd_client_with_tls(
store_addrs: &[String],
tls_config: Option<&TlsOption>,
) -> Result<Client> {
let etcd_endpoints = store_addrs
.iter()
.map(|x| x.trim())
.filter(|x| !x.is_empty())
.collect::<Vec<_>>();
let connect_options = tls_config
.map(|c| create_etcd_tls_options(&convert_tls_option(c)))
.transpose()
.context(BuildTlsOptionsSnafu)?
.flatten()
.map(|tls_options| ConnectOptions::new().with_tls(tls_options));
Client::connect(&etcd_endpoints, connect_options)
.await
.context(error::ConnectEtcdSnafu)
}
fn convert_tls_option(tls_option: &TlsOption) -> common_meta::kv_backend::etcd::TlsOption {
let mode = match tls_option.mode {
TlsMode::Disable => common_meta::kv_backend::etcd::TlsMode::Disable,
_ => common_meta::kv_backend::etcd::TlsMode::Require,
};
common_meta::kv_backend::etcd::TlsOption {
mode,
cert_path: tls_option.cert_path.clone(),
key_path: tls_option.key_path.clone(),
ca_cert_path: tls_option.ca_cert_path.clone(),
}
}

View File

@@ -0,0 +1,85 @@
// Copyright 2023 Greptime Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use common_telemetry::info;
use servers::tls::{TlsMode, TlsOption};
use snafu::{OptionExt, ResultExt};
use sqlx::mysql::{MySqlConnectOptions, MySqlPool, MySqlSslMode};
use crate::error::{self, Result};
async fn setup_mysql_options(
store_addrs: &[String],
tls_config: Option<&TlsOption>,
) -> Result<MySqlConnectOptions> {
let mysql_url = store_addrs.first().context(error::InvalidArgumentsSnafu {
err_msg: "empty store addrs",
})?;
// Avoid `SET` commands in sqlx
let opts: MySqlConnectOptions = mysql_url
.parse()
.context(error::ParseMySqlUrlSnafu { mysql_url })?;
let mut opts = opts
.no_engine_substitution(false)
.pipes_as_concat(false)
.timezone(None)
.set_names(false);
let Some(tls_config) = tls_config else {
return Ok(opts);
};
match tls_config.mode {
TlsMode::Disable => return Ok(opts),
TlsMode::Prefer => {
opts = opts.ssl_mode(MySqlSslMode::Preferred);
}
TlsMode::Require => {
opts = opts.ssl_mode(MySqlSslMode::Required);
}
TlsMode::VerifyCa => {
opts = opts.ssl_mode(MySqlSslMode::VerifyCa);
opts = opts.ssl_ca(&tls_config.ca_cert_path);
}
TlsMode::VerifyFull => {
opts = opts.ssl_mode(MySqlSslMode::VerifyIdentity);
opts = opts.ssl_ca(&tls_config.ca_cert_path);
}
}
info!(
"Setting up MySQL options with TLS mode: {:?}",
tls_config.mode
);
if !tls_config.cert_path.is_empty() && !tls_config.key_path.is_empty() {
info!("Loading client certificate for mutual TLS");
opts = opts.ssl_client_cert(&tls_config.cert_path);
opts = opts.ssl_client_key(&tls_config.key_path);
}
Ok(opts)
}
/// Creates a MySQL pool.
pub async fn create_mysql_pool(
store_addrs: &[String],
tls_config: Option<&TlsOption>,
) -> Result<MySqlPool> {
let opts = setup_mysql_options(store_addrs, tls_config).await?;
let pool = MySqlPool::connect_with(opts)
.await
.context(error::CreateMySqlPoolSnafu)?;
Ok(pool)
}

View File

@@ -0,0 +1,74 @@
// Copyright 2023 Greptime Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use common_error::ext::BoxedError;
use common_meta::kv_backend::rds::postgres::{
TlsMode as PgTlsMode, TlsOption as PgTlsOption, create_postgres_tls_connector,
};
use deadpool_postgres::{Config, Runtime};
use servers::tls::TlsOption;
use snafu::{OptionExt, ResultExt};
use tokio_postgres::NoTls;
use crate::error::{self, Result};
/// Converts [`TlsOption`] to [`PgTlsOption`] to avoid circular dependencies
fn convert_tls_option(tls_option: &TlsOption) -> PgTlsOption {
let mode = match tls_option.mode {
servers::tls::TlsMode::Disable => PgTlsMode::Disable,
servers::tls::TlsMode::Prefer => PgTlsMode::Prefer,
servers::tls::TlsMode::Require => PgTlsMode::Require,
servers::tls::TlsMode::VerifyCa => PgTlsMode::VerifyCa,
servers::tls::TlsMode::VerifyFull => PgTlsMode::VerifyFull,
};
PgTlsOption {
mode,
cert_path: tls_option.cert_path.clone(),
key_path: tls_option.key_path.clone(),
ca_cert_path: tls_option.ca_cert_path.clone(),
watch: tls_option.watch,
}
}
/// Creates a pool for the Postgres backend with config and optional TLS.
///
/// It only use first store addr to create a pool, and use the given config to create a pool.
pub async fn create_postgres_pool(
store_addrs: &[String],
cfg: Option<Config>,
tls_config: Option<TlsOption>,
) -> Result<deadpool_postgres::Pool> {
let mut cfg = cfg.unwrap_or_default();
let postgres_url = store_addrs.first().context(error::InvalidArgumentsSnafu {
err_msg: "empty store addrs",
})?;
cfg.url = Some(postgres_url.to_string());
let pool = if let Some(tls_config) = tls_config {
let pg_tls_config = convert_tls_option(&tls_config);
let tls_connector =
create_postgres_tls_connector(&pg_tls_config).map_err(|e| error::Error::Other {
source: BoxedError::new(e),
location: snafu::Location::new(file!(), line!(), 0),
})?;
cfg.create_pool(Some(Runtime::Tokio1), tls_connector)
.context(error::CreatePostgresPoolSnafu)?
} else {
cfg.create_pool(Some(Runtime::Tokio1), NoTls)
.context(error::CreatePostgresPoolSnafu)?
};
Ok(pool)
}

View File

@@ -86,7 +86,7 @@ The server certificate includes SANs for `localhost`, `etcd-tls`, `127.0.0.1`, a
### Regenerating Certificates (Optional)
If you need to regenerate the certificates:
If you need to regenerate the etcd certificates:
```bash
# Regenerate certificates (overwrites existing ones)
./scripts/generate-etcd-tls-certs.sh
@@ -95,4 +95,14 @@ If you need to regenerate the certificates:
./scripts/generate-etcd-tls-certs.sh /path/to/cert/directory
```
If you need to regenerate the mysql and postgres certificates:
```bash
# Regenerate certificates (overwrites existing ones)
./scripts/generate_certs.sh
# Or generate in custom location
./scripts/generate_certs.sh /path/to/cert/directory
```
**Note**: The checked-in certificates are for testing purposes only and should never be used in production.

View File

@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDFjCCAf6gAwIBAgIUIakjuzbmQLPnVJakOjLtjzjMEI8wDQYJKoZIhvcNAQEL
BQAwGzEZMBcGA1UEAwwQR3JlcHRpbWVEQlJvb3RDQTAeFw0yNTA5MTYxMTM3NTZa
Fw0yNjA5MTYxMTM3NTZaMBUxEzARBgNVBAMMCmdyZXB0aW1lZGIwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQD3WM82qlcPut5bAqFNf3bKSJCeVWaDsVqy
YqdERyyqtXzz9lsOlLlnsOuqiC8uJna9xypbhiFlNGtKmOKnJ34uCzSoXlZy8Vdp
sjlkRxKEWt971IT0KDnC8+du2Y1XvnyNol29wRvq7L4iBtlvL3PObuYIqFPGo2C6
AOiqRCqgekSnd0YjP1EHAIn7ltW5KZIxsZ1beIIvpUaAvKwLroozydy66Dw2gMl0
OuqDDWYVAs+5njReBC1pG8eIuYP6BTHnwQ8g7kw4mle+w77V+gqo0VIqgbivynyn
V0SP0naRAJ31pVhNpg9KjxkmCrHxkfbH47Egvn0I2a8Qlh5rW3AVAgMBAAGjWDBW
MBQGA1UdEQQNMAuCCWxvY2FsaG9zdDAdBgNVHQ4EFgQUjC3lp5jhuLoLJySTIg67
otB0xuAwHwYDVR0jBBgwFoAUcjFqs35tU0JvDB97G0nCHRxZ8rkwDQYJKoZIhvcN
AQELBQADggEBAKBylW71KVI/lbRfBUIZ8eUA5ZkXy5dYbGxC/utL6I0J5GsjnL5x
ShfBdBrIjY9da7b5ep365P56KG6KwG+9XWATtY6pRiCKbHPgpMxCye7ksF0fFFwP
UO7W0ayVnDpl1DqBqLtH+/l70+rfN82JFBmI75Djeof+LsyMeAR8aQB8Ii0ssHq1
t9JJJjeO5vElr7i34gxKoYUuvp5wIGn1Nc06GOYykwGyJyxviXIF6MYDeWrrjYyC
l07YAAsMLt/deAS6WDQHGRE3eu8tDyFT7I4PnlAwzjXuG9lwkByGSPxeZEh4mRa7
fSebkXFmB/NYJVg9mxOM0Ioz0j1pK6I7U7Q=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD3WM82qlcPut5b
AqFNf3bKSJCeVWaDsVqyYqdERyyqtXzz9lsOlLlnsOuqiC8uJna9xypbhiFlNGtK
mOKnJ34uCzSoXlZy8VdpsjlkRxKEWt971IT0KDnC8+du2Y1XvnyNol29wRvq7L4i
BtlvL3PObuYIqFPGo2C6AOiqRCqgekSnd0YjP1EHAIn7ltW5KZIxsZ1beIIvpUaA
vKwLroozydy66Dw2gMl0OuqDDWYVAs+5njReBC1pG8eIuYP6BTHnwQ8g7kw4mle+
w77V+gqo0VIqgbivynynV0SP0naRAJ31pVhNpg9KjxkmCrHxkfbH47Egvn0I2a8Q
lh5rW3AVAgMBAAECggEAIAUbLhR3GM40IswAXav2kNE8B4M+tiWrfBRRWwFrwt2j
w2FNAGI4bjf1Cip/ERZhoMe7sAV5JUTX7txD2ye9qfBBbNppjo+3pTMxQs4Ak6m/
7KPiYkki0R4KA8ueBJHgReWTBRGOr6beJkGEvGQENeEdXlmMHtbfmrSraf9RFVz9
h3dMJiYmn3Nlf/3mKI7cYr07ed8AH5tjiUkRxQz4RvUDkMNMoXiPBmxqrW0cDmcW
emYOFa+oSIqFQObVnhiEX9tKoGb8n/JmCzJhMcdWKEf8qdNAp28s3iZ6NOfAhcAP
/uEATvqAFlSvy9IeglJ5lw1fW/yMhnsJsSTZfdM0AQKBgQD8BTvKAvgUEVyagsWS
dJCcb5PVIUzTlUq4aYeCqEZVegZHyveHPH0rWqaCg+b5N72CxrLX1GovNFMYRWh0
g6g2tnn5ZPMjxkT/6K194mBFtl5pRY7wYYYxenDXYetGlGR8E2J+rZjrYB8X/nUc
fB8QqHPF4hdNn8JDDRkxS9JMQQKBgQD7QK8BF2XfP+3k/VyalDovCptcl4QX5Lfh
Wg5TU8EewscBgvwCrsiU1j9H9MbJshYzrZ9tkE7cCvAZQMeCdZMZmNixM8kJlIUz
N3ot/0rVvE/CGH0l4tnUP4Hf3F965Q5hZx6LaeTa/T42fNwfKWEos690MhpvpqkZ
VIUsxVp+1QKBgArSNrT/16MU88L2poEZXDQ7Xj+ihGf5WxH8BgOIMiztZmOixWob
IMzBwbCM9VfyGsMIwRkfV8fbGU5LfKa0GH7w6t/Yg9AfbUsHtJtZIwUbRYaDpBWS
Om7ZcQrue2m2aXIiavJBhfVlVTB0jpbAhZzzfrUadiGxIGg8ob5KISeBAoGBAOS9
qdXziFJssFeGdpVgl2wWWwU8CA18tA+c3mfsW2h26ZituGN03rXqK+iFp6ke0emw
ivHDrJfV+Wr0N+7EvexS5AzH2aI4LeBIBJr1zhqye2Z4xw8D7U63lrTIu8B14ta4
e+1uteTNI6OVnw1brdHRbM37g2fcshyWfzwYXGAVAoGAd5NcD+8cGEsEF1we9NVG
jrCCFHaJqga+fsNDPSwtyRBa0gcpyn+JSkmew7UcMC8xsXDWaRJYslz95j6JRIpb
aLO5P9m6yjK+IzwyYHO1YqB4f1JZPZo6I0D74RGWQJrTIWUgf1eWQq3PvzOM+BRp
xL68Sc3xmbloGYxY9m8n8fU=
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,77 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
24:00:0d:99:42:39:90:76:c8:97:26:b9:54:6a:02:c7:ac:77:05:b7
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = GreptimeDBRootCA
Validity
Not Before: Sep 16 11:37:56 2025 GMT
Not After : Sep 16 11:37:56 2026 GMT
Subject: CN = GreptimeDBRootCA
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:d3:38:dc:73:16:e7:4e:54:75:f6:39:37:b1:25:
89:2b:e4:ba:26:2f:2f:b6:d1:3c:7b:c7:f1:54:a1:
74:9d:d6:55:56:1a:e6:b3:ec:e8:d9:db:e7:47:46:
f9:02:48:ee:d8:09:c6:52:7b:5d:49:5b:40:8a:27:
23:49:ee:66:3a:7c:19:38:3a:ae:ac:a8:d8:75:a9:
f5:96:d2:34:9a:ba:50:ff:05:27:ae:3b:3b:19:9c:
30:58:1d:03:9f:a0:10:97:1f:ac:f7:65:d2:cd:ff:
9f:10:72:40:3b:48:02:c7:b7:60:cb:7b:ca:cb:2d:
27:a3:a3:84:7f:86:a4:62:e2:1b:c5:bd:4d:f0:56:
3b:79:f7:fc:79:c9:da:5f:46:f4:36:f6:fe:02:8e:
65:8b:a3:2f:4d:ac:f4:9e:ce:1b:a5:69:aa:ef:6d:
84:58:39:02:b4:29:46:bf:df:22:91:d3:1d:80:22:
2c:22:09:9d:1b:e5:5f:f7:81:14:91:b6:ae:9d:d1:
da:f5:f1:5a:28:a4:f9:78:76:af:0f:cc:f0:2a:9d:
e2:72:e1:ea:15:88:c6:50:c0:59:94:66:de:94:2e:
7d:dd:be:76:8f:5e:7d:19:52:1b:1a:55:9c:77:8f:
38:30:c4:17:6d:fd:d2:bd:51:80:60:0c:33:1c:19:
f9:bf
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
72:31:6A:B3:7E:6D:53:42:6F:0C:1F:7B:1B:49:C2:1D:1C:59:F2:B9
X509v3 Authority Key Identifier:
72:31:6A:B3:7E:6D:53:42:6F:0C:1F:7B:1B:49:C2:1D:1C:59:F2:B9
X509v3 Basic Constraints: critical
CA:TRUE
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
be:8d:13:94:bd:9c:57:31:9b:80:88:5f:40:5b:fe:60:c3:7f:
a3:b3:e0:7a:41:db:3b:50:9e:63:50:46:f8:14:8a:15:01:b7:
e1:fb:66:93:2a:c5:fc:35:91:cd:30:d2:46:b3:93:f5:6c:25:
da:ee:22:35:be:9c:04:d8:40:3d:40:8d:67:09:dd:d6:fb:52:
4e:07:7f:cf:1a:5a:c0:8b:2c:6f:56:3b:8c:e4:c1:db:1a:e9:
37:93:44:43:18:7e:2d:5f:e2:bc:03:24:7a:c2:d9:6a:14:51:
53:0d:81:c0:1d:1c:04:89:a0:7a:2d:54:23:d8:b3:36:25:b1:
60:f5:71:e2:07:ab:62:49:9e:1a:f4:ef:37:91:1b:95:99:1f:
f9:58:59:54:0d:86:4b:9b:f4:18:fb:34:36:ce:6a:89:54:24:
fa:92:d5:e5:b2:80:c9:8a:b7:aa:86:fc:be:51:49:b1:ce:8f:
a6:be:e9:c7:d8:2e:6d:17:19:7e:e2:7d:a6:d0:38:2a:43:c8:
8d:bf:5c:4c:42:80:1d:36:32:c4:5c:f0:82:8c:89:8b:da:d4:
0d:c2:e7:65:af:ff:3b:d7:f5:e3:c4:3c:34:6d:e4:71:eb:fc:
8f:fc:54:08:f6:d6:4a:06:36:3d:5b:2b:3c:1f:a6:97:88:9d:
e4:b2:25:49
-----BEGIN CERTIFICATE-----
MIIDFzCCAf+gAwIBAgIUJAANmUI5kHbIlya5VGoCx6x3BbcwDQYJKoZIhvcNAQEL
BQAwGzEZMBcGA1UEAwwQR3JlcHRpbWVEQlJvb3RDQTAeFw0yNTA5MTYxMTM3NTZa
Fw0yNjA5MTYxMTM3NTZaMBsxGTAXBgNVBAMMEEdyZXB0aW1lREJSb290Q0EwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTONxzFudOVHX2OTexJYkr5Lom
Ly+20Tx7x/FUoXSd1lVWGuaz7OjZ2+dHRvkCSO7YCcZSe11JW0CKJyNJ7mY6fBk4
Oq6sqNh1qfWW0jSaulD/BSeuOzsZnDBYHQOfoBCXH6z3ZdLN/58QckA7SALHt2DL
e8rLLSejo4R/hqRi4hvFvU3wVjt59/x5ydpfRvQ29v4CjmWLoy9NrPSezhulaarv
bYRYOQK0KUa/3yKR0x2AIiwiCZ0b5V/3gRSRtq6d0dr18VoopPl4dq8PzPAqneJy
4eoViMZQwFmUZt6ULn3dvnaPXn0ZUhsaVZx3jzgwxBdt/dK9UYBgDDMcGfm/AgMB
AAGjUzBRMB0GA1UdDgQWBBRyMWqzfm1TQm8MH3sbScIdHFnyuTAfBgNVHSMEGDAW
gBRyMWqzfm1TQm8MH3sbScIdHFnyuTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
DQEBCwUAA4IBAQC+jROUvZxXMZuAiF9AW/5gw3+js+B6Qds7UJ5jUEb4FIoVAbfh
+2aTKsX8NZHNMNJGs5P1bCXa7iI1vpwE2EA9QI1nCd3W+1JOB3/PGlrAiyxvVjuM
5MHbGuk3k0RDGH4tX+K8AyR6wtlqFFFTDYHAHRwEiaB6LVQj2LM2JbFg9XHiB6ti
SZ4a9O83kRuVmR/5WFlUDYZLm/QY+zQ2zmqJVCT6ktXlsoDJireqhvy+UUmxzo+m
vunH2C5tFxl+4n2m0DgqQ8iNv1xMQoAdNjLEXPCCjImL2tQNwudlr/871/XjxDw0
beRx6/yP/FQI9tZKBjY9Wys8H6aXiJ3ksiVJ
-----END CERTIFICATE-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDTONxzFudOVHX2
OTexJYkr5LomLy+20Tx7x/FUoXSd1lVWGuaz7OjZ2+dHRvkCSO7YCcZSe11JW0CK
JyNJ7mY6fBk4Oq6sqNh1qfWW0jSaulD/BSeuOzsZnDBYHQOfoBCXH6z3ZdLN/58Q
ckA7SALHt2DLe8rLLSejo4R/hqRi4hvFvU3wVjt59/x5ydpfRvQ29v4CjmWLoy9N
rPSezhulaarvbYRYOQK0KUa/3yKR0x2AIiwiCZ0b5V/3gRSRtq6d0dr18VoopPl4
dq8PzPAqneJy4eoViMZQwFmUZt6ULn3dvnaPXn0ZUhsaVZx3jzgwxBdt/dK9UYBg
DDMcGfm/AgMBAAECggEAHji3ihwP0mxRSH6dUlJdkxMx8z6zJARCFC/ifspvJhpB
sYU91yahlnJZYCBuhn4q9mxHnQubqGBPFrVM3konBF03Oo1i6r9p4UQFKKRA9JaN
+j8hk1NUPHDVlfXoy9EaLpVQqUE/bUwS1UaTnCfMhgjd5595v6JhVo+R6LE6Gg+f
aBfvwZT35pZnDG4JNguxl5jCTr1tnyi3QDQkOIjwP0hyjJlO4RZ6KkBZVtphfaiy
+2jEpovAtS831Imph4rCNZ17dmZ2Hn8n7TswUUT8yASqpgR8Eq88y0S9pqOjbK4Z
wbxGGwzLu8amTcRd3HXErBTykbqj0TcKbE+e0CQomQKBgQD1WIWXxwrVQehuiHqr
nMxbU/RwsLglv2oGJ4/yrobvkRMK7O4zscVKDuRciHrAHL7AlYj6wj1jCE9HjJ6n
LJtaE89xTxKEX6SJkFrHBfzdqZYzMfbbK+vASsFYrkCTcxHqjKhGKtepxjdf5KwD
lXHQsKQklLWORnpzktmwlSkF3QKBgQDcZP2eliaHrLV/uTQqJMO4AHU+1+sm2yXL
EockQ7t6zxydhixVsKcfMei7K+f3G/3geehIInEW+CanJyo6CVc8LTHp/w3jN+Pl
bR2Vhc4M9jhl/JucPpCrpNFb0+eb52IgRoU51HZ6at98fPs1pkEVOEeEXv8BHfPO
w7IHWisqSwKBgAVeTH4Hsur7qDRS9CwxIB+XdK/Kjhz1VUKRNwseWoF/cSU6DfQ/
nYuDS+fibJ0rg9j+7KJgAavbELEGGKqbxUJxUIawLj2LQjRoXjQgo2nOPiUcoMQm
79+jQxvHpE4VpHy3Wy893L4wQiHtBP6HFJh2ZQEP5/EXBbLlsbG1cdJZAoGBAKf2
p3rm/7JCo4gVg3Xi0u42GySrtWDFf0YGdsvies0bbrl2ee9MU/FoPe/3hC9FO/SJ
s8bVyM4yjHKzXHEikUMhRnsIkHLsM1i0EenUMbabJ1xCae1nmSXW1F9hXY/phVUH
qZzrGOgu7ueW8HRwogUkAQ0WwMz/IMIhTlpgE6mFAoGAOKktvlw4LpLsM5d18ycI
LPGlXJBMRsbQVkn9F2gBDm1EudFhSz/UWHVrjIjKy1ldNoIXBTP4k/+mZmgL/sHY
9oy1NswlhZqImz6yedudMsudVzu0mRvZqwIK41bqi8ajkp5YTC4fIgKIILysB+YT
FfVzb/lOZCmysmZm3Mfe8Ug=
-----END PRIVATE KEY-----

View File

@@ -0,0 +1 @@
21A923BB36E640B3E75496A43A32ED8F38CC108F

View File

@@ -0,0 +1,77 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
21:a9:23:bb:36:e6:40:b3:e7:54:96:a4:3a:32:ed:8f:38:cc:10:8e
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = GreptimeDBRootCA
Validity
Not Before: Sep 16 11:37:56 2025 GMT
Not After : Sep 16 11:37:56 2026 GMT
Subject: CN = greptime
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:b5:df:d8:cd:4a:f2:6f:d0:1f:75:07:f5:ec:2b:
44:af:59:94:c1:47:e1:8e:ad:a9:84:00:50:25:7f:
91:1b:4d:0b:b7:d1:cb:95:7f:f5:83:f5:c1:c2:4c:
83:6a:2b:99:7b:e6:a8:0a:4d:c5:fc:24:24:17:e1:
30:df:5f:31:0a:97:96:9c:1f:0f:10:63:a2:6d:be:
7c:f9:09:df:6e:87:bd:a0:94:6c:ee:71:60:a1:c2:
9a:8c:04:69:eb:7e:0c:46:57:99:30:a3:46:89:d2:
f2:8e:7e:5b:be:07:77:07:22:bd:6b:4a:6b:dd:40:
e5:d2:bd:a7:d7:18:49:4b:d9:6b:73:64:48:b8:29:
05:64:da:88:9b:2c:f4:eb:88:5e:e7:3e:f7:81:28:
55:56:cb:df:07:23:20:49:39:a3:d8:c7:cf:d1:23:
2b:f2:72:e1:95:1d:1c:72:ac:e7:60:09:40:c2:be:
5c:c0:50:d7:79:6c:28:61:5c:55:b2:2a:2c:99:6b:
29:4c:f7:4d:17:bc:d5:ba:44:56:0e:f0:61:24:42:
05:e2:c0:77:c2:19:53:82:10:c0:31:a7:8f:01:88:
71:87:46:d5:2b:00:75:33:06:3d:e3:bf:ff:dc:a9:
6b:63:8f:14:60:88:a7:c6:85:18:4a:33:31:56:fb:
1e:cb
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Alternative Name:
DNS:localhost, IP Address:127.0.0.1
X509v3 Subject Key Identifier:
0D:54:D1:9C:02:61:1C:37:23:8C:E2:1D:4F:F6:00:66:C0:FD:97:54
X509v3 Authority Key Identifier:
72:31:6A:B3:7E:6D:53:42:6F:0C:1F:7B:1B:49:C2:1D:1C:59:F2:B9
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
5d:61:15:7d:9e:60:05:bb:2c:e6:b0:a4:eb:9b:15:91:00:d3:
91:65:b6:87:bd:29:ce:17:93:8b:81:fb:c3:ed:97:6e:c9:93:
1d:5b:0d:35:1e:a8:cd:9d:ca:ae:70:84:cc:af:c6:ff:a9:d3:
b1:1c:cc:da:90:15:7b:85:19:80:3d:c9:dc:94:5d:28:fa:c5:
2d:32:6e:48:d9:f1:74:62:18:d0:f7:9d:3a:80:8b:75:7a:4d:
4a:a7:7a:8a:b4:28:d4:6d:6e:98:d6:96:4d:fc:a0:b1:ee:89:
35:b8:3f:b7:07:29:34:dc:dc:be:c9:eb:6d:fd:fd:86:3d:11:
e1:8c:3e:00:84:91:15:ef:d5:c9:6a:ec:31:73:02:fb:81:ef:
1a:c9:7f:62:a0:7f:8e:d0:d7:f0:b4:59:ba:a3:3f:e8:fb:04:
b3:ad:f9:9a:4e:ee:11:64:ca:a5:5a:4f:4a:36:ad:0e:55:82:
37:6e:8b:6f:b3:a6:89:e0:a9:45:df:f4:1f:d3:7c:76:bd:d8:
a4:9d:44:7d:89:70:76:b4:4f:5f:02:99:13:3c:d5:11:d5:6f:
ca:f8:7a:26:a1:c3:d9:c1:cb:3a:89:7c:97:bf:0d:90:0d:78:
37:ec:cb:6d:0f:13:a8:8f:f1:3e:f6:a9:82:22:1e:97:9c:e4:
0f:8d:6c:3e
-----BEGIN CERTIFICATE-----
MIIDGjCCAgKgAwIBAgIUIakjuzbmQLPnVJakOjLtjzjMEI4wDQYJKoZIhvcNAQEL
BQAwGzEZMBcGA1UEAwwQR3JlcHRpbWVEQlJvb3RDQTAeFw0yNTA5MTYxMTM3NTZa
Fw0yNjA5MTYxMTM3NTZaMBMxETAPBgNVBAMMCGdyZXB0aW1lMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtd/YzUryb9AfdQf17CtEr1mUwUfhjq2phABQ
JX+RG00Lt9HLlX/1g/XBwkyDaiuZe+aoCk3F/CQkF+Ew318xCpeWnB8PEGOibb58
+Qnfboe9oJRs7nFgocKajARp634MRleZMKNGidLyjn5bvgd3ByK9a0pr3UDl0r2n
1xhJS9lrc2RIuCkFZNqImyz064he5z73gShVVsvfByMgSTmj2MfP0SMr8nLhlR0c
cqznYAlAwr5cwFDXeWwoYVxVsiosmWspTPdNF7zVukRWDvBhJEIF4sB3whlTghDA
MaePAYhxh0bVKwB1MwY947//3KlrY48UYIinxoUYSjMxVvseywIDAQABo14wXDAa
BgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwHQYDVR0OBBYEFA1U0ZwCYRw3I4zi
HU/2AGbA/ZdUMB8GA1UdIwQYMBaAFHIxarN+bVNCbwwfextJwh0cWfK5MA0GCSqG
SIb3DQEBCwUAA4IBAQBdYRV9nmAFuyzmsKTrmxWRANORZbaHvSnOF5OLgfvD7Zdu
yZMdWw01HqjNncqucITMr8b/qdOxHMzakBV7hRmAPcnclF0o+sUtMm5I2fF0YhjQ
9506gIt1ek1Kp3qKtCjUbW6Y1pZN/KCx7ok1uD+3Byk03Ny+yett/f2GPRHhjD4A
hJEV79XJauwxcwL7ge8ayX9ioH+O0NfwtFm6oz/o+wSzrfmaTu4RZMqlWk9KNq0O
VYI3botvs6aJ4KlF3/Qf03x2vdiknUR9iXB2tE9fApkTPNUR1W/K+HomocPZwcs6
iXyXvw2QDXg37MttDxOoj/E+9qmCIh6XnOQPjWw+
-----END CERTIFICATE-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC139jNSvJv0B91
B/XsK0SvWZTBR+GOramEAFAlf5EbTQu30cuVf/WD9cHCTINqK5l75qgKTcX8JCQX
4TDfXzEKl5acHw8QY6Jtvnz5Cd9uh72glGzucWChwpqMBGnrfgxGV5kwo0aJ0vKO
flu+B3cHIr1rSmvdQOXSvafXGElL2WtzZEi4KQVk2oibLPTriF7nPveBKFVWy98H
IyBJOaPYx8/RIyvycuGVHRxyrOdgCUDCvlzAUNd5bChhXFWyKiyZaylM900XvNW6
RFYO8GEkQgXiwHfCGVOCEMAxp48BiHGHRtUrAHUzBj3jv//cqWtjjxRgiKfGhRhK
MzFW+x7LAgMBAAECggEAGJr2d3JVRuqZkY6zJvFaSH15EjEJrfDcByoNAxW1aZkh
0GDyBwFvE3GTIMM94DDn1zWxtDsott+jgRY2iMcVw8DAX9IVNVAVcH1zO4n/ZV0N
5YSkZsSF9ecgW3hZZW0BzMkbG35raPKrXWXGPvjpkdiRrHXpY98yIzGvXE/tJ/VA
nuXdopFQf8ZRI0qZ29e+zO14xGS0kVD4cPfL5CKboI1dosR2MK1AtH69MY7itZXV
aut0OSY0enHxHGyraZMUOxs4plviGqSTgCsE7K5dBwTYIkoKpbwU7TKob+eyySpW
pO5Xn4gnOkMDj2ePd8mVhg5nuhqc3c2+hRJzayfIGQKBgQD/sR3mWo08DkoI1bpT
K/EkN/WISTIhFDUsAoHtQw6vtDVY0Fmo+AjHqtipNaqvrrOMDmfASC3O4PWZqlJ6
Vwr1cECts6qpQqEw5Grz8jjOPJJsWkRaQRPyMKSGFwcqvdBNZUlArrdeN7fJVvQf
DrKnfz4IBelA2fzN6HiMKjXLJQKBgQC2F/TtT4AnICTGhaev2sgS6CPcIeh9jyAz
ZmgtvgSxn7UKO8+Ycj62gwD2i5vpbrzDnDQ4EbOOFjabPEQeQgRSIfkbYFiNH3TM
5kq6NpHQ53VqavFBg/Ht6cUl3elpVLZip6UxTMHHABerKBBPvDcJqPl2cF0yTgn9
UwYYnKiXLwKBgQDnnKPN9vZ6kSMFD71A+8vql/oEovESv8BOhlL0/co+5IlyUwD0
qwEhBKRP2noru5+OXbojlf5QqLwwnvUpNtGJdE5SSi5sNhvXMwGiyiYkwv+LwNSr
wMl1mBRgFln6oIggo4AQZHNbmDMV4Xsu6txwFzW0hM4+XXe/eTZBGTczNQKBgQCE
isWN4ts+H2iFVA3ts8sD8BMCDXBjzEU9BKJ6ASD7hWtKiqSdqzK4uHx/+LjA3gjq
PQ3JbiELIGIY9nICsDzi57Wh1qw99JKY/luQVV4n+AIANt1qubbkXM9Ss8dNEws6
nhpg2yUFS6zuuVe+IhrRnjwOmNVNY35j9QWv7n/MowKBgQDw0anDxc1QXvfI5lf8
CaYhGxSWaxLDD2XEle5gQRpyBBGIzR1sQ2StZdcyeRbRTUI+5xsrC9FSg9fSqhh7
7hRtt9jIHEQERElWIYgAe/1KdOzvCV+neWBKvjNdv6pinW/z+9dI4sWzrQNbMIo3
ggjGS2/AuAzFB2Pb1QsfoBHA7Q==
-----END PRIVATE KEY-----

View File

@@ -89,7 +89,7 @@ services:
- 5432:5432
volumes:
- ~/apps/postgres:/var/lib/postgresql/data
- ./pgsql-certs:/tmp/certs:ro
- ./certs:/tmp/certs:ro
- ./postgres/tls/pg_hba.conf:/var/lib/postgresql/pg_hba.conf
environment:
- POSTGRES_USER=greptimedb
@@ -127,7 +127,8 @@ services:
ports:
- 3306:3306
volumes:
- ~/apps/mysql:/var/lib/mysql
- ./certs:/certs:ro
- ./mysql/my.cnf:/opt/bitnami/mysql/conf/my_custom.cnf:ro
environment:
- MYSQL_DATABASE=mysql
- MYSQL_USER=greptimedb

View File

@@ -0,0 +1,4 @@
[mysqld]
ssl_ca=/certs/root.crt
ssl_cert=/certs/server.crt
ssl_key=/certs/server.key

View File

@@ -1,19 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDFDCCAfygAwIBAgIUFL/NRvcKVxZktJOLq0ly8YWXJ+4wDQYJKoZIhvcNAQEL
BQAwGTEXMBUGA1UEAwwOUG9zdGdyZXNSb290Q0EwHhcNMjUwOTE1MTM0OTI2WhcN
MjYwOTE1MTM0OTI2WjAVMRMwEQYDVQQDDApncmVwdGltZWRiMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwBBgZ5DdB2WdnrMtfsRo+YvF8odppvPaLdNd
bajQs+sf3yRU8W7PskiQFqMSgLwjtp5Vb2C517RMgWAvVfi3+gCXUhRJQHWxfNA0
neyt2hORimoC7S2uZ0x6o356gFatCRPsTSk0Y9oy4SpVl6a8Ht8q1e78++ojxVRj
PEN326a6PwEGCYw2nNfIN5T6C6Smlmr0FUWvzMhGFNgOaj4DujMZuvTw5fTbFmYN
1FSDTrSg68rzk2djkhht7JHZLkZ6YkRmsTkXaDgHiPnx22FQ6VqpLRmlKH10K1xC
6o7u8UbtkTo3xLbCz0J/2uxF9EclcmLNJIInrPrYU3DsD6lFEQIDAQABo1gwVjAU
BgNVHREEDTALgglsb2NhbGhvc3QwHQYDVR0OBBYEFJBMgeBaZKIfmnCiuI7dVbJq
FPtUMB8GA1UdIwQYMBaAFOTdao2MR8uh2v0lmUnBik8ZPBTBMA0GCSqGSIb3DQEB
CwUAA4IBAQDAJ9Pb7rN4Lf5xpHGlDVBIt6sYGI/JIRJCQhToL1dOPAimQBtkjzuJ
f+AeALJKQ8fGy4yvjgpq+3QzEZzbDCJBCCd4P3Wp2OFt6zbsSs5ZoRw1LQFvBKLM
FEpB50c6z5OLtllh53+RtPjLE6Hrkl9CsbNTN6pH4gbdioLWAMly23JzXjXfC/af
ega0R7W9nPS7D7R5tUvee75a+DBj5hO9S+d4eAIRDYYe4HUyIV8XW7Xf+g0YlBfq
VGQx7/ScM6E3uML8646ztpyOfhTUFRrhpytA1NAkcoWL6Sh+fLp+qF6uafUCP8Qe
eO8KrbBN0f0jHVFj3ykK0+r2ERA9WS8o
-----END CERTIFICATE-----

View File

@@ -1,28 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDAEGBnkN0HZZ2e
sy1+xGj5i8Xyh2mm89ot011tqNCz6x/fJFTxbs+ySJAWoxKAvCO2nlVvYLnXtEyB
YC9V+Lf6AJdSFElAdbF80DSd7K3aE5GKagLtLa5nTHqjfnqAVq0JE+xNKTRj2jLh
KlWXprwe3yrV7vz76iPFVGM8Q3fbpro/AQYJjDac18g3lPoLpKaWavQVRa/MyEYU
2A5qPgO6Mxm69PDl9NsWZg3UVINOtKDryvOTZ2OSGG3skdkuRnpiRGaxORdoOAeI
+fHbYVDpWqktGaUofXQrXELqju7xRu2ROjfEtsLPQn/a7EX0RyVyYs0kgies+thT
cOwPqUURAgMBAAECggEAA+TRGjMU/WoRUKmv9G/684Q369DUUtmCxNxnen+qVjDK
y/VVRjJ0Qi23w3MO6aPm0smm+xkU63Xw+cvUvy4fB2cs/4cAbS7K/bgi454WxY28
Ak21PXsS8W6FgKcMc2mjxxlWTcIrbmglZpc7IQCfs73b2nZ76TI4A297Njo6my4J
1AztlAPrC9hbbyzjjxMmVb6lgBEsYcb4OnDpkaBER28G3d44HKGvahJwl9YWWOCG
N431rT+ScF2iksEJkMR5GrngzFNRzjlpKSbc81krpQRncqgE6mIlTNoiqFCwZ6r+
Pp6pMCvOSqf5CuE9K4IUk0xUQCII6lKqUfGElmwsoQKBgQDghu1/Dk2MlBy22+qM
MS4sG4RpkN6AY64o6oB2lzum646GT/1/0yzsJ1jDSir3y9l5/ZtjkOxkPXxLwVMZ
4MA6VTg0AzglD3V65eIPFyA/pAcyOitgGVR17aNWjbQZ/XFCaHZqWiJZEUAC+y2q
Z4JBNHVqs898Ri+Bi/Y4tZtDZQKBgQDa/IXKXThsKsGhDgUZ+5g+9ssyxvBIZ0zZ
tucyoXWZnx8v9DLKDa6esxgUy2l6PdbUtR7BW8TqiJ4i3VnqwaKpiK8JBdcs4RFH
bD2hWFVMtPB4B3T3p66kQznaSv9t/t5rhdhDTRoKNxYy2ej8g4lnQHNQ4c6ZF8AK
PAOnatz+PQKBgCKMIFcAeGY9E/3+8KQJnTUkNseXhuTycYXSL1oQAxm3/QIlpLfa
YnsjwsCp1Nqz/w6KOzKixfzWiR2O7Z55upo/Kpq2NBlfu4/hd1sjD887CfBsiugK
fbh3jD5xJ1koahjxweazX72creDHjUs/CgfGbpqweCiDFCQ2yPgI2Y8RAoGADQPL
E87chpk37tF9fiDU2JEkXH00VKlHCP102i58ZcfuO5Rg3F9ZzHbKbJn2R9ybAwNM
s6M3ndTGyT2slWYyu66y8G9XbYRm2X7WXtt4Hln2nLucuQdPOtX//zhL3Aa0w2Eu
DDnt3UOO3zDSRweuR+OtttXkyHZ/5FvSv8V7IuUCgYEArC8bKrBc3rbAM127+3Qv
ERBjtsHwp4T5blo1xtjTDpKVvbefvihqfDn2OLN/s6FgAlNzIOr+Dw4ouBiEXC0i
aZ9oqFYy2RG+bkSFBOj2IOaVVX1HLzfxvAGUvXii0d3H5uZNYS11andeb7qjCLKa
UYVJFcRasXMzRnfIyHggT+E=
-----END PRIVATE KEY-----

View File

@@ -1,77 +0,0 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
3f:54:27:60:47:aa:7a:b6:4e:5c:82:6f:d8:55:73:b1:50:9b:9b:cc
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = PostgresRootCA
Validity
Not Before: Sep 15 13:49:26 2025 GMT
Not After : Sep 15 13:49:26 2026 GMT
Subject: CN = PostgresRootCA
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:d0:90:b1:24:87:1d:0e:2b:ae:f3:2e:9e:4e:8e:
e1:0f:5b:f8:18:79:a8:d6:89:ea:ae:be:57:69:8a:
1a:f3:e2:4b:a4:e0:d3:b7:66:84:bb:3c:f3:bf:27:
3f:e7:a0:ac:3e:2e:2e:90:e7:d0:93:91:43:3d:34:
5c:6a:ac:4f:0e:a4:c8:a3:58:7b:09:9c:cc:e0:e4:
98:47:cf:17:b0:0a:cb:1d:17:8b:80:3e:2e:61:90:
e3:98:70:79:2d:84:56:be:59:3e:31:d9:56:31:17:
ec:7a:81:18:1f:9e:74:9a:e0:db:ae:dc:cc:9d:5c:
da:93:d7:b8:99:5d:cc:38:3c:5b:2a:ba:e4:2c:7b:
f7:e9:51:c9:a1:09:40:a2:da:4b:bc:52:bc:27:82:
7b:4b:5a:76:41:03:5e:47:39:a6:b8:f7:46:ca:6e:
b1:80:31:89:d6:1d:64:a6:86:b1:4a:be:f8:20:54:
cb:f6:ac:01:73:ed:21:54:43:4a:a9:f8:cf:58:2f:
38:2f:32:77:a3:61:04:01:e5:9d:93:ba:72:c6:5d:
c8:1d:8b:87:a2:96:04:ae:a2:83:46:14:2e:ac:f5:
c3:9d:8b:14:81:91:e4:fb:1f:33:64:28:f0:0c:e7:
7d:87:4b:ef:45:0f:16:60:7e:a1:61:fa:09:8e:85:
73:29
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
E4:DD:6A:8D:8C:47:CB:A1:DA:FD:25:99:49:C1:8A:4F:19:3C:14:C1
X509v3 Authority Key Identifier:
E4:DD:6A:8D:8C:47:CB:A1:DA:FD:25:99:49:C1:8A:4F:19:3C:14:C1
X509v3 Basic Constraints: critical
CA:TRUE
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
20:61:5c:0e:76:66:c1:f0:31:cd:78:4f:e2:68:85:cc:3c:bd:
9b:19:44:ed:42:e3:6a:2d:02:94:15:f4:2a:35:73:7a:c6:0c:
af:f4:a4:02:66:bc:3e:9a:4e:24:06:cd:cb:5b:74:07:20:d7:
84:8c:86:18:f9:1f:13:f5:ab:da:ed:8d:49:33:c0:46:83:be:
c7:6b:7d:84:ab:6b:74:99:02:15:2d:45:3e:a1:ec:1a:1c:9a:
97:e6:a7:e9:39:66:ee:50:af:01:44:8e:6d:5c:d3:8b:22:f5:
95:5a:41:67:0a:b3:ca:3d:35:a3:51:42:d3:9c:00:8f:d3:09:
91:86:76:50:50:06:61:3c:ee:cf:9f:8a:b7:6c:38:97:2b:a5:
bb:45:ab:7a:e9:35:6a:5a:77:81:6d:e4:64:f6:df:19:9e:43:
f8:28:20:39:66:50:f5:4b:1c:31:fb:80:5f:e0:d8:8a:c8:63:
b5:1c:a2:c1:04:60:8d:5e:4c:99:dd:7a:bd:ed:e1:af:88:43:
5c:d6:3e:e0:73:02:eb:2d:0e:7d:a1:16:10:1b:ba:af:d2:53:
fb:6f:c3:07:b1:81:94:1d:4b:0d:76:98:42:f0:db:6d:4d:d0:
e2:6b:d9:b9:32:cc:ce:7d:72:c4:b8:ea:21:55:d6:83:23:f0:
6e:df:f6:9c
-----BEGIN CERTIFICATE-----
MIIDEzCCAfugAwIBAgIUP1QnYEeqerZOXIJv2FVzsVCbm8wwDQYJKoZIhvcNAQEL
BQAwGTEXMBUGA1UEAwwOUG9zdGdyZXNSb290Q0EwHhcNMjUwOTE1MTM0OTI2WhcN
MjYwOTE1MTM0OTI2WjAZMRcwFQYDVQQDDA5Qb3N0Z3Jlc1Jvb3RDQTCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBANCQsSSHHQ4rrvMunk6O4Q9b+Bh5qNaJ
6q6+V2mKGvPiS6Tg07dmhLs8878nP+egrD4uLpDn0JORQz00XGqsTw6kyKNYewmc
zODkmEfPF7AKyx0Xi4A+LmGQ45hweS2EVr5ZPjHZVjEX7HqBGB+edJrg267czJ1c
2pPXuJldzDg8Wyq65Cx79+lRyaEJQKLaS7xSvCeCe0tadkEDXkc5prj3RspusYAx
idYdZKaGsUq++CBUy/asAXPtIVRDSqn4z1gvOC8yd6NhBAHlnZO6csZdyB2Lh6KW
BK6ig0YULqz1w52LFIGR5PsfM2Qo8AznfYdL70UPFmB+oWH6CY6FcykCAwEAAaNT
MFEwHQYDVR0OBBYEFOTdao2MR8uh2v0lmUnBik8ZPBTBMB8GA1UdIwQYMBaAFOTd
ao2MR8uh2v0lmUnBik8ZPBTBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
BQADggEBACBhXA52ZsHwMc14T+Johcw8vZsZRO1C42otApQV9Co1c3rGDK/0pAJm
vD6aTiQGzctbdAcg14SMhhj5HxP1q9rtjUkzwEaDvsdrfYSra3SZAhUtRT6h7Boc
mpfmp+k5Zu5QrwFEjm1c04si9ZVaQWcKs8o9NaNRQtOcAI/TCZGGdlBQBmE87s+f
irdsOJcrpbtFq3rpNWpad4Ft5GT23xmeQ/goIDlmUPVLHDH7gF/g2IrIY7UcosEE
YI1eTJnder3t4a+IQ1zWPuBzAustDn2hFhAbuq/SU/tvwwexgZQdSw12mELw221N
0OJr2bkyzM59csS46iFV1oMj8G7f9pw=
-----END CERTIFICATE-----

View File

@@ -1,28 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDQkLEkhx0OK67z
Lp5OjuEPW/gYeajWiequvldpihrz4kuk4NO3ZoS7PPO/Jz/noKw+Li6Q59CTkUM9
NFxqrE8OpMijWHsJnMzg5JhHzxewCssdF4uAPi5hkOOYcHkthFa+WT4x2VYxF+x6
gRgfnnSa4Nuu3MydXNqT17iZXcw4PFsquuQse/fpUcmhCUCi2ku8UrwngntLWnZB
A15HOaa490bKbrGAMYnWHWSmhrFKvvggVMv2rAFz7SFUQ0qp+M9YLzgvMnejYQQB
5Z2TunLGXcgdi4eilgSuooNGFC6s9cOdixSBkeT7HzNkKPAM532HS+9FDxZgfqFh
+gmOhXMpAgMBAAECggEAJj7OOQpMxe81QPNExNeXBjPBei1/DD5UqnANg/U3DY9q
+yReNoSmZNZfxScLpSw1wZ5C3EAiI5lQ6O2L3+TSh3cosql0QHZALKJGmW5so9PK
3L4XTvA4HZyN28OOOQ5KQNQJH2eBEnYWX32CQLQUIl2G81kabWXcUB2QBJGqdAoq
cMTsDfWSQvZG+Qtdu5Btyd8rf2WWZPQXYh3GMxsphFYv5TnY+5Nu8oJ/SPlDcHVK
Fk3CI9jpUa98Mo2OWeMbP9ZU9HB+SpTMN1XpBfda9JKq8N67TBAfEkyUTxv2ILr0
4KUdUCWMyMsl69JpRVlRWCBxKnGOv3HXIkprWT4mzQKBgQD1XdVNm5ui/TCmQirg
3GJQvjsfKqxFKguvlCpC7yW2Z9nNP9B/7k+5lNuKkbdgGVO+G2P+UTBLo2mZ8FjH
hKSoxoDaRpfvmeDG1nrAdcfSSJYNdFOZIyT4bb6LQViTmq79H7J2kHRbM76+exKf
DO3A/nQ0R6NmZhmE3pI0i3/0xQKBgQDZmpL+e+xbc1+4D6UwVQ9E5e0btJha+zyA
qY2331Y2H3o/+wBOLglx7oA2RrVUrovVXGHxnhyZbt7SASOxmdnAtzEgjxQYkt/f
WoCfCRMWVaUaTNFx6yEe3MCzi4p+onuR03P6NeFuZg42Ce7OC/YUPvj3N8slwCHx
7vktQajTFQKBgQDhLeEg3SrjVPt9yYGyZW7w7PQzMSVyQBkDZLW4QeNZWEuBWwxN
K/cMuLkWnY4t6nB5KogFEJuC/xBeDz5atLeskKgWKe0sHgs0UAfNcwoUSTs0U8qY
4rD+MTFKeYkBxVbMxHcy6nVucbme9DHtt57GnH8mr4wc4JyQ3PZOtKuUhQKBgBXk
D/+b9eYpUCrTIBohpHyY6OaNn3ofpjGHXSNvS1v8hK7lEF3NEdQtWyaarGlNefHH
7m6BKn1hAvd7d9zJDnfttfqBilMCi5ULpL7bCdmIWiiwK63bKqTRLm2hjxg4Plgo
PpWgUAJsL+fNzgFagLH+t8sfCMfOcVvGX7jmzxYVAoGBAOG/WA9ag+owBeHFBdIH
astwrDOTFnpHUaZb3lL3YFctkVp+P2MQXVhS/N7Qk+0lu8GbL9rQt8KMRR9Z6z2G
mUNlVTFrLYjrWZUUYxGmGVCs1ML5GWaP4Dv6gu8yxLDN4IhRiLl034j2bJVABTDv
9vPkr7f1j3nDNpLXely/Cv4C
-----END PRIVATE KEY-----

View File

@@ -1 +0,0 @@
14BFCD46F70A571664B4938BAB4972F1859727EE

View File

@@ -1,77 +0,0 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
14:bf:cd:46:f7:0a:57:16:64:b4:93:8b:ab:49:72:f1:85:97:27:ed
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = PostgresRootCA
Validity
Not Before: Sep 15 13:49:26 2025 GMT
Not After : Sep 15 13:49:26 2026 GMT
Subject: CN = postgres
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:8f:d2:77:76:52:9e:e0:87:4d:a3:83:88:58:fc:
51:61:be:33:8b:c2:37:cd:52:ca:5c:29:78:10:99:
e8:29:a5:2f:af:70:2c:24:46:f8:2c:a7:3e:d3:62:
15:7e:09:e6:bb:9f:05:e8:ec:cc:e7:38:fc:92:36:
6d:ba:fd:0e:50:eb:5b:ac:50:93:b2:bd:87:43:95:
f1:b3:5e:88:3f:88:0d:26:0c:05:8f:f3:e2:9e:e9:
d4:f7:8b:ba:0c:af:45:06:29:ff:5b:aa:24:49:0e:
0e:ee:8a:88:16:3f:89:73:71:0c:e2:26:3b:30:3d:
f5:3f:73:fe:da:2d:cf:b0:46:eb:20:8a:6b:45:01:
0c:a6:c8:4f:ab:38:62:b2:dd:51:0a:fd:c3:e3:4b:
d2:ed:97:a1:de:03:d6:08:c6:50:5e:9d:03:6a:63:
03:27:c2:c1:80:e7:c0:88:0f:49:42:72:28:3c:72:
50:df:ee:47:b6:c5:75:85:61:8f:7c:e7:27:ee:c7:
d7:23:56:c4:bf:76:9e:62:79:b1:6f:3e:0d:b6:bc:
10:19:ff:c1:16:88:07:14:0e:99:87:21:e0:78:32:
97:69:b9:ba:30:16:95:ef:12:8d:2e:64:4c:c2:c9:
78:25:48:70:05:24:45:f1:aa:18:a5:02:33:d2:27:
d5:1b
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Alternative Name:
DNS:localhost, IP Address:127.0.0.1
X509v3 Subject Key Identifier:
06:3E:AA:04:F6:4F:3D:21:0E:99:9A:A7:F2:44:9F:B8:4C:23:3E:BA
X509v3 Authority Key Identifier:
E4:DD:6A:8D:8C:47:CB:A1:DA:FD:25:99:49:C1:8A:4F:19:3C:14:C1
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
4a:55:2d:de:ca:70:fb:54:69:e4:bb:4b:f2:74:a3:27:8d:d1:
89:3b:29:5d:05:b5:ea:04:ea:32:c8:8e:ca:d2:a4:1e:a3:83:
de:03:fe:cd:86:4d:21:d8:a3:b8:5b:e5:01:61:bd:d0:69:6f:
bd:9a:53:c9:c7:67:34:bd:b8:5c:5d:31:38:3d:d3:33:0d:ec:
44:a0:37:b3:13:96:50:82:f9:46:5e:3c:fa:c1:77:09:4b:21:
91:93:f7:59:d1:32:c7:cb:cd:2e:11:51:64:41:97:b0:ac:07:
d8:1d:ee:3d:54:e0:03:f6:74:e4:2b:03:63:e3:f6:3e:cc:61:
c5:0e:81:12:9e:84:1a:5e:64:1c:c5:3c:c4:6b:f3:50:a8:b6:
3b:53:81:73:fe:1e:fb:35:40:21:57:43:37:e2:de:52:6f:eb:
72:f6:39:1e:e5:58:1d:09:29:82:ef:c2:24:3f:19:0f:48:c2:
01:f6:34:2c:37:85:ea:6c:a2:c9:e4:a7:38:a5:e5:6e:0f:20:
89:ad:1c:4e:c4:21:a3:b1:74:89:dc:c5:67:99:db:83:4c:60:
74:66:5d:ee:f0:fc:58:30:03:e5:68:59:05:05:1e:cd:db:41:
16:45:01:b6:3e:04:5b:95:2a:4d:b6:bc:ea:2d:55:99:01:1d:
bd:4a:e6:89
-----BEGIN CERTIFICATE-----
MIIDGDCCAgCgAwIBAgIUFL/NRvcKVxZktJOLq0ly8YWXJ+0wDQYJKoZIhvcNAQEL
BQAwGTEXMBUGA1UEAwwOUG9zdGdyZXNSb290Q0EwHhcNMjUwOTE1MTM0OTI2WhcN
MjYwOTE1MTM0OTI2WjATMREwDwYDVQQDDAhwb3N0Z3JlczCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAI/Sd3ZSnuCHTaODiFj8UWG+M4vCN81SylwpeBCZ
6CmlL69wLCRG+CynPtNiFX4J5rufBejszOc4/JI2bbr9DlDrW6xQk7K9h0OV8bNe
iD+IDSYMBY/z4p7p1PeLugyvRQYp/1uqJEkODu6KiBY/iXNxDOImOzA99T9z/tot
z7BG6yCKa0UBDKbIT6s4YrLdUQr9w+NL0u2Xod4D1gjGUF6dA2pjAyfCwYDnwIgP
SUJyKDxyUN/uR7bFdYVhj3znJ+7H1yNWxL92nmJ5sW8+Dba8EBn/wRaIBxQOmYch
4Hgyl2m5ujAWle8SjS5kTMLJeCVIcAUkRfGqGKUCM9In1RsCAwEAAaNeMFwwGgYD
VR0RBBMwEYIJbG9jYWxob3N0hwR/AAABMB0GA1UdDgQWBBQGPqoE9k89IQ6Zmqfy
RJ+4TCM+ujAfBgNVHSMEGDAWgBTk3WqNjEfLodr9JZlJwYpPGTwUwTANBgkqhkiG
9w0BAQsFAAOCAQEASlUt3spw+1Rp5LtL8nSjJ43RiTspXQW16gTqMsiOytKkHqOD
3gP+zYZNIdijuFvlAWG90GlvvZpTycdnNL24XF0xOD3TMw3sRKA3sxOWUIL5Rl48
+sF3CUshkZP3WdEyx8vNLhFRZEGXsKwH2B3uPVTgA/Z05CsDY+P2PsxhxQ6BEp6E
Gl5kHMU8xGvzUKi2O1OBc/4e+zVAIVdDN+LeUm/rcvY5HuVYHQkpgu/CJD8ZD0jC
AfY0LDeF6myiyeSnOKXlbg8gia0cTsQho7F0idzFZ5nbg0xgdGZd7vD8WDAD5WhZ
BQUezdtBFkUBtj4EW5UqTba86i1VmQEdvUrmiQ==
-----END CERTIFICATE-----

View File

@@ -1,28 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCP0nd2Up7gh02j
g4hY/FFhvjOLwjfNUspcKXgQmegppS+vcCwkRvgspz7TYhV+Cea7nwXo7MznOPyS
Nm26/Q5Q61usUJOyvYdDlfGzXog/iA0mDAWP8+Ke6dT3i7oMr0UGKf9bqiRJDg7u
iogWP4lzcQziJjswPfU/c/7aLc+wRusgimtFAQymyE+rOGKy3VEK/cPjS9Ltl6He
A9YIxlBenQNqYwMnwsGA58CID0lCcig8clDf7ke2xXWFYY985yfux9cjVsS/dp5i
ebFvPg22vBAZ/8EWiAcUDpmHIeB4MpdpubowFpXvEo0uZEzCyXglSHAFJEXxqhil
AjPSJ9UbAgMBAAECggEAMzjlPSnt1c+6kgrti6vJnEMufo8caq8azTojvd3itrpT
Qmtz/TOdfeHO2WAsgWgrWhUn+K4lyBAevEBQUnGAIMfCLZvBDr67pxGHNLftVJsy
+6L6dCuLQcfMBqIoEA54Sdq5TL9LDw3HuaNzFmFopcnUqdKt79+RMhkw4inAt2nx
8mWzzA+u3FjR6OsjakUV3Xl+ae0GnybtLb3o2LofDbLWtw9LDPDfE8znMd5Zrmsc
fETfAFrrb1bfGRtE8T822EHt5TLc1mAqTGsVc9lMp22NEJwrfj6KLUhZex1MUqwh
q7Q4NQGNO2z89VVDXYuADKOLmk+FN4Yn710xL5HYgQKBgQDBxhbWY4xKXkzMDr2T
os8PjPfObbCmeutbjgZ0T8vf0BzHamHr5lnGjUL/1U0fZDOSO7eiT3WG+juvx4Yz
J+VNxyr6km+1ILJuiZ9N5Smf7mPa3dbbPGY39fatNxHa4nQUkLpzIjRNnQeKjult
I7IAxmKx+9Xkp50VaRrcBBXiQQKBgQC+Ael+mL6/rCaN9O9Ikh4dI4YY3rHtBOEG
uYKflchq9wwiqls+wlDXA4Zy3LRTvCA3k6DcVKHRl1fJiWebD/aLF+5lDFkdcq/H
g8V/HPxOGXt2z1LOBoHtK9z+3X6/iKTEEGs2gFX38AKazErFnmyR8beZGSoPTeV9
kwqqEPZoWwKBgHY6GGECJ+yQEKdILx7O7Sr9xYxrnKZWxRb9Vq7MqQWLgqOhItG9
n/Tm54QUbpPCnTyRtv8PIBQUxSoAwL6hjuSfqndrtUB8DKIo0cvDEFyGJOK7C4aE
GZH3PmR8/yXfmRebHkd1DRh1wM4nWADBELAsWrdhgiWaptsTX9HxpDCBAoGBALRm
/FrGi9VQjgqaU9aDZTQlVlymlCiGSWEpIWyDuhZVGaCQPrIHrNl4715/TndyVuRt
tn17nWhyWVdwrn37WgoWXrJyVuouydaUth9KVgORDjhwAB687v9QdjNF+eHAyy+c
py7ICtetecufzB51nH+1EKy7x8NgIlRoF+gA0vOhAoGBAItY2Tl+tNn4Bywfgevb
rSn+DVB/25ZaeQvoHlaE44Af010iYQUReyS6bLckS08XBbBHvFt5N5PDaeCmWKl3
v2q7S1rvcMhdn5RsgJXNY63xLGSFHt/BKa2QBdFBO2sEmvsZh8K5dYJLvvfq8h3c
nR7IvxmGOWUF677QtjDbrase
-----END PRIVATE KEY-----