add tls support for db connection

This commit is contained in:
tsmethurst 2021-07-19 16:26:39 +02:00
commit 66ea26ced2
5 changed files with 134 additions and 37 deletions

View file

@ -117,6 +117,18 @@ func main() {
Value: defaults.DbDatabase, Value: defaults.DbDatabase,
EnvVars: []string{envNames.DbDatabase}, EnvVars: []string{envNames.DbDatabase},
}, },
&cli.StringFlag{
Name: flagNames.DbTLSMode,
Usage: "Database tls mode",
Value: defaults.DBTlsMode,
EnvVars: []string{envNames.DbTLSMode},
},
&cli.StringFlag{
Name: flagNames.DbTLSCACert,
Usage: "Path to CA cert for db tls connection",
Value: defaults.DBTlsCACert,
EnvVars: []string{envNames.DbTLSCACert},
},
// TEMPLATE FLAGS // TEMPLATE FLAGS
&cli.StringFlag{ &cli.StringFlag{

View file

@ -165,6 +165,14 @@ func (c *Config) ParseCLIFlags(f KeyedFlags, version string) error {
c.DBConfig.Database = f.String(fn.DbDatabase) c.DBConfig.Database = f.String(fn.DbDatabase)
} }
if c.DBConfig.TLSMode == DBTLSModeUnset || f.IsSet(fn.DbTLSMode) {
c.DBConfig.TLSMode = DBTLSMode(f.String(fn.DbTLSMode))
}
if c.DBConfig.TLSCACert == "" || f.IsSet(fn.DbTLSCACert) {
c.DBConfig.TLSCACert = f.String(fn.DbTLSCACert)
}
// template flags // template flags
if c.TemplateConfig.BaseDir == "" || f.IsSet(fn.TemplateBaseDir) { if c.TemplateConfig.BaseDir == "" || f.IsSet(fn.TemplateBaseDir) {
c.TemplateConfig.BaseDir = f.String(fn.TemplateBaseDir) c.TemplateConfig.BaseDir = f.String(fn.TemplateBaseDir)
@ -290,6 +298,8 @@ type Flags struct {
DbUser string DbUser string
DbPassword string DbPassword string
DbDatabase string DbDatabase string
DbTLSMode string
DbTLSCACert string
TemplateBaseDir string TemplateBaseDir string
AssetBaseDir string AssetBaseDir string
@ -335,6 +345,8 @@ type Defaults struct {
DbUser string DbUser string
DbPassword string DbPassword string
DbDatabase string DbDatabase string
DBTlsMode string
DBTlsCACert string
TemplateBaseDir string TemplateBaseDir string
AssetBaseDir string AssetBaseDir string
@ -381,6 +393,8 @@ func GetFlagNames() Flags {
DbUser: "db-user", DbUser: "db-user",
DbPassword: "db-password", DbPassword: "db-password",
DbDatabase: "db-database", DbDatabase: "db-database",
DbTLSMode: "db-tls-mode",
DbTLSCACert: "db-tls-ca-cert",
TemplateBaseDir: "template-basedir", TemplateBaseDir: "template-basedir",
AssetBaseDir: "asset-basedir", AssetBaseDir: "asset-basedir",
@ -428,6 +442,8 @@ func GetEnvNames() Flags {
DbUser: "GTS_DB_USER", DbUser: "GTS_DB_USER",
DbPassword: "GTS_DB_PASSWORD", DbPassword: "GTS_DB_PASSWORD",
DbDatabase: "GTS_DB_DATABASE", DbDatabase: "GTS_DB_DATABASE",
DbTLSMode: "GTS_DB_TLS_MODE",
DbTLSCACert: "GTS_DB_CA_CERT",
TemplateBaseDir: "GTS_TEMPLATE_BASEDIR", TemplateBaseDir: "GTS_TEMPLATE_BASEDIR",
AssetBaseDir: "GTS_ASSET_BASEDIR", AssetBaseDir: "GTS_ASSET_BASEDIR",

View file

@ -27,4 +27,23 @@ type DBConfig struct {
Password string `yaml:"password"` Password string `yaml:"password"`
Database string `yaml:"database"` Database string `yaml:"database"`
ApplicationName string `yaml:"applicationName"` ApplicationName string `yaml:"applicationName"`
TLSMode DBTLSMode `yaml:"tlsMode"`
TLSCACert string `yaml:"tlsCACert"`
} }
// DBTLSMode describes a mode of connecting to a database with or without TLS.
type DBTLSMode string
// DBTLSModeDisable does not attempt to make a TLS connection to the database.
var DBTLSModeDisable DBTLSMode = "disable"
// DBTLSModeEnable attempts to make a TLS connection to the database, but doesn't fail if
// the certificate passed by the database isn't verified.
var DBTLSModeEnable DBTLSMode = "enable"
// DBTLSModeRequire attempts to make a TLS connection to the database, and requires
// that the certificate presented by the database is valid.
var DBTLSModeRequire DBTLSMode = "require"
// DBTLSModeUnset means that the TLS mode has not been set.
var DBTLSModeUnset DBTLSMode = ""

View file

@ -126,6 +126,8 @@ func GetDefaults() Defaults {
DbUser: "postgres", DbUser: "postgres",
DbPassword: "postgres", DbPassword: "postgres",
DbDatabase: "postgres", DbDatabase: "postgres",
DBTlsMode: "disable",
DBTlsCACert: "",
TemplateBaseDir: "./web/template/", TemplateBaseDir: "./web/template/",
AssetBaseDir: "./web/assets/", AssetBaseDir: "./web/assets/",

View file

@ -22,10 +22,14 @@ import (
"context" "context"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"errors" "errors"
"fmt" "fmt"
"net" "net"
"net/mail" "net/mail"
"os"
"strings" "strings"
"time" "time"
@ -133,6 +137,49 @@ func derivePGOptions(c *config.Config) (*pg.Options, error) {
return nil, errors.New("no database set") return nil, errors.New("no database set")
} }
var tlsConfig *tls.Config
switch c.DBConfig.TLSMode {
case config.DBTLSModeDisable, config.DBTLSModeUnset:
break // nothing to do
case config.DBTLSModeEnable:
tlsConfig = &tls.Config{
InsecureSkipVerify: true,
}
case config.DBTLSModeRequire:
tlsConfig = &tls.Config{
InsecureSkipVerify: false,
}
}
if tlsConfig != nil && c.DBConfig.TLSCACert != "" {
// load the system cert pool first -- we'll append the given CA cert to this
certPool, err := x509.SystemCertPool()
if err != nil {
return nil, fmt.Errorf("error fetching system CA cert pool: %s", err)
}
caCertBytes, err := os.ReadFile(c.DBConfig.TLSCACert)
if err != nil {
return nil, fmt.Errorf("error opening CA certificate at %s: %s", c.DBConfig.TLSCACert, err)
}
if len(caCertBytes) == 0 {
return nil, fmt.Errorf("ca cert at %s was empty", c.DBConfig.TLSCACert)
}
caPem, _ := pem.Decode(caCertBytes)
if caPem == nil {
return nil, fmt.Errorf("could not parse cert at %s into PEM", c.DBConfig.TLSCACert)
}
caCert, err := x509.ParseCertificate(caPem.Bytes)
if err != nil {
return nil, fmt.Errorf("could not parse cert at %s into x509 certificate: %s", c.DBConfig.TLSCACert, err)
}
certPool.AddCert(caCert)
}
// We can rely on the pg library we're using to set // We can rely on the pg library we're using to set
// sensible defaults for everything we don't set here. // sensible defaults for everything we don't set here.
options := &pg.Options{ options := &pg.Options{
@ -141,6 +188,7 @@ func derivePGOptions(c *config.Config) (*pg.Options, error) {
Password: c.DBConfig.Password, Password: c.DBConfig.Password,
Database: c.DBConfig.Database, Database: c.DBConfig.Database,
ApplicationName: c.ApplicationName, ApplicationName: c.ApplicationName,
TLSConfig: tlsConfig,
} }
return options, nil return options, nil