Skip to content

Commit 7431331

Browse files
authored
Merge pull request #10 from FortnoxAB/feature/tls
Support TLS
2 parents c1d7ee1 + 1794173 commit 7431331

File tree

16 files changed

+254
-50
lines changed

16 files changed

+254
-50
lines changed

cmd/gmc/main.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ func app() *cli.App {
5050
Value: "info",
5151
Usage: "available levels are: " + strings.Join(getLevels(), ","),
5252
},
53+
&cli.BoolFlag{
54+
Name: "log-json",
55+
Usage: "log in json",
56+
},
5357
}
5458

5559
app.Commands = []*cli.Command{
@@ -155,10 +159,23 @@ func app() *cli.App {
155159
Value: "8080",
156160
Usage: "webserver port to listen to",
157161
},
162+
&cli.StringFlag{
163+
Name: "tls-port",
164+
Value: "8443",
165+
Usage: "tls webserver port to listen to",
166+
},
158167
&cli.StringFlag{
159168
Name: "redis-url",
160169
Usage: "redis url, ex redis://[[username]:[password]]@localhost:6379/0 or rediss://[[username]:[password]]@localhost:6379/0 for tls/ssl connections",
161170
},
171+
&cli.StringFlag{
172+
Name: "tls-cert",
173+
Usage: "path to tls cert",
174+
},
175+
&cli.StringFlag{
176+
Name: "tls-key",
177+
Usage: "path to tls key",
178+
},
162179
},
163180
},
164181
{
@@ -315,6 +332,9 @@ func globalBefore(c *cli.Context) error {
315332
fmt.Fprintf(os.Stderr, "using loglevel: %s\n", lvl.String())
316333
}
317334
logrus.SetLevel(lvl)
335+
if c.Bool("log-json") {
336+
logrus.SetFormatter(&logrus.JSONFormatter{})
337+
}
318338
return nil
319339
}
320340

e2e/cert.pem

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDsDCCApigAwIBAgIUFRSYcI0Hw9U+pu1PsesHunDy+2EwDQYJKoZIhvcNAQEL
3+
BQAweDELMAkGA1UEBhMCWFgxDDAKBgNVBAgMA04vQTEMMAoGA1UEBwwDTi9BMSAw
4+
HgYDVQQKDBdTZWxmLXNpZ25lZCBjZXJ0aWZpY2F0ZTErMCkGA1UEAwwiMTIwLjAu
5+
MC4xOiBTZWxmLXNpZ25lZCBjZXJ0aWZpY2F0ZTAeFw0yNTAzMjAwODI3MjFaFw0z
6+
NTAzMTgwODI3MjFaMHgxCzAJBgNVBAYTAlhYMQwwCgYDVQQIDANOL0ExDDAKBgNV
7+
BAcMA04vQTEgMB4GA1UECgwXU2VsZi1zaWduZWQgY2VydGlmaWNhdGUxKzApBgNV
8+
BAMMIjEyMC4wLjAuMTogU2VsZi1zaWduZWQgY2VydGlmaWNhdGUwggEiMA0GCSqG
9+
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGRJ5iehfHEbpbpzhFljGTfOvPAB1IAR56
10+
R74yTt/q0b2b6InYEfYRFI4Phx3TqLtlXEv5sxcSu6v808TImHrCe3ejbDiBZspi
11+
Sndngj8FKgKhqFCjqYxBNWZw/CyqbE4uBWWhC+gXQ7i8iyeLyYvs4hfhMinVfMd+
12+
Edqaw/sYQK4liETtk+A7FTyI7vKQltV0kfOfqIR3UT4qDwocS2oarRZ0SeXy9tmE
13+
YrHOmC0TzEpB1GucJjoHkyDoUDi8d0RSjfFGaxfCSBbHoJQwa0outjMnFInUvFjM
14+
/oF86dCZf3A8bMleqF3vS9SFxE0EYZwzMJVXEcWZcQgniQLkka5FAgMBAAGjMjAw
15+
MA8GA1UdEQQIMAaHBH8AAAEwHQYDVR0OBBYEFOeuMeGUQ1r0ynilKh6gaOb2CAcs
16+
MA0GCSqGSIb3DQEBCwUAA4IBAQAHFX8UI7UPmAV1/8AwAcHUmi8es+RYQw5HszIP
17+
PYna5S0I6blHg8aUaEeMF7oUw8Yt0lZDB05tnshIm2QN62OpuJCgoUAWZ3vc4wo2
18+
dtlGbAtbh9q++5bOSCniKb/QjK7d+2w3G8Gc1Ba+9zhBls6rr4NkW46F6ydhqUZ5
19+
QTnPyfHYoNRS3s4micSDHYXHzIGfjeDelvS9ePGXgez/dCrZSLejNQFLShW2qMui
20+
i5ZayvQy5I3SierhwyZ4dlgsUVbiwfs6TKOXw6fWTm46+UOfBWKOAeVMDc381+m4
21+
2EerEPYXwJAGHP1tUocLYanD8pI++BJSUMPY+Ii5HNduGl+y
22+
-----END CERTIFICATE-----

e2e/e2e_test.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ spec:
130130
assert.NoError(t, err)
131131
assert.EqualValues(t, "filecontentishere\n", content)
132132

133-
time.Sleep(time.Second)
133+
time.Sleep(time.Second * 1)
134134
cancel()
135135
c.wg.Wait()
136136
}
@@ -236,13 +236,13 @@ spec:
236236
c.waitForAccepted()
237237

238238
err = os.WriteFile("./adminConfig", []byte(fmt.Sprintf(`
239-
{"masters":[{"name":"http://127.0.0.1:%s","zone":"zone1"}],
240-
"token":"%s"}`, c.master.WsPort, c.client.Token)), 0666)
239+
{"masters":[{"name":"https://127.0.0.1:%s","zone":"zone1"}],
240+
"token":"%s"}`, c.master.WsTLSPort, c.client.Token)), 0666)
241241
assert.NoError(t, err)
242242

243243
stdout := captureStdout()
244244

245-
a := admin.NewAdmin("./adminConfig", "", "")
245+
a := admin.NewAdmin("./adminConfig", "", "", admin.WithTLSConfig(trustCert("./cert.pem")))
246246
err = a.Exec(context.TODO(), "uptime")
247247
assert.NoError(t, err)
248248

@@ -282,16 +282,16 @@ spec:
282282
c.waitForAccepted()
283283

284284
err = os.WriteFile("./adminConfig", []byte(fmt.Sprintf(`
285-
{"masters":[{"name":"http://127.0.0.1:%s","zone":"zone1"}],
286-
"token":"%s"}`, c.master.WsPort, "blaha")), 0666)
285+
{"masters":[{"name":"https://127.0.0.1:%s","zone":"zone1"}],
286+
"token":"%s"}`, c.master.WsTLSPort, "blaha")), 0666)
287287
assert.NoError(t, err)
288288

289289
buf := &bytes.Buffer{}
290290
logrus.SetFormatter(&logrus.TextFormatter{
291291
DisableColors: true,
292292
})
293293
logrus.SetOutput(buf)
294-
a := admin.NewAdmin("./adminConfig", "", "")
294+
a := admin.NewAdmin("./adminConfig", "", "", admin.WithTLSConfig(trustCert("./cert.pem")))
295295
err = a.Exec(context.TODO(), "uptime")
296296
assert.Equal(t, "websocket: bad handshake", err.Error())
297297

@@ -328,7 +328,7 @@ spec:
328328

329329
buf := &bytes.Buffer{}
330330
logrus.SetOutput(buf)
331-
a := admin.NewAdmin("./agentConfig", "", "")
331+
a := admin.NewAdmin("./agentConfig", "", "", admin.WithTLSConfig(trustCert("./cert.pem")))
332332
err = a.Exec(context.TODO(), "uptime")
333333
assert.NoError(t, err)
334334

@@ -372,8 +372,8 @@ spec:
372372
c.waitForAccepted()
373373

374374
err = os.WriteFile("./adminConfig", []byte(fmt.Sprintf(`
375-
{"masters":[{"name":"http://127.0.0.1:%s","zone":"zone1"}],
376-
"token":"%s"}`, c.master.WsPort, c.client.Token)), 0666)
375+
{"masters":[{"name":"https://127.0.0.1:%s","zone":"zone1"}],
376+
"token":"%s"}`, c.master.WsTLSPort, c.client.Token)), 0666)
377377
assert.NoError(t, err)
378378

379379
var content []byte
@@ -406,7 +406,7 @@ spec:
406406

407407
stdout := captureStdout()
408408

409-
a := admin.NewAdmin("./adminConfig", "", "")
409+
a := admin.NewAdmin("./adminConfig", "", "", admin.WithTLSConfig(trustCert("./cert.pem")))
410410
err = a.Apply(ctx, []string{"newspec.yml"})
411411
assert.NoError(t, err)
412412

e2e/key.pem

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDGRJ5iehfHEbpb
3+
pzhFljGTfOvPAB1IAR56R74yTt/q0b2b6InYEfYRFI4Phx3TqLtlXEv5sxcSu6v8
4+
08TImHrCe3ejbDiBZspiSndngj8FKgKhqFCjqYxBNWZw/CyqbE4uBWWhC+gXQ7i8
5+
iyeLyYvs4hfhMinVfMd+Edqaw/sYQK4liETtk+A7FTyI7vKQltV0kfOfqIR3UT4q
6+
DwocS2oarRZ0SeXy9tmEYrHOmC0TzEpB1GucJjoHkyDoUDi8d0RSjfFGaxfCSBbH
7+
oJQwa0outjMnFInUvFjM/oF86dCZf3A8bMleqF3vS9SFxE0EYZwzMJVXEcWZcQgn
8+
iQLkka5FAgMBAAECggEAFZ1ZdFGXJ1lOwGXZ2Uw8b2BzsgdzKcG6zt3kuvClB0vX
9+
qJg7SYD7xcpAin99HZwMLKAD8F7j1GOn+6d35oko/kFzk4SyzE37y6dj87bb+Ut7
10+
1J/YMAobLj4MwviFmLa3TIZuGUExC4g91YreGXveyJ5auBmTPKDz0VH6S7EIk2YY
11+
nUrGBO1LJyunaObtxPELZlu4cDs/wFaRRSY+z065ETFx/LE/jJ0XGwBZrBWFw8tZ
12+
qwNqHcfR39XR5NpzqfY94pTbwMrnzJ6Ahl9+ppn84JM1RMg5ijJgsF/FUC7yxPP9
13+
8HrLz6Oz4YGrRIerYmrApqWv27sOlTUViJLXCSGF6QKBgQDnMttqawya8dMbV9o8
14+
4ZFkadNPcAQ0TusBtipygBh/uUuIXnwXGFDDeNevNNgE27bXzhQDVdUcdUQxMk/I
15+
MioQS790r3UpYE5ZLt70lncX8Z38+f8bDTEtuWAaRe2ECCVSNI20nK9iKlSu3Vv4
16+
+j8LFT/nZ9cJ8z06YPm4c4OjuwKBgQDbiW0HC+enKNy1FPQUWobTpWwnZpHlii62
17+
VfSlXIIcwv1y659czBo2VmCiRYFQqSyKnPE3YeKT2Y0ZzNNYhMl4aQdBN+nYzj7s
18+
yl6ZDYWFcRgZCLKZeY36p3DQRc9nlnwBWV9STLQV+mrabf5R0KIoiTrOtG8VK1aO
19+
oUIf/rXV/wKBgQDSYLm9/UkMGS7C+88vhQZa+9z3tPNucb1w4kV/yUYBuyebIHcE
20+
QPEE3gpNeOV0jkWz2+bkHg99BMwXhDOK9PLHv1WpJRuUmfjROFBS+jPGiur7TrUu
21+
9XMhq0Riw+zcLtlfE0k65zTEO8axE+ZkCbqiKCTtOdU4TakSXTn++MX5jwKBgAxN
22+
ym9/qk8DCkOX1goh/LZ16fbXV8vuj6mmbZyq75vfDcdYD0lrIvjypF3T2WiE4rsu
23+
CpLZCJLSuYa9pQasAoKeGEr+cDu3a21n9h9L07Tj3r7gbuoNFvj6U2dI0lPy6iZF
24+
NQNuyxUEQOLXEU7Si5QMBOC62hLsp+A8h3E1nElPAoGBALdt0xj5WRy9Hjo1tVgX
25+
L9drgcUTYFHvhGXPKDhYXY2LWpYislD1aBBdj8HdfrLU8sAlEV95WGHpYSkdOaR+
26+
Y8Hk1BCTK4MvPUOyxXZfKtGS8rbVuDn8RyU7BP1emxQuv0vhwjsqMT+oAeomn43t
27+
itXx5KSBHoqh6RphLVoXPwAv
28+
-----END PRIVATE KEY-----

e2e/utils_test.go

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ package e2e_test
33
import (
44
"bytes"
55
"context"
6+
"crypto/tls"
7+
"crypto/x509"
68
"io"
9+
"log"
710
"net"
811
"os"
912
"strconv"
@@ -16,6 +19,7 @@ import (
1619
"github.com/fortnoxab/gitmachinecontroller/pkg/agent"
1720
"github.com/fortnoxab/gitmachinecontroller/pkg/agent/config"
1821
"github.com/fortnoxab/gitmachinecontroller/pkg/authedhttpclient"
22+
"github.com/fortnoxab/gitmachinecontroller/pkg/httpclient"
1923
"github.com/fortnoxab/gitmachinecontroller/pkg/master"
2024
"github.com/sirupsen/logrus"
2125
"github.com/stretchr/testify/assert"
@@ -36,24 +40,29 @@ func initMasterAgent(t *testing.T, ctx context.Context) testWrapper {
3640
port, err := freePort()
3741
assert.NoError(t, err)
3842
portStr := strconv.Itoa(port)
43+
tlsPortStr := strconv.Itoa(port + 1)
3944
redisMock := mocks.NewMockCmdable(t)
4045
master := &master.Master{
4146
GitURL: "https://test/gitrepo",
4247
GitPollInterval: time.Second,
4348
WsPort: portStr,
49+
WsTLSPort: tlsPortStr,
50+
TLSCertFile: "./cert.pem",
51+
TLSKeyFIle: "./key.pem",
4452
JWTKey: "asdfasdf",
4553
SecretKey: "asdfasdf",
4654
RedisClient: redisMock,
4755
Masters: config.Masters{
4856
{
49-
URL: "http://127.0.0.1:" + portStr,
57+
URL: "https://127.0.0.1:" + tlsPortStr,
5058
Zone: "zone1",
5159
},
5260
},
5361
}
62+
httpclient.SetTLSConfig(trustCert("./cert.pem"))
5463
mockedCommander := mocks.NewMockCommander(t)
55-
agent := agent.NewAgent("./agentConfig", mockedCommander)
56-
agent.Master = "http://127.0.0.1:" + portStr
64+
agent := agent.NewAgent("./agentConfig", mockedCommander, agent.WithTLSConfig(trustCert("./cert.pem")))
65+
agent.Master = "https://127.0.0.1:" + tlsPortStr
5766
agent.Hostname = "mycooltestagent"
5867

5968
wg := &sync.WaitGroup{}
@@ -73,7 +82,7 @@ func initMasterAgent(t *testing.T, ctx context.Context) testWrapper {
7382
}()
7483

7584
time.Sleep(400 * time.Millisecond)
76-
client := authedhttpclient.New(t, "http://127.0.0.1:"+portStr)
85+
client := authedhttpclient.New(t, "https://127.0.0.1:"+tlsPortStr)
7786
err = client.AuthAsAdmin()
7887
assert.NoError(t, err)
7988

@@ -96,6 +105,31 @@ func initMasterAgent(t *testing.T, ctx context.Context) testWrapper {
96105
},
97106
}
98107
}
108+
109+
func trustCert(localCertFile string) *tls.Config {
110+
// Get the SystemCertPool, continue with an empty pool on error
111+
rootCAs, _ := x509.SystemCertPool()
112+
if rootCAs == nil {
113+
rootCAs = x509.NewCertPool()
114+
}
115+
116+
// Read in the cert file
117+
certs, err := os.ReadFile(localCertFile)
118+
if err != nil {
119+
log.Fatalf("Failed to append %q to RootCAs: %v", localCertFile, err)
120+
}
121+
122+
// Append our cert to the system pool
123+
if ok := rootCAs.AppendCertsFromPEM(certs); !ok {
124+
log.Println("No certs appended, using system certs only")
125+
}
126+
127+
// Trust the augmented cert pool in our client
128+
return &tls.Config{
129+
RootCAs: rootCAs,
130+
}
131+
}
132+
99133
func freePort() (port int, err error) {
100134
var a *net.TCPAddr
101135
if a, err = net.ResolveTCPAddr("tcp", "127.0.0.1:0"); err == nil {

go.mod

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ require (
99
github.com/fortnoxab/ginprometheus v0.0.0-20211026110220-d3da4ce1dc2b
1010
github.com/gin-contrib/pprof v1.5.1
1111
github.com/gin-gonic/gin v1.10.0
12-
github.com/golang-jwt/jwt/v4 v4.5.1
12+
github.com/golang-jwt/jwt/v5 v5.2.1
1313
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
1414
github.com/google/uuid v1.6.0
1515
github.com/gorilla/websocket v1.5.3
@@ -58,7 +58,6 @@ require (
5858
github.com/go-playground/universal-translator v0.18.1 // indirect
5959
github.com/go-playground/validator/v10 v10.23.0 // indirect
6060
github.com/goccy/go-json v0.10.3 // indirect
61-
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
6261
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
6362
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
6463
github.com/json-iterator/go v1.1.12 // indirect

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,6 @@ github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PU
182182
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
183183
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
184184
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
185-
github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
186-
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
187185
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
188186
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
189187
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=

pkg/admin/admin.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package admin
22

33
import (
44
"context"
5+
"crypto/tls"
56
"encoding/json"
67
"errors"
78
"fmt"
@@ -43,6 +44,8 @@ type Admin struct {
4344
// binary location when Bootstrap
4445
targetPath string
4546
sshUser string
47+
48+
tlsConfig *tls.Config
4649
}
4750

4851
func NewAdminFromContext(c *cli.Context) *Admin {
@@ -56,13 +59,22 @@ func NewAdminFromContext(c *cli.Context) *Admin {
5659
}
5760
}
5861

59-
func NewAdmin(configFile, selector, regexp string) *Admin {
62+
func NewAdmin(configFile, selector, regexp string, options ...func(*Admin)) *Admin {
6063

61-
return &Admin{
64+
a := &Admin{
6265
configFile: configFile,
6366
selector: selector,
6467
regexp: regexp,
6568
}
69+
for _, o := range options {
70+
o(a)
71+
}
72+
return a
73+
}
74+
func WithTLSConfig(c *tls.Config) func(*Admin) {
75+
return func(s *Admin) {
76+
s.tlsConfig = c
77+
}
6678
}
6779

6880
func (a *Admin) wsConnect(ctx context.Context, conf *config.Config) (websocket.Websocket, error) {
@@ -71,6 +83,9 @@ func (a *Admin) wsConnect(ctx context.Context, conf *config.Config) (websocket.W
7183
headers.Set("Authorization", conf.Token)
7284

7385
wsClient := websocket.NewWebsocketClient()
86+
if a.tlsConfig != nil {
87+
wsClient.SetTLSConfig(a.tlsConfig)
88+
}
7489

7590
master := conf.FindMasterForConnection(ctx, "", "")
7691

pkg/agent/agent.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package agent
22

33
import (
44
"context"
5+
"crypto/tls"
56
"encoding/json"
67
"net/http"
78
"os"
@@ -53,17 +54,26 @@ func NewAgentFromContext(c *cli.Context) *Agent {
5354
}
5455
return m
5556
}
56-
func NewAgent(configFile string, commander command.Commander) *Agent {
57+
func NewAgent(configFile string, commander command.Commander, options ...func(*Agent)) *Agent {
5758
m := &Agent{
5859
wg: &sync.WaitGroup{},
5960
callbacks: make(map[string][]OnFunc),
6061
client: websocket.NewWebsocketClient(),
6162
configFile: configFile,
6263
commander: commander,
6364
}
65+
for _, o := range options {
66+
o(m)
67+
}
6468
return m
6569
}
6670

71+
func WithTLSConfig(c *tls.Config) func(*Agent) {
72+
return func(s *Agent) {
73+
s.client.SetTLSConfig(c)
74+
}
75+
}
76+
6777
func (a *Agent) Run(ctx context.Context) error {
6878
conf, err := config.FromFile(a.configFile)
6979

@@ -73,7 +83,7 @@ func (a *Agent) Run(ctx context.Context) error {
7383
conf = &config.Config{
7484
Masters: config.Masters{&config.Master{URL: a.Master}},
7585
}
76-
err := os.MkdirAll(filepath.Dir(a.configFile), 0700)
86+
err = os.MkdirAll(filepath.Dir(a.configFile), 0700)
7787
if err != nil {
7888
return err
7989
}

0 commit comments

Comments
 (0)