Skip to content
This repository has been archived by the owner on Aug 21, 2023. It is now read-only.

validation(database/postgres) add SSL client authentication (PROJQUAY-2417) #214

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
113 changes: 113 additions & 0 deletions pkg/lib/fieldgroups/database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,22 @@ type DbConnectionArgsStruct struct {
// Postgres arguments
SslRootCert string `default:"" json:"sslrootcert,omitempty" yaml:"sslrootcert,omitempty"`
SslMode string `default:"" json:"sslmode,omitempty" yaml:"sslmode,omitempty"`
SslCert string `default:"" json:"sslcert,omitempty" yaml:"sslcert,omitempty"`
SslKey string `default:"" json:"sslkey,omitempty" yaml:"sslkey,omitempty"`
SslSni string `default:"" json:"sslsni,omitempty" yaml:"sslsni,omitempty"`
SslMinProtocolVersion string `default:"" json:"ssl_min_protocol_version,omitempty" yaml:"ssl_min_protocol_version,omitempty"`
SslMaxProtocolVersion string `default:"" json:"ssl_max_protocol_version,omitempty" yaml:"ssl_max_protocol_version,omitempty"`
SslCrl string `default:"" json:"sslcrl,omitempty" yaml:"sslcrl,omitempty"`
SslCrlDir string `default:"" json:"sslcrldir,omitempty" yaml:"sslcrldir,omitempty"`
SslCompression int `default:0 json:"sslcompression,omitempty" yaml:"sslcompression,omitempty"`


// Network arguments
Keepalives int `default:0 json:"keepalives,omitempty" yaml:"keepalives,omitempty"`
KeepalivesIdle int `default:10 json:"keepalives_idle,omitempty" yaml:"keepalives_idle,omitempty"`
KeepalivesInterval int `default:2 json:"keepalives_interval,omitempty" yaml:"keepalives_interval,omitempty"`
KeepalivesCount int `default:3 json:"keepalives_count,omitempty" yaml:"keepalives_count,omitempty"`
TcpUserTimeout int `default:0 json:"tcp_user_timeout,omitempty" yaml:"tcp_user_timeout,omitempty"`
}

// SslStruct represents the SslStruct config fields
Expand Down Expand Up @@ -52,6 +68,19 @@ func NewDatabaseFieldGroup(fullConfig map[string]interface{}) (*DatabaseFieldGro
return newDatabaseFieldGroup, nil
}

// function to ensure supported TLS versions are used in Postgres connection URI
func IsValidTLS(version string) bool {
switch version {
case
"TLSv1",
"TLSv1.1",
"TLSv1.2",
"TLSv1.3":
return true
}
return false
}

// NewDbConnectionArgsStruct creates a new DbConnectionArgsStruct
func NewDbConnectionArgsStruct(fullConfig map[string]interface{}) (*DbConnectionArgsStruct, error) {
newDbConnectionArgsStruct := &DbConnectionArgsStruct{}
Expand Down Expand Up @@ -89,6 +118,90 @@ func NewDbConnectionArgsStruct(fullConfig map[string]interface{}) (*DbConnection
return newDbConnectionArgsStruct, errors.New("sslrootcert must be of type string")
}
}
if value, ok := fullConfig["sslcert"]; ok {
newDbConnectionArgsStruct.SslCert, ok = value.(string)
if !ok {
return newDbConnectionArgsStruct, errors.New("sslcert must be of type string")
}
}
if value, ok := fullConfig["sslkey"]; ok {
newDbConnectionArgsStruct.SslKey, ok = value.(string)
if !ok {
return newDbConnectionArgsStruct, errors.New("sslkey must be of type string")
}
}
if value, ok := fullConfig["sslsni"]; ok {
newDbConnectionArgsStruct.SslSni, ok = value.(string)
if !ok {
return newDbConnectionArgsStruct, errors.New("sslsni must be of type string")
}
}
if value, ok := fullConfig["ssl_min_protocolversion"]; ok {
newDbConnectionArgsStruct.SslMinProtocolVersion, ok = value.(string)
if !ok {
return newDbConnectionArgsStruct, errors.New("ssl_min_protoclversion must be of type string")
}
if !IsValidTLS(value.(string)) {
return newDbConnectionArgsStruct, errors.New("ssl_min_protoclversion invalid value (supported TLSv1,TLSv1.1-TLSv1.3)")
}
}
if value, ok := fullConfig["ssl_max_protocolversion"]; ok {
newDbConnectionArgsStruct.SslMaxProtocolVersion, ok = value.(string)
if !ok {
return newDbConnectionArgsStruct, errors.New("ssl_max_protoclversion must be of type string")
}
if !IsValidTLS(value.(string)) {
return newDbConnectionArgsStruct, errors.New("ssl_max_protoclversion invalid value (supported TLSv1,TLSv1.1-TLSv1.3)")
}
}
if value, ok := fullConfig["sslcrl"]; ok {
newDbConnectionArgsStruct.SslCrl, ok = value.(string)
if !ok {
return newDbConnectionArgsStruct, errors.New("sslcrl must be of type string")
}
}
if value, ok := fullConfig["sslcrldir"]; ok {
newDbConnectionArgsStruct.SslCrlDir, ok = value.(string)
if !ok {
return newDbConnectionArgsStruct, errors.New("sslcrldir must be of type string")
}
}
if value, ok := fullConfig["sslcompression"]; ok {
newDbConnectionArgsStruct.SslCompression, ok = value.(int)
if !ok {
return newDbConnectionArgsStruct, errors.New("sslcompression must be of type int")
}
}
if value, ok := fullConfig["keepalives"]; ok {
newDbConnectionArgsStruct.Keepalives, ok = value.(int)
if !ok {
return newDbConnectionArgsStruct, errors.New("keepalives must be of type int")
}
}
if value, ok := fullConfig["keepalives_idle"]; ok {
newDbConnectionArgsStruct.KeepalivesIdle, ok = value.(int)
if !ok {
return newDbConnectionArgsStruct, errors.New("keepalives_idle must be of type int")
}
}
if value, ok := fullConfig["keepalives_interval"]; ok {
newDbConnectionArgsStruct.KeepalivesInterval, ok = value.(int)
if !ok {
return newDbConnectionArgsStruct, errors.New("keepalives_interval must be of type int")
}
}
if value, ok := fullConfig["keepalives_count"]; ok {
newDbConnectionArgsStruct.KeepalivesCount, ok = value.(int)
if !ok {
return newDbConnectionArgsStruct, errors.New("keepalives_count must be of type int")
}
}
if value, ok := fullConfig["tcp_user_timeout"]; ok {
newDbConnectionArgsStruct.TcpUserTimeout, ok = value.(int)
if !ok {
return newDbConnectionArgsStruct, errors.New("tcp_user_timeout must be of type int")
}
}

return newDbConnectionArgsStruct, nil
}
Expand Down
56 changes: 30 additions & 26 deletions pkg/lib/fieldgroups/database/database_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,35 +32,39 @@ func (fg *DatabaseFieldGroup) Validate(opts shared.Options) []shared.ValidationE
}

sslrootcertTmpPath := fg.DbConnectionArgs.SslRootCert
if fg.DbConnectionArgs.SslMode == "verify-full" || fg.DbConnectionArgs.SslMode == "verify-ca" {
// Write database.pem needed for db validation, if any, to a temp file
tmpCert, err := ioutil.TempFile("/tmp", "database.*.pem")
if err != nil {
newError := shared.ValidationError{
Tags: []string{"DB_URI"},
FieldGroup: fgName,
Message: "Could write database certificate to temporary file. Error: " + err.Error(),
if fg.DbConnectionArgs.SslRootCert == "" {
if fg.DbConnectionArgs.SslMode == "verify-full" || fg.DbConnectionArgs.SslMode == "verify-ca" {
// Write database.pem needed for db validation, if any, to a temp file
tmpCert, err := ioutil.TempFile("/tmp", "database.*.pem")
if err != nil {
newError := shared.ValidationError{
Tags: []string{"DB_URI"},
FieldGroup: fgName,
Message: "Could write database certificate to temporary file. Error: " + err.Error(),
}
errors = append(errors, newError)
return errors
}
errors = append(errors, newError)
return errors
}

defer func() {
tmpCert.Close()
os.Remove(tmpCert.Name())
}()

if _, err := tmpCert.Write(opts.Certificates["database.pem"]); err != nil {
newError := shared.ValidationError{
Tags: []string{"DB_URI"},
FieldGroup: fgName,
Message: "Could write database certificate to temporary file. Error: " + err.Error(),

defer func() {
tmpCert.Close()
os.Remove(tmpCert.Name())
}()

if _, err := tmpCert.Write(opts.Certificates["database.pem"]); err != nil {
newError := shared.ValidationError{
Tags: []string{"DB_URI"},
FieldGroup: fgName,
Message: "Could write database certificate to temporary file. Error: " + err.Error(),
}
errors = append(errors, newError)
return errors
}
errors = append(errors, newError)
return errors

sslrootcertTmpPath = tmpCert.Name()
}

sslrootcertTmpPath = tmpCert.Name()
} else {
sslrootcertTmpPath = fg.DbConnectionArgs.SslRootCert
}

// Connect to database
Expand Down