Skip to content

Commit aeef306

Browse files
authored
Merge pull request #364 from gatewayd-io/tls-termination-for-server
TLS termination for incoming client connection
2 parents 3f4b240 + 52d2455 commit aeef306

31 files changed

+838
-355
lines changed

.github/workflows/test.yaml

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,27 @@ jobs:
130130
EOF
131131
132132
- name: Run GatewayD with template plugin 🚀
133-
run: GATEWAYD_LOGGERS_DEFAULT_LEVEL=debug ./gatewayd run &
133+
run: ./gatewayd run -c testdata/gatewayd_tls.yaml &
134134

135-
- name: Run a test with PSQL 🧪
135+
- name: Install PSQL 🧑‍💻
136136
run: |
137137
sudo apt-get update
138138
sudo apt-get install --yes --no-install-recommends postgresql-client
139+
140+
- name: Run a test with PSQL over plaintext connection 🧪
141+
run: |
139142
psql ${PGURL} -c "CREATE TABLE test_table (id serial PRIMARY KEY, name varchar(255));" | grep CREATE || exit 1
140143
psql ${PGURL} -c "INSERT INTO test_table (name) VALUES ('test');" | grep INSERT || exit 1
141144
psql ${PGURL} -c "SELECT * FROM test_table;" | grep test || exit 1
145+
psql ${PGURL} -c "DROP TABLE test_table;" | grep DROP || exit 1
146+
env:
147+
PGURL: postgres://postgres:postgres@localhost:15432/postgres?sslmode=disable
148+
149+
- name: Run a test with PSQL over TLS connection 🧪
150+
run: |
151+
psql ${PGURL_TLS} -c "CREATE TABLE test_table (id serial PRIMARY KEY, name varchar(255));" | grep CREATE || exit 1
152+
psql ${PGURL_TLS} -c "INSERT INTO test_table (name) VALUES ('test');" | grep INSERT || exit 1
153+
psql ${PGURL_TLS} -c "SELECT * FROM test_table;" | grep test || exit 1
154+
psql ${PGURL_TLS} -c "DROP TABLE test_table;" | grep DROP || exit 1
142155
env:
143-
PGURL: postgres://postgres:postgres@localhost:15432/postgres
156+
PGURL_TLS: postgres://postgres:postgres@localhost:15432/postgres?sslmode=require

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,6 @@ dist/
2929

3030
# Tensorflow files
3131
libtensorflow*
32+
33+
# Test generated files
34+
cmd/test_plugins.yaml.bak

cmd/cmd_helpers_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import (
77
)
88

99
var (
10-
globalTestConfigFile = "./test_global.yaml"
11-
pluginTestConfigFile = "./test_plugins.yaml"
10+
globalTestConfigFile = "./test_global.yaml"
11+
globalTLSTestConfigFile = "./testdata/gatewayd_tls.yaml"
12+
pluginTestConfigFile = "./test_plugins.yaml"
1213
)
1314

1415
// executeCommandC executes a cobra command and returns the command, output, and error.

cmd/run.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,10 @@ var runCmd = &cobra.Command{
661661
logger,
662662
pluginRegistry,
663663
conf.Plugin.Timeout,
664+
cfg.EnableTLS,
665+
cfg.CertFile,
666+
cfg.KeyFile,
667+
cfg.HandshakeTimeout,
664668
)
665669

666670
span.AddEvent("Create server", trace.WithAttributes(
@@ -669,6 +673,10 @@ var runCmd = &cobra.Command{
669673
attribute.String("address", cfg.Address),
670674
attribute.String("tickInterval", cfg.TickInterval.String()),
671675
attribute.String("pluginTimeout", conf.Plugin.Timeout.String()),
676+
attribute.Bool("enableTLS", cfg.EnableTLS),
677+
attribute.String("certFile", cfg.CertFile),
678+
attribute.String("keyFile", cfg.KeyFile),
679+
attribute.String("handshakeTimeout", cfg.HandshakeTimeout.String()),
672680
))
673681

674682
pluginTimeoutCtx, cancel = context.WithTimeout(

cmd/run_test.go

Lines changed: 97 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,23 @@ func Test_runCmd(t *testing.T) {
2626
assert.FileExists(t, globalTestConfigFile, "configInitCmd should create a config file")
2727

2828
var waitGroup sync.WaitGroup
29+
30+
waitGroup.Add(1)
31+
go func(waitGroup *sync.WaitGroup) {
32+
// Test run command.
33+
output := capturer.CaptureOutput(func() {
34+
_, err := executeCommandC(rootCmd, "run", "-c", globalTestConfigFile, "-p", pluginTestConfigFile)
35+
require.NoError(t, err, "run command should not have returned an error")
36+
})
37+
// Print the output for debugging purposes.
38+
runCmd.Print(output)
39+
// Check if GatewayD started and stopped correctly.
40+
assert.Contains(t, output, "GatewayD is running")
41+
assert.Contains(t, output, "Stopped all servers\n")
42+
43+
waitGroup.Done()
44+
}(&waitGroup)
45+
2946
waitGroup.Add(1)
3047
go func(waitGroup *sync.WaitGroup) {
3148
time.Sleep(100 * time.Millisecond)
@@ -34,7 +51,7 @@ func Test_runCmd(t *testing.T) {
3451
context.Background(),
3552
nil,
3653
nil,
37-
nil,
54+
metricsServer,
3855
nil,
3956
loggers[config.Default],
4057
servers,
@@ -44,38 +61,15 @@ func Test_runCmd(t *testing.T) {
4461
waitGroup.Done()
4562
}(&waitGroup)
4663

47-
waitGroup.Add(1)
48-
go func(waitGroup *sync.WaitGroup) {
49-
// Test run command.
50-
output := capturer.CaptureOutput(func() {
51-
_, err := executeCommandC(rootCmd, "run", "-c", globalTestConfigFile, "-p", pluginTestConfigFile)
52-
require.NoError(t, err, "run command should not have returned an error")
53-
})
54-
// Print the output for debugging purposes.
55-
runCmd.Print(output)
56-
// Check if GatewayD started and stopped correctly.
57-
assert.Contains(t,
58-
output,
59-
"GatewayD is running",
60-
"run command should have returned the correct output")
61-
assert.Contains(t,
62-
output,
63-
"Stopped all servers\n",
64-
"run command should have returned the correct output")
65-
66-
waitGroup.Done()
67-
}(&waitGroup)
68-
6964
waitGroup.Wait()
7065

7166
// Clean up.
7267
require.NoError(t, os.Remove(pluginTestConfigFile))
7368
require.NoError(t, os.Remove(globalTestConfigFile))
7469
}
7570

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

8680
var waitGroup sync.WaitGroup
81+
// TODO: Test client certificate authentication.
82+
8783
waitGroup.Add(1)
8884
go func(waitGroup *sync.WaitGroup) {
89-
time.Sleep(500 * time.Millisecond)
85+
// Test run command.
86+
output := capturer.CaptureOutput(func() {
87+
_, err := executeCommandC(rootCmd, "run", "-c", globalTLSTestConfigFile, "-p", pluginTestConfigFile)
88+
require.NoError(t, err, "run command should not have returned an error")
89+
})
90+
91+
// Print the output for debugging purposes.
92+
runCmd.Print(output)
93+
94+
// Check if GatewayD started and stopped correctly.
95+
assert.Contains(t, output, "GatewayD is running")
96+
assert.Contains(t, output, "TLS is enabled")
97+
assert.Contains(t, output, "Stopped all servers\n")
98+
99+
waitGroup.Done()
100+
}(&waitGroup)
101+
102+
waitGroup.Add(1)
103+
go func(waitGroup *sync.WaitGroup) {
104+
time.Sleep(100 * time.Millisecond)
90105

91106
StopGracefully(
92107
context.Background(),
93108
nil,
94109
nil,
95-
nil,
110+
metricsServer,
96111
nil,
97112
loggers[config.Default],
98113
servers,
@@ -102,6 +117,24 @@ func Test_runCmdWithMultiTenancy(t *testing.T) {
102117
waitGroup.Done()
103118
}(&waitGroup)
104119

120+
waitGroup.Wait()
121+
122+
// Clean up.
123+
require.NoError(t, os.Remove(pluginTestConfigFile))
124+
}
125+
126+
// Test_runCmdWithMultiTenancy tests the run command with multi-tenancy enabled.
127+
// Note: This test needs two instances of PostgreSQL running on ports 5432 and 5433.
128+
func Test_runCmdWithMultiTenancy(t *testing.T) {
129+
// Create a test plugins config file.
130+
_, err := executeCommandC(rootCmd, "plugin", "init", "--force", "-p", pluginTestConfigFile)
131+
require.NoError(t, err, "plugin init command should not have returned an error")
132+
assert.FileExists(t, pluginTestConfigFile, "plugin init command should have created a config file")
133+
134+
stopChan = make(chan struct{})
135+
136+
var waitGroup sync.WaitGroup
137+
105138
waitGroup.Add(1)
106139
go func(waitGroup *sync.WaitGroup) {
107140
// Test run command.
@@ -123,6 +156,24 @@ func Test_runCmdWithMultiTenancy(t *testing.T) {
123156
waitGroup.Done()
124157
}(&waitGroup)
125158

159+
waitGroup.Add(1)
160+
go func(waitGroup *sync.WaitGroup) {
161+
time.Sleep(500 * time.Millisecond)
162+
163+
StopGracefully(
164+
context.Background(),
165+
nil,
166+
nil,
167+
metricsServer,
168+
nil,
169+
loggers[config.Default],
170+
servers,
171+
stopChan,
172+
)
173+
174+
waitGroup.Done()
175+
}(&waitGroup)
176+
126177
waitGroup.Wait()
127178

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

166217
var waitGroup sync.WaitGroup
218+
219+
waitGroup.Add(1)
220+
go func(waitGroup *sync.WaitGroup) {
221+
// Test run command.
222+
output := capturer.CaptureOutput(func() {
223+
_, err := executeCommandC(rootCmd, "run", "-c", globalTestConfigFile, "-p", pluginTestConfigFile)
224+
require.NoError(t, err, "run command should not have returned an error")
225+
})
226+
// Print the output for debugging purposes.
227+
runCmd.Print(output)
228+
// Check if GatewayD started and stopped correctly.
229+
assert.Contains(t, output, "GatewayD is running")
230+
assert.Contains(t, output, "Stopped all servers\n")
231+
232+
waitGroup.Done()
233+
}(&waitGroup)
234+
167235
waitGroup.Add(1)
168236
go func(waitGroup *sync.WaitGroup) {
169237
time.Sleep(time.Second)
@@ -172,7 +240,7 @@ func Test_runCmdWithCachePlugin(t *testing.T) {
172240
context.Background(),
173241
nil,
174242
nil,
175-
nil,
243+
metricsServer,
176244
nil,
177245
loggers[config.Default],
178246
servers,
@@ -182,28 +250,6 @@ func Test_runCmdWithCachePlugin(t *testing.T) {
182250
waitGroup.Done()
183251
}(&waitGroup)
184252

185-
waitGroup.Add(1)
186-
go func(waitGroup *sync.WaitGroup) {
187-
// Test run command.
188-
output := capturer.CaptureOutput(func() {
189-
_, err := executeCommandC(rootCmd, "run", "-c", globalTestConfigFile, "-p", pluginTestConfigFile)
190-
require.NoError(t, err, "run command should not have returned an error")
191-
})
192-
// Print the output for debugging purposes.
193-
runCmd.Print(output)
194-
// Check if GatewayD started and stopped correctly.
195-
assert.Contains(t,
196-
output,
197-
"GatewayD is running",
198-
"run command should have returned the correct output")
199-
assert.Contains(t,
200-
output,
201-
"Stopped all servers\n",
202-
"run command should have returned the correct output")
203-
204-
waitGroup.Done()
205-
}(&waitGroup)
206-
207253
waitGroup.Wait()
208254

209255
// Clean up.

cmd/testdata/gatewayd_tls.yaml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# GatewayD Global Configuration
2+
3+
loggers:
4+
default:
5+
level: info
6+
output: ["console"]
7+
noColor: True
8+
9+
metrics:
10+
default:
11+
enabled: True
12+
13+
clients:
14+
default:
15+
address: localhost:5432
16+
17+
pools:
18+
default:
19+
size: 10
20+
21+
proxies:
22+
default:
23+
elastic: False
24+
25+
servers:
26+
default:
27+
address: 0.0.0.0:15432
28+
enableTLS: True
29+
certFile: testdata/localhost.crt
30+
keyFile: testdata/localhost.key
31+
32+
api:
33+
enabled: False

cmd/testdata/localhost.crt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDDzCCAfegAwIBAgIUVsSdpPwgCHFdyFpWk5jfYP6jjNMwDQYJKoZIhvcNAQEL
3+
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIzMTEwNTAxMzQzNloXDTIzMTIw
4+
NTAxMzQzNlowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
5+
AAOCAQ8AMIIBCgKCAQEAt3IF8F5pROpjyhGacQBq0Q0a+rUyFOm5RxSgqFlfYLkY
6+
9k8QwO7cCwWXF1BMCcXRkUnY2KtqEAhZtIcTRze7YAeyp7T13xdQJIxBIAmwU4Zz
7+
8Z8rALps9PhfBtjHsLEA+2FoCYK9aaFPTrrYzaJQHnBbomWn6sPxFNgB7rEdUSlw
8+
nt9krpo3oqSx0csl+SXdHp9FQjQNKVnBjvRD5Syim6kaGP2rNAgQB6eNbzNEbNBp
9+
RdiOaU9edwbFiy08kCv7E2fV/fSfMu1jixFC55EPsIomPgah7lCBNACxQpJCbncM
10+
rQTt5+VEpJf87BqMIDZ6qpsVgjM0w66EvxXTdc6f4QIDAQABo1kwVzAUBgNVHREE
11+
DTALgglsb2NhbGhvc3QwCwYDVR0PBAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMB
12+
MB0GA1UdDgQWBBQi14cdty4GZc82KEKWMjzFqM0+jDANBgkqhkiG9w0BAQsFAAOC
13+
AQEAWQ+kgucGvHmUjTjYFGGSrcCuaqg7I/qif7fAU7Fvmeg7g6+ghW9xfEoggwAX
14+
o6UVrt6EIo3Z3UCMPO2j1JLCCyfdz6EYd8ZIrlVXD9wzbA9keLtDiyfC6UMRgY5I
15+
zQoIi+0XZF7tXXegVgFKv3bSV6fep3hqBbr7Q4j9N32s938Hgzq4/v7DddIByNUL
16+
Wvx8Ly2WPp5tykM6cz9C4koTDl8rOXzFQSd4aBz41qxMSthtjIp9+ZKSstMX3Vkp
17+
sWBaph/2qNn/mmwPBKo5sGzieotqMdFWT8EQBSm72d46K36yJH0kFXZ9jZjKok+Y
18+
yRvD2snfuaaj17OoLwT+NE7Meg==
19+
-----END CERTIFICATE-----

cmd/testdata/localhost.key

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC3cgXwXmlE6mPK
3+
EZpxAGrRDRr6tTIU6blHFKCoWV9guRj2TxDA7twLBZcXUEwJxdGRSdjYq2oQCFm0
4+
hxNHN7tgB7KntPXfF1AkjEEgCbBThnPxnysAumz0+F8G2MewsQD7YWgJgr1poU9O
5+
utjNolAecFuiZafqw/EU2AHusR1RKXCe32SumjeipLHRyyX5Jd0en0VCNA0pWcGO
6+
9EPlLKKbqRoY/as0CBAHp41vM0Rs0GlF2I5pT153BsWLLTyQK/sTZ9X99J8y7WOL
7+
EULnkQ+wiiY+BqHuUIE0ALFCkkJudwytBO3n5USkl/zsGowgNnqqmxWCMzTDroS/
8+
FdN1zp/hAgMBAAECggEAC9Qlg6O01kGmZFrY+nEgUiFOFG0vYOeWv7l56A0WQDiD
9+
Pmun/QbR58CJJvLRql4n9p48RjFcZhMBxMkicjjK42TvrU52/bcFPwwPrXsOdH5S
10+
ltmQdmwu9ydWSkzbaH5rXapA4P8eC1QAVwdnkC/nimTslbbIGnRexN0uV7eyOB/P
11+
iYnvWyKB3upr5cvcqDQuAOcYSjP9PyoqTNJFp3tmKZav1P52IvlS1k3Xvt9OvNqd
12+
2BwK5QtXq45CpAr9z3qmFBGOik5/ZM7JGNVrUUVWP32iDadTy0QYyCqJE37l7s5K
13+
A8rghjL9JBXrOikFOqWvocjNAd/Nzeiwn85eUtIRaQKBgQD8kwI6OlJYP79RxvHm
14+
JiZ7PjtGXGszZyobqhWo6CaQGKZ6hakUb01M5Nq9Z40VoDZoimXjXap2+o/IG0uU
15+
uGx+IV2QQvcO27hj/OYpquVthwOVaXpW4LG/+vtfFTKIIhPehlXpmDuCWb6XGwqC
16+
bbIJkpqruLH4R/FzbhjYfkmkiQKBgQC57vwHnAGZtbSFlKh4g8cJ6Ruo+nC38i9w
17+
KT5xcipbqW8j/ZlK/tPO25vPAn03kNtLklgga4D/gxJ4PECfU1rNUDPHFTK5nUs4
18+
sH44HQA7KlP07XQKviWZ4iN7eab5IRzlhuHiWCiXVGT189HxgD3lQ4BkS0q/54/v
19+
JHKj09J6mQKBgFUcJLACXyUltg6Uf4cSa/0zpz26ftU/ek0AL3RPZk9APzkiOSuN
20+
pfq3U45nin8zEaKAoHzRX1Pgcvr3V6yxyL1n+ONX7XCwUZ4/5j88OzuBN4/tjzAf
21+
X0ZWCMatme2NrixaEDE6/zKZk0PP9OammEvpfv1Gq5ICjDZdbznktGQhAoGBAJsZ
22+
pTl/xMIBFkZ7/JETdBxrTPyHdTGsoC/S59jgoD74NtLyAEbUDcG35eAoNmX8u0Hu
23+
IP9iTihWoTiVIl8FvHAaYCbJIxg9AvuWFqQeZQv1wjVFQxCXD2yvfGPK1iNpoN5C
24+
xvj2C145M0MMEex/yqINzfNb703oD2QwpkTNNP25AoGBALOaemuCWRZGBZhH2BTJ
25+
brj4W4bCZLEuSBYDfbx1vS7cdTIC4hi+njtWvRy5Ts1HewaTKOsXntpRNXF6rvBE
26+
UAEWmIOCTIW9ffybEU927V+XvVGu1Dyv7VMYnk2c6oeR09xPavrJeLfLV3NtzMb5
27+
Wydqz6tdxdjxptOk2I93W6l+
28+
-----END PRIVATE KEY-----

0 commit comments

Comments
 (0)