Skip to content

Commit

Permalink
feat: support Cloud SQL CAS instances. (#850)
Browse files Browse the repository at this point in the history
* feat: add support for CAS instances.

* fix: remove an extra curly brace.

* feat: upgrade google.golang.org/api to v0.190.0 for the new server_ca_mode field

* fix: format the code to fix lint issue.

* fix: fix some bugs in tests.

* fix: fix a typo in tests.

* fix: set instance name for CAS unit tests.

Also renamed gotDNS to gotPSCDNS to make it clear that the DNS from PSC address.

* fix: enable mocked SQL admin client to pass server_ca_mode too.

* refactor: resolve some review comments.

* doc: add godoc for the usage of the DNS name.

* doc: fix a few typos in comments.

* test: add e2e test for CAS instances.

* test: fix an syntax error.

* test: add more accurate descriptions for CAS instance tests.

* test: use a separate test for connections to the CAS instances.
  • Loading branch information
feng-zhe authored Aug 8, 2024
1 parent 0a2dfb9 commit 511fae4
Show file tree
Hide file tree
Showing 10 changed files with 294 additions and 65 deletions.
2 changes: 2 additions & 0 deletions dialer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,8 @@ func TestDialerRemovesInvalidInstancesFromCache(t *testing.T) {
info: cloudsql.NewConnectionInfo(
instance.ConnName{},
"",
"GOOGLE_MANAGED_INTERNAL_CA",
"",
map[string]string{
// no public IP
cloudsql.PrivateIP: "10.0.0.1",
Expand Down
64 changes: 59 additions & 5 deletions e2e_postgres_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,27 @@ import (
)

var (
postgresConnName = os.Getenv("POSTGRES_CONNECTION_NAME") // "Cloud SQL Postgres instance connection name, in the form of 'project:region:instance'.
postgresUser = os.Getenv("POSTGRES_USER") // Name of database user.
postgresPass = os.Getenv("POSTGRES_PASS") // Password for the database user; be careful when entering a password on the command line (it may go into your terminal's history).
postgresDB = os.Getenv("POSTGRES_DB") // Name of the database to connect to.
postgresUserIAM = os.Getenv("POSTGRES_USER_IAM") // Name of database IAM user.
postgresConnName = os.Getenv("POSTGRES_CONNECTION_NAME") // "Cloud SQL Postgres instance connection name, in the form of 'project:region:instance'.
postgresCASConnName = os.Getenv("POSTGRES_CAS_CONNECTION_NAME") // "Cloud SQL Postgres CAS instance connection name, in the form of 'project:region:instance'.
postgresUser = os.Getenv("POSTGRES_USER") // Name of database user.
postgresPass = os.Getenv("POSTGRES_PASS") // Password for the database user; be careful when entering a password on the command line (it may go into your terminal's history).
postgresCASPass = os.Getenv("POSTGRES_CAS_PASS") // Password for the database user for CAS instances; be careful when entering a password on the command line (it may go into your terminal's history).
postgresDB = os.Getenv("POSTGRES_DB") // Name of the database to connect to.
postgresUserIAM = os.Getenv("POSTGRES_USER_IAM") // Name of database IAM user.
)

func requirePostgresVars(t *testing.T) {
switch "" {
case postgresConnName:
t.Fatal("'POSTGRES_CONNECTION_NAME' env var not set")
case postgresCASConnName:
t.Fatal("'POSTGRES_CAS_CONNECTION_NAME' env var not set")
case postgresUser:
t.Fatal("'POSTGRES_USER' env var not set")
case postgresPass:
t.Fatal("'POSTGRES_PASS' env var not set")
case postgresCASPass:
t.Fatal("'POSTGRES_CAS_PASS' env var not set")
case postgresDB:
t.Fatal("'POSTGRES_DB' env var not set")
case postgresUserIAM:
Expand Down Expand Up @@ -107,6 +113,54 @@ func TestPostgresPgxPoolConnect(t *testing.T) {
t.Log(now)
}

func TestPostgresCASConnect(t *testing.T) {
if testing.Short() {
t.Skip("skipping Postgres integration tests")
}
requirePostgresVars(t)

ctx := context.Background()

// Configure the driver to connect to the database
dsn := fmt.Sprintf("user=%s password=%s dbname=%s sslmode=disable", postgresUser, postgresCASPass, postgresDB)
config, err := pgxpool.ParseConfig(dsn)
if err != nil {
t.Fatalf("failed to parse pgx config: %v", err)
}

// Create a new dialer with any options
d, err := cloudsqlconn.NewDialer(ctx)
if err != nil {
t.Fatalf("failed to init Dialer: %v", err)
}

// call cleanup when you're done with the database connection to close dialer
cleanup := func() error { return d.Close() }

// Tell the driver to use the Cloud SQL Go Connector to create connections
// postgresConnName takes the form of 'project:region:instance'.
config.ConnConfig.DialFunc = func(ctx context.Context, _ string, _ string) (net.Conn, error) {
return d.Dial(ctx, postgresCASConnName)
}

// Interact with the driver directly as you normally would
pool, err := pgxpool.NewWithConfig(ctx, config)
if err != nil {
t.Fatalf("failed to create pool: %s", err)
}
// ... etc

defer cleanup()
defer pool.Close()

var now time.Time
err = pool.QueryRow(context.Background(), "SELECT NOW()").Scan(&now)
if err != nil {
t.Fatalf("QueryRow failed: %s", err)
}
t.Log(now)
}

func TestPostgresConnectWithIAMUser(t *testing.T) {
if testing.Short() {
t.Skip("skipping Postgres integration tests")
Expand Down
17 changes: 8 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,25 @@ require (
golang.org/x/net v0.27.0
golang.org/x/oauth2 v0.21.0
golang.org/x/time v0.5.0
google.golang.org/api v0.188.0
google.golang.org/genproto/googleapis/rpc v0.0.0-20240709173604-40e1e62336c5
google.golang.org/api v0.190.0
google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf
google.golang.org/grpc v1.64.1
)

require (
cloud.google.com/go/auth v0.7.0 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect
cloud.google.com/go/compute/metadata v0.4.0 // indirect
cloud.google.com/go/auth v0.7.3 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.3 // indirect
cloud.google.com/go/compute/metadata v0.5.0 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/google/s2a-go v0.1.8 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.5 // indirect
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.14.3 // indirect
github.com/jackc/pgio v1.0.0 // indirect
Expand Down
38 changes: 18 additions & 20 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go/auth v0.7.0 h1:kf/x9B3WTbBUHkC+1VS8wwwli9TzhSt0vSTVBmMR8Ts=
cloud.google.com/go/auth v0.7.0/go.mod h1:D+WqdrpcjmiCgWrXmLLxOVq1GACoE36chW6KXoEvuIw=
cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4=
cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q=
cloud.google.com/go/compute/metadata v0.4.0 h1:vHzJCWaM4g8XIcm8kopr3XmDA4Gy/lblD3EhhSux05c=
cloud.google.com/go/compute/metadata v0.4.0/go.mod h1:SIQh1Kkb4ZJ8zJ874fqVkslA29PRXuleyj6vOzlbK7M=
cloud.google.com/go/auth v0.7.3 h1:98Vr+5jMaCZ5NZk6e/uBgf60phTk/XN84r8QEWB9yjY=
cloud.google.com/go/auth v0.7.3/go.mod h1:HJtWUx1P5eqjy/f6Iq5KeytNpbAcGolPhOgyop2LlzA=
cloud.google.com/go/auth/oauth2adapt v0.2.3 h1:MlxF+Pd3OmSudg/b1yZ5lJwoXCEaeedAguodky1PcKI=
cloud.google.com/go/auth/oauth2adapt v0.2.3/go.mod h1:tMQXOfZzFuNuUxOypHlQEXgdfX5cuhwU+ffUuXRJE8I=
cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 h1:lGlwhPtrX6EVml1hO0ivjkUxsSyl4dsiw9qcA1k/3IQ=
Expand Down Expand Up @@ -36,8 +36,8 @@ github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
Expand All @@ -64,8 +64,6 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
Expand All @@ -74,15 +72,15 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
github.com/googleapis/gax-go/v2 v2.12.5 h1:8gw9KZK8TiVKB6q3zHY3SBzLnrGp6HQjyfYBYGmXdxA=
github.com/googleapis/gax-go/v2 v2.12.5/go.mod h1:BUDKcWo+RaKq5SC9vVYL0wLADa3VcfswbOMMRmB9H3E=
github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s=
github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
Expand Down Expand Up @@ -288,17 +286,17 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.188.0 h1:51y8fJ/b1AaaBRJr4yWm96fPcuxSo0JcegXE3DaHQHw=
google.golang.org/api v0.188.0/go.mod h1:VR0d+2SIiWOYG3r/jdm7adPW9hI2aRv9ETOSCQ9Beag=
google.golang.org/api v0.190.0 h1:ASM+IhLY1zljNdLu19W1jTmU6A+gMk6M46Wlur61s+Q=
google.golang.org/api v0.190.0/go.mod h1:QIr6I9iedBLnfqoD6L6Vze1UvS5Hzj5r2aUBOaZnLHo=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20240708141625-4ad9e859172b h1:dSTjko30weBaMj3eERKc0ZVXW4GudCswM3m+P++ukU0=
google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3 h1:QW9+G6Fir4VcRXVH8x3LilNAb6cxBGLa6+GM4hRwexE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240709173604-40e1e62336c5 h1:SbSDUWW1PAO24TNpLdeheoYPd7kllICcLU52x6eD4kQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240709173604-40e1e62336c5/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
google.golang.org/genproto v0.0.0-20240730163845-b1a4ccb954bf h1:OqdXDEakZCVtDiZTjcxfwbHPCT11ycCEsTKesBVKvyY=
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d h1:kHjw/5UfflP/L5EbledDrcG4C2597RtymmGRZvHiCuY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf h1:liao9UHurZLtiEwBgT9LMOnKYsHze6eA6w1KQCMVN2Q=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
Expand Down
29 changes: 24 additions & 5 deletions internal/cloudsql/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,28 +170,36 @@ func (i *RefreshAheadCache) Close() error {
type ConnectionInfo struct {
ConnectionName instance.ConnName
ClientCertificate tls.Certificate
ServerCaCert *x509.Certificate
ServerCACert []*x509.Certificate
ServerCAMode string
DBVersion string
Expiration time.Time
// The DNSName is from the ConnectSettings API.
// It is used to validate the server identity of the CAS instances.
DNSName string
Expiration time.Time

addrs map[string]string
}

// NewConnectionInfo initializes a ConnectionInfo struct.
func NewConnectionInfo(
cn instance.ConnName,
dnsName string,
serverCAMode string,
version string,
ipAddrs map[string]string,
serverCaCert *x509.Certificate,
serverCACert []*x509.Certificate,
clientCert tls.Certificate,
) ConnectionInfo {
return ConnectionInfo{
addrs: ipAddrs,
ServerCaCert: serverCaCert,
ClientCertificate: clientCert,
ServerCACert: serverCACert,
ServerCAMode: serverCAMode,
Expiration: clientCert.Leaf.NotAfter,
DBVersion: version,
ConnectionName: cn,
DNSName: dnsName,
}
}

Expand Down Expand Up @@ -225,7 +233,18 @@ func (c ConnectionInfo) Addr(ipType string) (string, error) {
// TLSConfig constructs a TLS configuration for the given connection info.
func (c ConnectionInfo) TLSConfig() *tls.Config {
pool := x509.NewCertPool()
pool.AddCert(c.ServerCaCert)
for _, caCert := range c.ServerCACert {
pool.AddCert(caCert)
}
if c.ServerCAMode == "GOOGLE_MANAGED_CAS_CA" {
// For CAS instances, we can rely on the DNS name to verify the server identity.
return &tls.Config{
ServerName: c.DNSName,
Certificates: []tls.Certificate{c.ClientCertificate},
RootCAs: pool,
MinVersion: tls.VersionTLS13,
}
}
return &tls.Config{
ServerName: c.ConnectionName.String(),
Certificates: []tls.Certificate{c.ClientCertificate},
Expand Down
85 changes: 82 additions & 3 deletions internal/cloudsql/instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ func TestConnectionInfoTLSConfig(t *testing.T) {
t.Fatal(err)
}
b, _ = pem.Decode(certBytes)
serverCert, err := x509.ParseCertificate(b.Bytes)
serverCACert, err := x509.ParseCertificate(b.Bytes)
if err != nil {
t.Fatal(err)
}
Expand All @@ -172,7 +172,7 @@ func TestConnectionInfoTLSConfig(t *testing.T) {
PrivateKey: RSAKey,
Leaf: clientCert,
},
ServerCaCert: serverCert,
ServerCACert: []*x509.Certificate{serverCACert},
DBVersion: "doesn't matter here",
Expiration: clientCert.NotAfter,
}
Expand All @@ -198,7 +198,7 @@ func TestConnectionInfoTLSConfig(t *testing.T) {
}

verifyPeerCert := got.VerifyPeerCertificate
err = verifyPeerCert([][]byte{serverCert.Raw}, nil)
err = verifyPeerCert([][]byte{serverCACert.Raw}, nil)
if err != nil {
t.Fatalf("expected to verify peer cert, got error: %v", err)
}
Expand Down Expand Up @@ -375,3 +375,82 @@ func TestRefreshDuration(t *testing.T) {
})
}
}

func TestConnectionInfoTLSConfigForCAS(t *testing.T) {
cn := testInstanceConnName()
i := mock.NewFakeCSQLInstance(cn.Project(), cn.Region(), cn.Name())
// Generate a client certificate with the client's public key and signed by
// the server's private key
cert, err := i.ClientCert(&RSAKey.PublicKey)
if err != nil {
t.Fatal(err)
}
// Now parse the bytes back out as structured data
b, _ := pem.Decode(cert)
clientCert, err := x509.ParseCertificate(b.Bytes)
if err != nil {
t.Fatal(err)
}
// Create a PEM with two certificates to pretend the sub CA and root CA.
rootCABytes, err := mock.SelfSign(i.Cert, i.Key)
if err != nil {
t.Fatal(err)
}
b, _ = pem.Decode(rootCABytes)
rootCACert, err := x509.ParseCertificate(b.Bytes)
if err != nil {
t.Fatal(err)
}
subCABytes, err := mock.SelfSign(i.Cert, i.Key)
if err != nil {
t.Fatal(err)
}
b, _ = pem.Decode(subCABytes)
subCACert, err := x509.ParseCertificate(b.Bytes)
if err != nil {
t.Fatal(err)
}
caCerts := []*x509.Certificate{rootCACert, subCACert}
wantRootCAs := x509.NewCertPool()
wantRootCAs.AddCert(rootCACert)
wantRootCAs.AddCert(subCACert)
// Assemble a connection info with the raw and parsed client cert
// and the self-signed server certificate
wantServerName := "testing dns name"
ci := ConnectionInfo{
DNSName: wantServerName,
ClientCertificate: tls.Certificate{
Certificate: [][]byte{clientCert.Raw},
PrivateKey: RSAKey,
Leaf: clientCert,
},
ServerCACert: caCerts,
DBVersion: "doesn't matter here",
Expiration: clientCert.NotAfter,
ServerCAMode: "GOOGLE_MANAGED_CAS_CA",
}

got := ci.TLSConfig()

if got.ServerName != wantServerName {
t.Fatalf(
"ConnectInfo return unexpected server name in TLS Config, "+
"want = %v, got = %v",
wantServerName, got.ServerName,
)
}
if got.MinVersion != tls.VersionTLS13 {
t.Fatalf(
"want TLS 1.3, got = %v", got.MinVersion,
)
}
if got.Certificates[0].Leaf != ci.ClientCertificate.Leaf {
t.Fatal("leaf certificates do not match")
}
if got.InsecureSkipVerify {
t.Fatal("InsecureSkipVerify is true, expected false")
}
if !got.RootCAs.Equal(wantRootCAs) {
t.Fatalf("unexpected root CAs, got %v, want %v", got.RootCAs, wantRootCAs)
}
}
Loading

0 comments on commit 511fae4

Please sign in to comment.