ce5be947b4
Revert fixture fixes for postgres Use postgres connection string with spaces instead of url Fix label order Make postgres tests in ci less verbose Add sequence update script Skip resets in postgres Remove option to skip resets in postgres Make postgres tests in ci verboseq Update test fixtures database Fix file tests on postgres Add postgres options to sample config Make sure tests init test fixtures before running the actual tests Fix issues with IDs too big to fit in an int Fix duplicate auto incremented IDs Refactor / Fix team tests Refactor team member tests Fix team member create Fix label test Fix getting labels Fix test fixtures for postgresql Fix connection string params Disable ssl mode on postgres integration tests Disable ssl mode on postgres tests Use sprintf to create the connection string for postgresql fixup! Add postgres support Add postgres support Added generate as a make dependency for make build Clarify docs on building Co-authored-by: kolaente <k@knt.li> Co-authored-by: Jan Tojnar <jtojnar@gmail.com> Reviewed-on: https://kolaente.dev/vikunja/api/pulls/135
175 lines
5.4 KiB
Go
175 lines
5.4 KiB
Go
package pq
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"io/ioutil"
|
|
"net"
|
|
"os"
|
|
"os/user"
|
|
"path/filepath"
|
|
)
|
|
|
|
// ssl generates a function to upgrade a net.Conn based on the "sslmode" and
|
|
// related settings. The function is nil when no upgrade should take place.
|
|
func ssl(o values) (func(net.Conn) (net.Conn, error), error) {
|
|
verifyCaOnly := false
|
|
tlsConf := tls.Config{}
|
|
switch mode := o["sslmode"]; mode {
|
|
// "require" is the default.
|
|
case "", "require":
|
|
// We must skip TLS's own verification since it requires full
|
|
// verification since Go 1.3.
|
|
tlsConf.InsecureSkipVerify = true
|
|
|
|
// From http://www.postgresql.org/docs/current/static/libpq-ssl.html:
|
|
//
|
|
// Note: For backwards compatibility with earlier versions of
|
|
// PostgreSQL, if a root CA file exists, the behavior of
|
|
// sslmode=require will be the same as that of verify-ca, meaning the
|
|
// server certificate is validated against the CA. Relying on this
|
|
// behavior is discouraged, and applications that need certificate
|
|
// validation should always use verify-ca or verify-full.
|
|
if sslrootcert, ok := o["sslrootcert"]; ok {
|
|
if _, err := os.Stat(sslrootcert); err == nil {
|
|
verifyCaOnly = true
|
|
} else {
|
|
delete(o, "sslrootcert")
|
|
}
|
|
}
|
|
case "verify-ca":
|
|
// We must skip TLS's own verification since it requires full
|
|
// verification since Go 1.3.
|
|
tlsConf.InsecureSkipVerify = true
|
|
verifyCaOnly = true
|
|
case "verify-full":
|
|
tlsConf.ServerName = o["host"]
|
|
case "disable":
|
|
return nil, nil
|
|
default:
|
|
return nil, fmterrorf(`unsupported sslmode %q; only "require" (default), "verify-full", "verify-ca", and "disable" supported`, mode)
|
|
}
|
|
|
|
err := sslClientCertificates(&tlsConf, o)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = sslCertificateAuthority(&tlsConf, o)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Accept renegotiation requests initiated by the backend.
|
|
//
|
|
// Renegotiation was deprecated then removed from PostgreSQL 9.5, but
|
|
// the default configuration of older versions has it enabled. Redshift
|
|
// also initiates renegotiations and cannot be reconfigured.
|
|
tlsConf.Renegotiation = tls.RenegotiateFreelyAsClient
|
|
|
|
return func(conn net.Conn) (net.Conn, error) {
|
|
client := tls.Client(conn, &tlsConf)
|
|
if verifyCaOnly {
|
|
err := sslVerifyCertificateAuthority(client, &tlsConf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return client, nil
|
|
}, nil
|
|
}
|
|
|
|
// sslClientCertificates adds the certificate specified in the "sslcert" and
|
|
// "sslkey" settings, or if they aren't set, from the .postgresql directory
|
|
// in the user's home directory. The configured files must exist and have
|
|
// the correct permissions.
|
|
func sslClientCertificates(tlsConf *tls.Config, o values) error {
|
|
// user.Current() might fail when cross-compiling. We have to ignore the
|
|
// error and continue without home directory defaults, since we wouldn't
|
|
// know from where to load them.
|
|
user, _ := user.Current()
|
|
|
|
// In libpq, the client certificate is only loaded if the setting is not blank.
|
|
//
|
|
// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1036-L1037
|
|
sslcert := o["sslcert"]
|
|
if len(sslcert) == 0 && user != nil {
|
|
sslcert = filepath.Join(user.HomeDir, ".postgresql", "postgresql.crt")
|
|
}
|
|
// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1045
|
|
if len(sslcert) == 0 {
|
|
return nil
|
|
}
|
|
// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1050:L1054
|
|
if _, err := os.Stat(sslcert); os.IsNotExist(err) {
|
|
return nil
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
|
|
// In libpq, the ssl key is only loaded if the setting is not blank.
|
|
//
|
|
// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1123-L1222
|
|
sslkey := o["sslkey"]
|
|
if len(sslkey) == 0 && user != nil {
|
|
sslkey = filepath.Join(user.HomeDir, ".postgresql", "postgresql.key")
|
|
}
|
|
|
|
if len(sslkey) > 0 {
|
|
if err := sslKeyPermissions(sslkey); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
cert, err := tls.LoadX509KeyPair(sslcert, sslkey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
tlsConf.Certificates = []tls.Certificate{cert}
|
|
return nil
|
|
}
|
|
|
|
// sslCertificateAuthority adds the RootCA specified in the "sslrootcert" setting.
|
|
func sslCertificateAuthority(tlsConf *tls.Config, o values) error {
|
|
// In libpq, the root certificate is only loaded if the setting is not blank.
|
|
//
|
|
// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L950-L951
|
|
if sslrootcert := o["sslrootcert"]; len(sslrootcert) > 0 {
|
|
tlsConf.RootCAs = x509.NewCertPool()
|
|
|
|
cert, err := ioutil.ReadFile(sslrootcert)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !tlsConf.RootCAs.AppendCertsFromPEM(cert) {
|
|
return fmterrorf("couldn't parse pem in sslrootcert")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// sslVerifyCertificateAuthority carries out a TLS handshake to the server and
|
|
// verifies the presented certificate against the CA, i.e. the one specified in
|
|
// sslrootcert or the system CA if sslrootcert was not specified.
|
|
func sslVerifyCertificateAuthority(client *tls.Conn, tlsConf *tls.Config) error {
|
|
err := client.Handshake()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
certs := client.ConnectionState().PeerCertificates
|
|
opts := x509.VerifyOptions{
|
|
DNSName: client.ConnectionState().ServerName,
|
|
Intermediates: x509.NewCertPool(),
|
|
Roots: tlsConf.RootCAs,
|
|
}
|
|
for i, cert := range certs {
|
|
if i == 0 {
|
|
continue
|
|
}
|
|
opts.Intermediates.AddCert(cert)
|
|
}
|
|
_, err = certs[0].Verify(opts)
|
|
return err
|
|
}
|