Skip to content

Commit

Permalink
Merge pull request #364 from gatewayd-io/tls-termination-for-server
Browse files Browse the repository at this point in the history
TLS termination for incoming client connection
  • Loading branch information
mostafa authored Nov 5, 2023
2 parents 3f4b240 + 52d2455 commit aeef306
Show file tree
Hide file tree
Showing 31 changed files with 838 additions and 355 deletions.
19 changes: 16 additions & 3 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,27 @@ jobs:
EOF
- name: Run GatewayD with template plugin 🚀
run: GATEWAYD_LOGGERS_DEFAULT_LEVEL=debug ./gatewayd run &
run: ./gatewayd run -c testdata/gatewayd_tls.yaml &

- name: Run a test with PSQL 🧪
- name: Install PSQL 🧑‍💻
run: |
sudo apt-get update
sudo apt-get install --yes --no-install-recommends postgresql-client
- name: Run a test with PSQL over plaintext connection 🧪
run: |
psql ${PGURL} -c "CREATE TABLE test_table (id serial PRIMARY KEY, name varchar(255));" | grep CREATE || exit 1
psql ${PGURL} -c "INSERT INTO test_table (name) VALUES ('test');" | grep INSERT || exit 1
psql ${PGURL} -c "SELECT * FROM test_table;" | grep test || exit 1
psql ${PGURL} -c "DROP TABLE test_table;" | grep DROP || exit 1
env:
PGURL: postgres://postgres:postgres@localhost:15432/postgres?sslmode=disable

- name: Run a test with PSQL over TLS connection 🧪
run: |
psql ${PGURL_TLS} -c "CREATE TABLE test_table (id serial PRIMARY KEY, name varchar(255));" | grep CREATE || exit 1
psql ${PGURL_TLS} -c "INSERT INTO test_table (name) VALUES ('test');" | grep INSERT || exit 1
psql ${PGURL_TLS} -c "SELECT * FROM test_table;" | grep test || exit 1
psql ${PGURL_TLS} -c "DROP TABLE test_table;" | grep DROP || exit 1
env:
PGURL: postgres://postgres:postgres@localhost:15432/postgres
PGURL_TLS: postgres://postgres:postgres@localhost:15432/postgres?sslmode=require
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ dist/

# Tensorflow files
libtensorflow*

# Test generated files
cmd/test_plugins.yaml.bak
5 changes: 3 additions & 2 deletions cmd/cmd_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import (
)

var (
globalTestConfigFile = "./test_global.yaml"
pluginTestConfigFile = "./test_plugins.yaml"
globalTestConfigFile = "./test_global.yaml"
globalTLSTestConfigFile = "./testdata/gatewayd_tls.yaml"
pluginTestConfigFile = "./test_plugins.yaml"
)

// executeCommandC executes a cobra command and returns the command, output, and error.
Expand Down
8 changes: 8 additions & 0 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,10 @@ var runCmd = &cobra.Command{
logger,
pluginRegistry,
conf.Plugin.Timeout,
cfg.EnableTLS,
cfg.CertFile,
cfg.KeyFile,
cfg.HandshakeTimeout,
)

span.AddEvent("Create server", trace.WithAttributes(
Expand All @@ -669,6 +673,10 @@ var runCmd = &cobra.Command{
attribute.String("address", cfg.Address),
attribute.String("tickInterval", cfg.TickInterval.String()),
attribute.String("pluginTimeout", conf.Plugin.Timeout.String()),
attribute.Bool("enableTLS", cfg.EnableTLS),
attribute.String("certFile", cfg.CertFile),
attribute.String("keyFile", cfg.KeyFile),
attribute.String("handshakeTimeout", cfg.HandshakeTimeout.String()),
))

pluginTimeoutCtx, cancel = context.WithTimeout(
Expand Down
148 changes: 97 additions & 51 deletions cmd/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,23 @@ func Test_runCmd(t *testing.T) {
assert.FileExists(t, globalTestConfigFile, "configInitCmd should create a config file")

var waitGroup sync.WaitGroup

waitGroup.Add(1)
go func(waitGroup *sync.WaitGroup) {
// Test run command.
output := capturer.CaptureOutput(func() {
_, err := executeCommandC(rootCmd, "run", "-c", globalTestConfigFile, "-p", pluginTestConfigFile)
require.NoError(t, err, "run command should not have returned an error")
})
// Print the output for debugging purposes.
runCmd.Print(output)
// Check if GatewayD started and stopped correctly.
assert.Contains(t, output, "GatewayD is running")
assert.Contains(t, output, "Stopped all servers\n")

waitGroup.Done()
}(&waitGroup)

waitGroup.Add(1)
go func(waitGroup *sync.WaitGroup) {
time.Sleep(100 * time.Millisecond)
Expand All @@ -34,7 +51,7 @@ func Test_runCmd(t *testing.T) {
context.Background(),
nil,
nil,
nil,
metricsServer,
nil,
loggers[config.Default],
servers,
Expand All @@ -44,38 +61,15 @@ func Test_runCmd(t *testing.T) {
waitGroup.Done()
}(&waitGroup)

waitGroup.Add(1)
go func(waitGroup *sync.WaitGroup) {
// Test run command.
output := capturer.CaptureOutput(func() {
_, err := executeCommandC(rootCmd, "run", "-c", globalTestConfigFile, "-p", pluginTestConfigFile)
require.NoError(t, err, "run command should not have returned an error")
})
// Print the output for debugging purposes.
runCmd.Print(output)
// Check if GatewayD started and stopped correctly.
assert.Contains(t,
output,
"GatewayD is running",
"run command should have returned the correct output")
assert.Contains(t,
output,
"Stopped all servers\n",
"run command should have returned the correct output")

waitGroup.Done()
}(&waitGroup)

waitGroup.Wait()

// Clean up.
require.NoError(t, os.Remove(pluginTestConfigFile))
require.NoError(t, os.Remove(globalTestConfigFile))
}

// Test_runCmdWithMultiTenancy tests the run command with multi-tenancy enabled.
// Note: This test needs two instances of PostgreSQL running on ports 5432 and 5433.
func Test_runCmdWithMultiTenancy(t *testing.T) {
// Test_runCmdWithTLS tests the run command with TLS enabled on the server.
func Test_runCmdWithTLS(t *testing.T) {
// Create a test plugins config file.
_, err := executeCommandC(rootCmd, "plugin", "init", "--force", "-p", pluginTestConfigFile)
require.NoError(t, err, "plugin init command should not have returned an error")
Expand All @@ -84,15 +78,36 @@ func Test_runCmdWithMultiTenancy(t *testing.T) {
stopChan = make(chan struct{})

var waitGroup sync.WaitGroup
// TODO: Test client certificate authentication.

waitGroup.Add(1)
go func(waitGroup *sync.WaitGroup) {
time.Sleep(500 * time.Millisecond)
// Test run command.
output := capturer.CaptureOutput(func() {
_, err := executeCommandC(rootCmd, "run", "-c", globalTLSTestConfigFile, "-p", pluginTestConfigFile)
require.NoError(t, err, "run command should not have returned an error")
})

// Print the output for debugging purposes.
runCmd.Print(output)

// Check if GatewayD started and stopped correctly.
assert.Contains(t, output, "GatewayD is running")
assert.Contains(t, output, "TLS is enabled")
assert.Contains(t, output, "Stopped all servers\n")

waitGroup.Done()
}(&waitGroup)

waitGroup.Add(1)
go func(waitGroup *sync.WaitGroup) {
time.Sleep(100 * time.Millisecond)

StopGracefully(
context.Background(),
nil,
nil,
nil,
metricsServer,
nil,
loggers[config.Default],
servers,
Expand All @@ -102,6 +117,24 @@ func Test_runCmdWithMultiTenancy(t *testing.T) {
waitGroup.Done()
}(&waitGroup)

waitGroup.Wait()

// Clean up.
require.NoError(t, os.Remove(pluginTestConfigFile))
}

// Test_runCmdWithMultiTenancy tests the run command with multi-tenancy enabled.
// Note: This test needs two instances of PostgreSQL running on ports 5432 and 5433.
func Test_runCmdWithMultiTenancy(t *testing.T) {
// Create a test plugins config file.
_, err := executeCommandC(rootCmd, "plugin", "init", "--force", "-p", pluginTestConfigFile)
require.NoError(t, err, "plugin init command should not have returned an error")
assert.FileExists(t, pluginTestConfigFile, "plugin init command should have created a config file")

stopChan = make(chan struct{})

var waitGroup sync.WaitGroup

waitGroup.Add(1)
go func(waitGroup *sync.WaitGroup) {
// Test run command.
Expand All @@ -123,6 +156,24 @@ func Test_runCmdWithMultiTenancy(t *testing.T) {
waitGroup.Done()
}(&waitGroup)

waitGroup.Add(1)
go func(waitGroup *sync.WaitGroup) {
time.Sleep(500 * time.Millisecond)

StopGracefully(
context.Background(),
nil,
nil,
metricsServer,
nil,
loggers[config.Default],
servers,
stopChan,
)

waitGroup.Done()
}(&waitGroup)

waitGroup.Wait()

// Clean up.
Expand Down Expand Up @@ -164,6 +215,23 @@ func Test_runCmdWithCachePlugin(t *testing.T) {
assert.Contains(t, output, "Name: gatewayd-plugin-cache")

var waitGroup sync.WaitGroup

waitGroup.Add(1)
go func(waitGroup *sync.WaitGroup) {
// Test run command.
output := capturer.CaptureOutput(func() {
_, err := executeCommandC(rootCmd, "run", "-c", globalTestConfigFile, "-p", pluginTestConfigFile)
require.NoError(t, err, "run command should not have returned an error")
})
// Print the output for debugging purposes.
runCmd.Print(output)
// Check if GatewayD started and stopped correctly.
assert.Contains(t, output, "GatewayD is running")
assert.Contains(t, output, "Stopped all servers\n")

waitGroup.Done()
}(&waitGroup)

waitGroup.Add(1)
go func(waitGroup *sync.WaitGroup) {
time.Sleep(time.Second)
Expand All @@ -172,7 +240,7 @@ func Test_runCmdWithCachePlugin(t *testing.T) {
context.Background(),
nil,
nil,
nil,
metricsServer,
nil,
loggers[config.Default],
servers,
Expand All @@ -182,28 +250,6 @@ func Test_runCmdWithCachePlugin(t *testing.T) {
waitGroup.Done()
}(&waitGroup)

waitGroup.Add(1)
go func(waitGroup *sync.WaitGroup) {
// Test run command.
output := capturer.CaptureOutput(func() {
_, err := executeCommandC(rootCmd, "run", "-c", globalTestConfigFile, "-p", pluginTestConfigFile)
require.NoError(t, err, "run command should not have returned an error")
})
// Print the output for debugging purposes.
runCmd.Print(output)
// Check if GatewayD started and stopped correctly.
assert.Contains(t,
output,
"GatewayD is running",
"run command should have returned the correct output")
assert.Contains(t,
output,
"Stopped all servers\n",
"run command should have returned the correct output")

waitGroup.Done()
}(&waitGroup)

waitGroup.Wait()

// Clean up.
Expand Down
33 changes: 33 additions & 0 deletions cmd/testdata/gatewayd_tls.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# GatewayD Global Configuration

loggers:
default:
level: info
output: ["console"]
noColor: True

metrics:
default:
enabled: True

clients:
default:
address: localhost:5432

pools:
default:
size: 10

proxies:
default:
elastic: False

servers:
default:
address: 0.0.0.0:15432
enableTLS: True
certFile: testdata/localhost.crt
keyFile: testdata/localhost.key

api:
enabled: False
19 changes: 19 additions & 0 deletions cmd/testdata/localhost.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDDzCCAfegAwIBAgIUVsSdpPwgCHFdyFpWk5jfYP6jjNMwDQYJKoZIhvcNAQEL
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIzMTEwNTAxMzQzNloXDTIzMTIw
NTAxMzQzNlowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAt3IF8F5pROpjyhGacQBq0Q0a+rUyFOm5RxSgqFlfYLkY
9k8QwO7cCwWXF1BMCcXRkUnY2KtqEAhZtIcTRze7YAeyp7T13xdQJIxBIAmwU4Zz
8Z8rALps9PhfBtjHsLEA+2FoCYK9aaFPTrrYzaJQHnBbomWn6sPxFNgB7rEdUSlw
nt9krpo3oqSx0csl+SXdHp9FQjQNKVnBjvRD5Syim6kaGP2rNAgQB6eNbzNEbNBp
RdiOaU9edwbFiy08kCv7E2fV/fSfMu1jixFC55EPsIomPgah7lCBNACxQpJCbncM
rQTt5+VEpJf87BqMIDZ6qpsVgjM0w66EvxXTdc6f4QIDAQABo1kwVzAUBgNVHREE
DTALgglsb2NhbGhvc3QwCwYDVR0PBAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMB
MB0GA1UdDgQWBBQi14cdty4GZc82KEKWMjzFqM0+jDANBgkqhkiG9w0BAQsFAAOC
AQEAWQ+kgucGvHmUjTjYFGGSrcCuaqg7I/qif7fAU7Fvmeg7g6+ghW9xfEoggwAX
o6UVrt6EIo3Z3UCMPO2j1JLCCyfdz6EYd8ZIrlVXD9wzbA9keLtDiyfC6UMRgY5I
zQoIi+0XZF7tXXegVgFKv3bSV6fep3hqBbr7Q4j9N32s938Hgzq4/v7DddIByNUL
Wvx8Ly2WPp5tykM6cz9C4koTDl8rOXzFQSd4aBz41qxMSthtjIp9+ZKSstMX3Vkp
sWBaph/2qNn/mmwPBKo5sGzieotqMdFWT8EQBSm72d46K36yJH0kFXZ9jZjKok+Y
yRvD2snfuaaj17OoLwT+NE7Meg==
-----END CERTIFICATE-----
28 changes: 28 additions & 0 deletions cmd/testdata/localhost.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC3cgXwXmlE6mPK
EZpxAGrRDRr6tTIU6blHFKCoWV9guRj2TxDA7twLBZcXUEwJxdGRSdjYq2oQCFm0
hxNHN7tgB7KntPXfF1AkjEEgCbBThnPxnysAumz0+F8G2MewsQD7YWgJgr1poU9O
utjNolAecFuiZafqw/EU2AHusR1RKXCe32SumjeipLHRyyX5Jd0en0VCNA0pWcGO
9EPlLKKbqRoY/as0CBAHp41vM0Rs0GlF2I5pT153BsWLLTyQK/sTZ9X99J8y7WOL
EULnkQ+wiiY+BqHuUIE0ALFCkkJudwytBO3n5USkl/zsGowgNnqqmxWCMzTDroS/
FdN1zp/hAgMBAAECggEAC9Qlg6O01kGmZFrY+nEgUiFOFG0vYOeWv7l56A0WQDiD
Pmun/QbR58CJJvLRql4n9p48RjFcZhMBxMkicjjK42TvrU52/bcFPwwPrXsOdH5S
ltmQdmwu9ydWSkzbaH5rXapA4P8eC1QAVwdnkC/nimTslbbIGnRexN0uV7eyOB/P
iYnvWyKB3upr5cvcqDQuAOcYSjP9PyoqTNJFp3tmKZav1P52IvlS1k3Xvt9OvNqd
2BwK5QtXq45CpAr9z3qmFBGOik5/ZM7JGNVrUUVWP32iDadTy0QYyCqJE37l7s5K
A8rghjL9JBXrOikFOqWvocjNAd/Nzeiwn85eUtIRaQKBgQD8kwI6OlJYP79RxvHm
JiZ7PjtGXGszZyobqhWo6CaQGKZ6hakUb01M5Nq9Z40VoDZoimXjXap2+o/IG0uU
uGx+IV2QQvcO27hj/OYpquVthwOVaXpW4LG/+vtfFTKIIhPehlXpmDuCWb6XGwqC
bbIJkpqruLH4R/FzbhjYfkmkiQKBgQC57vwHnAGZtbSFlKh4g8cJ6Ruo+nC38i9w
KT5xcipbqW8j/ZlK/tPO25vPAn03kNtLklgga4D/gxJ4PECfU1rNUDPHFTK5nUs4
sH44HQA7KlP07XQKviWZ4iN7eab5IRzlhuHiWCiXVGT189HxgD3lQ4BkS0q/54/v
JHKj09J6mQKBgFUcJLACXyUltg6Uf4cSa/0zpz26ftU/ek0AL3RPZk9APzkiOSuN
pfq3U45nin8zEaKAoHzRX1Pgcvr3V6yxyL1n+ONX7XCwUZ4/5j88OzuBN4/tjzAf
X0ZWCMatme2NrixaEDE6/zKZk0PP9OammEvpfv1Gq5ICjDZdbznktGQhAoGBAJsZ
pTl/xMIBFkZ7/JETdBxrTPyHdTGsoC/S59jgoD74NtLyAEbUDcG35eAoNmX8u0Hu
IP9iTihWoTiVIl8FvHAaYCbJIxg9AvuWFqQeZQv1wjVFQxCXD2yvfGPK1iNpoN5C
xvj2C145M0MMEex/yqINzfNb703oD2QwpkTNNP25AoGBALOaemuCWRZGBZhH2BTJ
brj4W4bCZLEuSBYDfbx1vS7cdTIC4hi+njtWvRy5Ts1HewaTKOsXntpRNXF6rvBE
UAEWmIOCTIW9ffybEU927V+XvVGu1Dyv7VMYnk2c6oeR09xPavrJeLfLV3NtzMb5
Wydqz6tdxdjxptOk2I93W6l+
-----END PRIVATE KEY-----
Loading

0 comments on commit aeef306

Please sign in to comment.