Skip to content

Commit 0314940

Browse files
authored
Merge pull request #5147 from anynines/jetstream-Consume-SSL-certs-for-database-connection-via-VCAP-ENV-variable
Consume SSL certs provided as VCAP ENV var for secure db connection Weill provide in a dev release
2 parents 045cd27 + b9bed42 commit 0314940

File tree

2 files changed

+68
-2
lines changed

2 files changed

+68
-2
lines changed

src/jetstream/datastore/database_cf_config.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/json"
55
"errors"
66
"fmt"
7+
"os"
78
"regexp"
89
"strconv"
910
"strings"
@@ -77,13 +78,35 @@ func findDatabaseConfig(vcapServices map[string][]VCAPService, db *DatabaseConfi
7778
// 1) Check db config in credentials
7879
db.Username = getDBCredentialsValue(dbCredentials["username"])
7980
db.Password = getDBCredentialsValue(dbCredentials["password"])
80-
db.Host = getDBCredentialsValue(dbCredentials["hostname"])
81+
db.Host = getDBCredentialsValue(dbCredentials["host"])
8182
db.SSLMode = env.String("DB_SSL_MODE", "disable")
8283
db.Port, _ = strconv.Atoi(getDBCredentialsValue(dbCredentials["port"]))
8384
// Note - Both isPostgresService and isMySQLService look at the credentials uri & tags
8485
if isPostgresService(service) {
8586
db.DatabaseProvider = "pgsql"
86-
db.Database = getDBCredentialsValue(dbCredentials["dbname"])
87+
db.Database = getDBCredentialsValue(dbCredentials["name"])
88+
if db.SSLMode == string(SSLVerifyCA) {
89+
log.Infof("Attempting to use SSL for database connection")
90+
tempFile, err := os.CreateTemp("", "postgres-ssl-*.crt")
91+
if err != nil {
92+
log.Warnf("Failed store Cloud Foundry service certificate in temp file; could not create temp file: %s", err.Error())
93+
return false
94+
}
95+
96+
_, err = tempFile.WriteString(getDBCredentialsValue(dbCredentials["cacrt"]))
97+
if err != nil {
98+
log.Warnf("Failed store Cloud Foundry service certificate in temp file; could not write to temp file: %s", err.Error())
99+
return false
100+
}
101+
102+
err = tempFile.Close()
103+
if err != nil {
104+
log.Warnf("Failed store Cloud Foundry service certificate in temp file; could not save temp file after writing: %s", err.Error())
105+
return false
106+
}
107+
108+
db.SSLRootCertificate = tempFile.Name()
109+
}
87110
} else if isMySQLService(service) {
88111
db.DatabaseProvider = "mysql"
89112
db.Database = getDBCredentialsValue(dbCredentials["name"])

src/jetstream/datastore/datastore_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
"gopkg.in/DATA-DOG/go-sqlmock.v1"
1212

13+
"github.com/govau/cf-common/env"
1314
. "github.com/smartystreets/goconvey/convey"
1415
)
1516

@@ -24,6 +25,7 @@ func TestDatastore(t *testing.T) {
2425
mockPort = 5432
2526
mockSSLModeDisable = "disable"
2627
mockSSLModeRequire = "require"
28+
mockSSLModeVerifyCA = "verify-ca"
2729
mockConnTimeout = 5
2830
mockSSLCertificate = "ssl-certificate-related-stuff"
2931
mockSSLKey = "ssl-key-related-stuff"
@@ -411,4 +413,45 @@ func TestDatastore(t *testing.T) {
411413
})
412414
})
413415
})
416+
417+
Convey("Given the requirement for the connection to be TLS secured", t, func() {
418+
var mockDatabaseConfigSSL = DatabaseConfig{
419+
DatabaseProvider: mockDatabaseProvider,
420+
Username: mockUsername,
421+
Password: mockPassword,
422+
Database: mockDatabase,
423+
Host: mockHost,
424+
Port: mockPort,
425+
SSLMode: mockSSLModeRequire,
426+
ConnectionTimeoutInSecs: mockConnTimeout,
427+
SSLRootCertificate: mockSSLRootCertificate,
428+
}
429+
430+
mockEnvVarsMap := make(map[string]string)
431+
mockEnvVarsMap["DB_SSL_MODE"] = mockSSLModeVerifyCA
432+
mockEnvVarsMap["VCAP_SERVICES"] = `{"cf-postgresql-service": [ { "name": "mock-stratos-ssl", "credentials": { "cacrt": "mockcert", "host": "mockhost", "name": "mockname", "password": "mockpassword", "port": 5432, "username": "mockusername", "uri": "postgres://mockuser:mockpassword@mockhost:5432/mockname" } } ] }`
433+
434+
mockVarSet := env.NewVarSet(env.WithMapLookup(mockEnvVarsMap))
435+
436+
db, _, err := sqlmock.New()
437+
if err != nil {
438+
t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
439+
}
440+
defer db.Close()
441+
442+
Convey("when the cloudfoundry database config is present", func() {
443+
444+
Convey("err will be nil", func() {
445+
_, err := ParseCFEnvs(&mockDatabaseConfigSSL, mockVarSet)
446+
So(err, ShouldBeNil)
447+
So(mockDatabaseConfigSSL.SSLRootCertificate, ShouldContainSubstring, "postgres-ssl-")
448+
So(mockDatabaseConfigSSL.SSLRootCertificate, ShouldEndWith, ".crt")
449+
So(mockDatabaseConfigSSL.Username, ShouldEqual, "mockusername")
450+
So(mockDatabaseConfigSSL.Password, ShouldEqual, "mockpassword")
451+
So(mockDatabaseConfigSSL.Database, ShouldEqual, "mockname")
452+
So(mockDatabaseConfigSSL.Host, ShouldEqual, "mockhost")
453+
So(mockDatabaseConfigSSL.SSLMode, ShouldEqual, mockSSLModeVerifyCA)
454+
})
455+
})
456+
})
414457
}

0 commit comments

Comments
 (0)