diff --git a/config/config_defaults.go b/config/config_defaults.go index 839d22e5d..4773b908a 100644 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -189,7 +189,12 @@ const CGRATES_CFG_JSON = ` "sqlMaxIdleConns": 10, // maximum database connections idle, not applying for mongo "sqlConnMaxLifetime": "0", // maximum amount of time a connection may be reused (0 for unlimited), not applying for mongo "mysqlDSNParams":{}, // DSN params for opening db - "pgSSLMode":"disable", // ssl mode in case of *postgres + "pgSSLMode": "disable", // determines whether or with what priority a secure SSL TCP/IP connection will be negotiated with the server + //"pgSSLCert": "", // file name of the client SSL certificate, replacing the default ~/.postgresql/postgresql.crt + //"pgSSLKey": "", // location for the secret key used for the client certificate + //"pgSSLPassword": "", // specifies the password for the secret key specified in pgSSLKey + //"pgSSLCertMode": "allow", // determines whether a client certificate may be sent to the server, and whether the server is required to request one + //"pgSSLRootCert": "", // name of a file containing SSL certificate authority (CA) certificate(s) "mysqlLocation": "Local", // the location the time from mysql is retrived "mongoQueryTimeout":"10s", // timeout for query when mongo is used "mongoConnScheme": "mongodb" // scheme for MongoDB connection diff --git a/config/configsanity.go b/config/configsanity.go index 0eb09ceab..9cd8f2c5d 100644 --- a/config/configsanity.go +++ b/config/configsanity.go @@ -882,10 +882,14 @@ func (cfg *CGRConfig) checkConfigSanity() error { // StorDB sanity checks if cfg.storDbCfg.Type == utils.MetaPostgres { - if !slices.Contains([]string{utils.PostgresSSLModeDisable, utils.PostgresSSLModeAllow, - utils.PostgresSSLModePrefer, utils.PostgresSSLModeRequire, utils.PostgresSSLModeVerifyCa, - utils.PostgresSSLModeVerifyFull}, cfg.storDbCfg.Opts.PgSSLMode) { - return fmt.Errorf("<%s> unsupported ssl mode for storDB", utils.StorDB) + if !slices.Contains([]string{utils.PgSSLModeDisable, utils.PgSSLModeAllow, + utils.PgSSLModePrefer, utils.PgSSLModeRequire, utils.PgSSLModeVerifyCA, + utils.PgSSLModeVerifyFull}, cfg.storDbCfg.Opts.PgSSLMode) { + return fmt.Errorf("<%s> unsupported pgSSLMode (sslmode) in storDB configuration", utils.StorDB) + } + if !slices.Contains([]string{utils.PgSSLModeDisable, utils.PgSSLModeAllow, utils.PgSSLModeRequire, + utils.EmptyString}, cfg.storDbCfg.Opts.PgSSLCertMode) { + return fmt.Errorf("<%s> unsupported pgSSLCertMode (sslcertmode) in storDB configuration", utils.StorDB) } } diff --git a/config/datadbcfg.go b/config/datadbcfg.go index 99eedc87e..81de55dfa 100644 --- a/config/datadbcfg.go +++ b/config/datadbcfg.go @@ -514,6 +514,11 @@ type DBOptsJson struct { SQLConnMaxLifetime *string `json:"sqlConnMaxLifetime"` MYSQLDSNParams map[string]string `json:"mysqlDSNParams"` PgSSLMode *string `json:"pgSSLMode"` + PgSSLCert *string `json:"pgSSLCert"` + PgSSLKey *string `json:"pgSSLKey"` + PgSSLPassword *string `json:"pgSSLPassword"` + PgSSLCertMode *string `json:"pgSSLCertMode"` + PgSSLRootCert *string `json:"pgSSLRootCert"` MySQLLocation *string `json:"mysqlLocation"` } diff --git a/config/stordbcfg.go b/config/stordbcfg.go index 3b92ccfec..5e9d2ec6c 100644 --- a/config/stordbcfg.go +++ b/config/stordbcfg.go @@ -36,6 +36,11 @@ type StorDBOpts struct { SQLConnMaxLifetime time.Duration SQLDSNParams map[string]string PgSSLMode string + PgSSLCert string + PgSSLKey string + PgSSLPassword string + PgSSLCertMode string + PgSSLRootCert string MySQLLocation string MongoQueryTimeout time.Duration MongoConnScheme string @@ -88,6 +93,21 @@ func (dbOpts *StorDBOpts) loadFromJSONCfg(jsnCfg *DBOptsJson) (err error) { if jsnCfg.PgSSLMode != nil { dbOpts.PgSSLMode = *jsnCfg.PgSSLMode } + if jsnCfg.PgSSLCert != nil { + dbOpts.PgSSLCert = *jsnCfg.PgSSLCert + } + if jsnCfg.PgSSLKey != nil { + dbOpts.PgSSLKey = *jsnCfg.PgSSLKey + } + if jsnCfg.PgSSLPassword != nil { + dbOpts.PgSSLPassword = *jsnCfg.PgSSLPassword + } + if jsnCfg.PgSSLCertMode != nil { + dbOpts.PgSSLCertMode = *jsnCfg.PgSSLCertMode + } + if jsnCfg.PgSSLRootCert != nil { + dbOpts.PgSSLRootCert = *jsnCfg.PgSSLRootCert + } if jsnCfg.MySQLLocation != nil { dbOpts.MySQLLocation = *jsnCfg.MySQLLocation } @@ -182,6 +202,11 @@ func (dbOpts *StorDBOpts) Clone() *StorDBOpts { SQLConnMaxLifetime: dbOpts.SQLConnMaxLifetime, SQLDSNParams: dbOpts.SQLDSNParams, PgSSLMode: dbOpts.PgSSLMode, + PgSSLCert: dbOpts.PgSSLCert, + PgSSLKey: dbOpts.PgSSLKey, + PgSSLPassword: dbOpts.PgSSLPassword, + PgSSLCertMode: dbOpts.PgSSLCertMode, + PgSSLRootCert: dbOpts.PgSSLRootCert, MySQLLocation: dbOpts.MySQLLocation, MongoQueryTimeout: dbOpts.MongoQueryTimeout, MongoConnScheme: dbOpts.MongoConnScheme, @@ -231,6 +256,21 @@ func (dbcfg StorDbCfg) AsMapInterface(string) any { utils.MongoQueryTimeoutCfg: dbcfg.Opts.MongoQueryTimeout.String(), utils.MongoConnSchemeCfg: dbcfg.Opts.MongoConnScheme, } + if dbcfg.Opts.PgSSLCert != "" { + opts[utils.PgSSLCertCfg] = dbcfg.Opts.PgSSLCert + } + if dbcfg.Opts.PgSSLKey != "" { + opts[utils.PgSSLKeyCfg] = dbcfg.Opts.PgSSLKey + } + if dbcfg.Opts.PgSSLPassword != "" { + opts[utils.PgSSLPasswordCfg] = dbcfg.Opts.PgSSLPassword + } + if dbcfg.Opts.PgSSLCertMode != "" { + opts[utils.PgSSLCertModeCfg] = dbcfg.Opts.PgSSLCertMode + } + if dbcfg.Opts.PgSSLRootCert != "" { + opts[utils.PgSSLRootCertCfg] = dbcfg.Opts.PgSSLRootCert + } mp := map[string]any{ utils.DataDbTypeCfg: utils.Meta + dbcfg.Type, utils.DataDbHostCfg: dbcfg.Host, @@ -276,6 +316,21 @@ func diffStorDBOptsJsonCfg(d *DBOptsJson, v1, v2 *StorDBOpts) *DBOptsJson { if v1.PgSSLMode != v2.PgSSLMode { d.PgSSLMode = utils.StringPointer(v2.PgSSLMode) } + if v1.PgSSLCert != v2.PgSSLCert { + d.PgSSLCert = utils.StringPointer(v2.PgSSLCert) + } + if v1.PgSSLKey != v2.PgSSLKey { + d.PgSSLKey = utils.StringPointer(v2.PgSSLKey) + } + if v1.PgSSLPassword != v2.PgSSLPassword { + d.PgSSLPassword = utils.StringPointer(v2.PgSSLPassword) + } + if v1.PgSSLCertMode != v2.PgSSLCertMode { + d.PgSSLCertMode = utils.StringPointer(v2.PgSSLCertMode) + } + if v1.PgSSLRootCert != v2.PgSSLRootCert { + d.PgSSLRootCert = utils.StringPointer(v2.PgSSLRootCert) + } if v1.MySQLLocation != v2.MySQLLocation { d.MySQLLocation = utils.StringPointer(v2.MySQLLocation) } diff --git a/data/conf/cgrates/cgrates.json b/data/conf/cgrates/cgrates.json index 1fe0f1776..fa98ecfc2 100755 --- a/data/conf/cgrates/cgrates.json +++ b/data/conf/cgrates/cgrates.json @@ -167,7 +167,12 @@ // "sqlMaxIdleConns": 10, // maximum database connections idle, not applying for mongo // "sqlConnMaxLifetime": "0", // maximum amount of time a connection may be reused (0 for unlimited), not applying for mongo // "mysqlDSNParams":{}, // DSN params for opening db -// "pgSSLMode":"disable", // ssl mode in case of *postgres +// "pgSSLMode": "disable", // determines whether or with what priority a secure SSL TCP/IP connection will be negotiated with the server +// //"pgSSLCert": "", // file name of the client SSL certificate, replacing the default ~/.postgresql/postgresql.crt +// //"pgSSLKey": "", // location for the secret key used for the client certificate +// //"pgSSLPassword": "", // specifies the password for the secret key specified in pgSSLKey +// //"pgSSLCertMode": "allow", // determines whether a client certificate may be sent to the server, and whether the server is required to request one +// //"pgSSLRootCert": "", // name of a file containing SSL certificate authority (CA) certificate(s) // "mysqlLocation": "Local", // the location the time from mysql is retrived // "mongoQueryTimeout":"10s", // timeout for query when mongo is used // "mongoConnScheme": "mongodb" // scheme for MongoDB connection diff --git a/engine/storage_postgres.go b/engine/storage_postgres.go index 81ea3c66a..4c990092c 100644 --- a/engine/storage_postgres.go +++ b/engine/storage_postgres.go @@ -32,29 +32,48 @@ type PostgresStorage struct { } // NewPostgresStorage returns the posgres storDB -func NewPostgresStorage(host, port, name, user, password, sslmode string, maxConn, maxIdleConn int, connMaxLifetime time.Duration) (*SQLStorage, error) { - connectString := fmt.Sprintf("host=%s port=%s dbname=%s user=%s password=%s sslmode=%s", host, port, name, user, password, sslmode) - db, err := gorm.Open(postgres.Open(connectString), &gorm.Config{AllowGlobalUpdate: true}) +func NewPostgresStorage(host, port, name, user, password, + sslmode, sslcert, sslkey, sslpassword, sslcertmode, sslrootcert string, + maxConn, maxIdleConn int, connMaxLifetime time.Duration) (*SQLStorage, error) { + connStr := fmt.Sprintf( + "host=%s port=%s dbname=%s user=%s password=%s sslmode=%s", + host, port, name, user, password, sslmode) + if sslcert != "" { + connStr = connStr + " sslcert=" + sslcert + } + if sslkey != "" { + connStr = connStr + " sslkey=" + sslkey + } + if sslpassword != "" { + connStr = connStr + " sslpassword=" + sslpassword + } + if sslcertmode != "" { + connStr = connStr + " sslcertmode=" + sslcertmode + } + if sslrootcert != "" { + connStr = connStr + " sslrootcert=" + sslrootcert + } + db, err := gorm.Open(postgres.Open(connStr), &gorm.Config{AllowGlobalUpdate: true}) if err != nil { return nil, err } - postgresStorage := new(PostgresStorage) - if postgresStorage.DB, err = db.DB(); err != nil { + pgStor := new(PostgresStorage) + if pgStor.DB, err = db.DB(); err != nil { return nil, err } - if err = postgresStorage.DB.Ping(); err != nil { + if err = pgStor.DB.Ping(); err != nil { return nil, err } - postgresStorage.DB.SetMaxIdleConns(maxIdleConn) - postgresStorage.DB.SetMaxOpenConns(maxConn) - postgresStorage.DB.SetConnMaxLifetime(connMaxLifetime) + pgStor.DB.SetMaxIdleConns(maxIdleConn) + pgStor.DB.SetMaxOpenConns(maxConn) + pgStor.DB.SetConnMaxLifetime(connMaxLifetime) //db.LogMode(true) - postgresStorage.db = db + pgStor.db = db return &SQLStorage{ - DB: postgresStorage.DB, - db: postgresStorage.db, - StorDB: postgresStorage, - SQLImpl: postgresStorage, + DB: pgStor.DB, + db: pgStor.db, + StorDB: pgStor, + SQLImpl: pgStor, }, nil } diff --git a/engine/storage_utils.go b/engine/storage_utils.go index 27a1ce2d8..ccf02bff5 100644 --- a/engine/storage_utils.go +++ b/engine/storage_utils.go @@ -70,6 +70,7 @@ func NewStorDBConn(dbType, host, port, name, user, pass, marshaler string, db, err = NewMongoStorage(opts.MongoConnScheme, host, port, name, user, pass, marshaler, utils.MetaStorDB, stringIndexedFields, opts.MongoQueryTimeout) case utils.MetaPostgres: db, err = NewPostgresStorage(host, port, name, user, pass, opts.PgSSLMode, + opts.PgSSLCert, opts.PgSSLKey, opts.PgSSLPassword, opts.PgSSLCertMode, opts.PgSSLRootCert, opts.SQLMaxOpenConns, opts.SQLMaxIdleConns, opts.SQLConnMaxLifetime) case utils.MetaMySQL: db, err = NewMySQLStorage(host, port, name, user, pass, opts.SQLMaxOpenConns, opts.SQLMaxIdleConns, diff --git a/services/stordb.go b/services/stordb.go index cc5eb5c46..defca89bf 100644 --- a/services/stordb.go +++ b/services/stordb.go @@ -196,5 +196,10 @@ func (db *StorDBService) needsConnectionReload() bool { return true } return db.cfg.StorDbCfg().Type == utils.MetaPostgres && - db.oldDBCfg.Opts.PgSSLMode != db.cfg.StorDbCfg().Opts.PgSSLMode + (db.oldDBCfg.Opts.PgSSLMode != db.cfg.StorDbCfg().Opts.PgSSLMode || + db.oldDBCfg.Opts.PgSSLCert != db.cfg.StorDbCfg().Opts.PgSSLCert || + db.oldDBCfg.Opts.PgSSLKey != db.cfg.StorDbCfg().Opts.PgSSLKey || + db.oldDBCfg.Opts.PgSSLPassword != db.cfg.StorDbCfg().Opts.PgSSLPassword || + db.oldDBCfg.Opts.PgSSLCertMode != db.cfg.StorDbCfg().Opts.PgSSLCertMode || + db.oldDBCfg.Opts.PgSSLRootCert != db.cfg.StorDbCfg().Opts.PgSSLRootCert) } diff --git a/utils/consts.go b/utils/consts.go index 24c992223..432fa35ec 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -1826,12 +1826,12 @@ const ( // StorDB var ( - PostgresSSLModeDisable = "disable" - PostgresSSLModeAllow = "allow" - PostgresSSLModePrefer = "prefer" - PostgresSSLModeRequire = "require" - PostgresSSLModeVerifyCa = "verify-ca" - PostgresSSLModeVerifyFull = "verify-full" + PgSSLModeDisable = "disable" + PgSSLModeAllow = "allow" + PgSSLModePrefer = "prefer" + PgSSLModeRequire = "require" + PgSSLModeVerifyCA = "verify-ca" + PgSSLModeVerifyFull = "verify-full" ) // GeneralCfg @@ -1888,6 +1888,11 @@ const ( MongoQueryTimeoutCfg = "mongoQueryTimeout" MongoConnSchemeCfg = "mongoConnScheme" PgSSLModeCfg = "pgSSLMode" + PgSSLCertCfg = "pgSSLCert" + PgSSLKeyCfg = "pgSSLKey" + PgSSLPasswordCfg = "pgSSLPassword" + PgSSLCertModeCfg = "pgSSLCertMode" + PgSSLRootCertCfg = "pgSSLRootCert" ItemsCfg = "items" OptsCfg = "opts" Tenants = "tenants"