Skip to content

Commit f9177e4

Browse files
committed
Add tests for TLS
1 parent 0cc4263 commit f9177e4

File tree

8 files changed

+138
-54
lines changed

8 files changed

+138
-54
lines changed

coderd/coderdtest/coderdtest.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"crypto/rand"
88
"crypto/rsa"
99
"crypto/sha256"
10+
"crypto/tls"
1011
"crypto/x509"
1112
"crypto/x509/pkix"
1213
"encoding/base64"
@@ -75,6 +76,7 @@ type Options struct {
7576
AutobuildTicker <-chan time.Time
7677
AutobuildStats chan<- executor.Stats
7778
Auditor audit.Auditor
79+
TLSCertificates []tls.Certificate
7880

7981
// IncludeProvisionerDaemon when true means to start an in-memory provisionerD
8082
IncludeProvisionerDaemon bool
@@ -158,7 +160,14 @@ func NewOptions(t *testing.T, options *Options) (*httptest.Server, context.Cance
158160
srv.Config.BaseContext = func(_ net.Listener) context.Context {
159161
return ctx
160162
}
161-
srv.Start()
163+
if options.TLSCertificates != nil {
164+
srv.TLS = &tls.Config{
165+
Certificates: options.TLSCertificates,
166+
}
167+
srv.StartTLS()
168+
} else {
169+
srv.Start()
170+
}
162171
t.Cleanup(srv.Close)
163172

164173
tcpAddr, ok := srv.Listener.Addr().(*net.TCPAddr)
@@ -201,6 +210,7 @@ func NewOptions(t *testing.T, options *Options) (*httptest.Server, context.Cance
201210
APIRateLimit: options.APIRateLimit,
202211
Authorizer: options.Authorizer,
203212
Telemetry: telemetry.NewNoop(),
213+
TLSCertificates: options.TLSCertificates,
204214
DERPMap: &tailcfg.DERPMap{
205215
Regions: map[int]*tailcfg.DERPRegion{
206216
1: {
@@ -215,7 +225,7 @@ func NewOptions(t *testing.T, options *Options) (*httptest.Server, context.Cance
215225
DERPPort: derpPort,
216226
STUNPort: stunAddr.Port,
217227
InsecureForTests: true,
218-
ForceHTTP: true,
228+
ForceHTTP: options.TLSCertificates == nil,
219229
}},
220230
},
221231
},

codersdk/workspaceagents.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,8 @@ func (c *Client) ListenWorkspaceAgentTailnet(ctx context.Context) (net.Conn, err
315315
Value: c.SessionToken,
316316
}})
317317
httpClient := &http.Client{
318-
Jar: jar,
318+
Jar: jar,
319+
Transport: c.HTTPClient.Transport,
319320
}
320321
// nolint:bodyclose
321322
conn, res, err := websocket.Dial(ctx, coordinateURL.String(), &websocket.DialOptions{
@@ -380,7 +381,8 @@ func (c *Client) DialWorkspaceAgent(ctx context.Context, agentID uuid.UUID, opti
380381
Value: c.SessionToken,
381382
}})
382383
httpClient := &http.Client{
383-
Jar: jar,
384+
Jar: jar,
385+
Transport: c.HTTPClient.Transport,
384386
}
385387
ctx, cancelFunc := context.WithCancel(ctx)
386388
closed := make(chan struct{})

enterprise/coderd/coderd.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,9 @@ func New(ctx context.Context, options *Options) (*API, error) {
150150
rootCA.AddCert(certificate)
151151
}
152152
}
153-
154153
// nolint:gosec
155154
api.derpMesh = derpmesh.New(options.Logger.Named("derpmesh"), api.DERPServer, &tls.Config{
156-
ServerName: options.AccessURL.Host,
155+
ServerName: options.AccessURL.Hostname(),
157156
RootCAs: rootCA,
158157
})
159158

enterprise/coderd/coderdenttest/coderdenttest.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import (
44
"context"
55
"crypto/ed25519"
66
"crypto/rand"
7+
"crypto/tls"
78
"io"
9+
"net/http"
810
"testing"
911
"time"
1012

@@ -85,7 +87,16 @@ func NewWithAPI(t *testing.T, options *Options) (*codersdk.Client, io.Closer, *c
8587
_ = provisionerCloser.Close()
8688
_ = coderAPI.Close()
8789
})
88-
return codersdk.New(coderAPI.AccessURL), provisionerCloser, coderAPI
90+
client := codersdk.New(coderAPI.AccessURL)
91+
client.HTTPClient = &http.Client{
92+
Transport: &http.Transport{
93+
TLSClientConfig: &tls.Config{
94+
//nolint:gosec
95+
InsecureSkipVerify: true,
96+
},
97+
},
98+
}
99+
return client, provisionerCloser, coderAPI
89100
}
90101

91102
type LicenseOptions struct {

enterprise/coderd/replicas_test.go

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

33
import (
44
"context"
5+
"crypto/tls"
56
"testing"
67
"time"
78

@@ -19,7 +20,7 @@ import (
1920

2021
func TestReplicas(t *testing.T) {
2122
t.Parallel()
22-
t.Run("WarningsWithoutLicense", func(t *testing.T) {
23+
t.Run("ErrorWithoutLicense", func(t *testing.T) {
2324
t.Parallel()
2425
db, pubsub := dbtestutil.NewDB(t)
2526
firstClient := coderdenttest.New(t, &coderdenttest.Options{
@@ -39,7 +40,7 @@ func TestReplicas(t *testing.T) {
3940
secondClient.SessionToken = firstClient.SessionToken
4041
ents, err := secondClient.Entitlements(context.Background())
4142
require.NoError(t, err)
42-
require.Len(t, ents.Warnings, 1)
43+
require.Len(t, ents.Errors, 1)
4344
_ = secondAPI.Close()
4445

4546
ents, err = firstClient.Entitlements(context.Background())
@@ -85,6 +86,48 @@ func TestReplicas(t *testing.T) {
8586
return err == nil
8687
}, testutil.WaitLong, testutil.IntervalFast)
8788
_ = conn.Close()
89+
})
90+
t.Run("ConnectAcrossMultipleTLS", func(t *testing.T) {
91+
t.Parallel()
92+
db, pubsub := dbtestutil.NewDB(t)
93+
certificates := []tls.Certificate{testutil.GenerateTLSCertificate(t, "localhost")}
94+
firstClient := coderdenttest.New(t, &coderdenttest.Options{
95+
Options: &coderdtest.Options{
96+
IncludeProvisionerDaemon: true,
97+
Database: db,
98+
Pubsub: pubsub,
99+
TLSCertificates: certificates,
100+
},
101+
})
102+
firstUser := coderdtest.CreateFirstUser(t, firstClient)
103+
coderdenttest.AddLicense(t, firstClient, coderdenttest.LicenseOptions{
104+
HighAvailability: true,
105+
})
106+
107+
secondClient := coderdenttest.New(t, &coderdenttest.Options{
108+
Options: &coderdtest.Options{
109+
Database: db,
110+
Pubsub: pubsub,
111+
TLSCertificates: certificates,
112+
},
113+
})
114+
secondClient.SessionToken = firstClient.SessionToken
115+
replicas, err := secondClient.Replicas(context.Background())
116+
require.NoError(t, err)
117+
require.Len(t, replicas, 2)
88118

119+
_, agent := setupWorkspaceAgent(t, firstClient, firstUser, 0)
120+
conn, err := secondClient.DialWorkspaceAgent(context.Background(), agent.ID, &codersdk.DialWorkspaceAgentOptions{
121+
BlockEndpoints: true,
122+
Logger: slogtest.Make(t, nil).Leveled(slog.LevelDebug),
123+
})
124+
require.NoError(t, err)
125+
require.Eventually(t, func() bool {
126+
ctx, cancelFunc := context.WithTimeout(context.Background(), 3*time.Second)
127+
defer cancelFunc()
128+
_, err = conn.Ping(ctx)
129+
return err == nil
130+
}, testutil.WaitLong, testutil.IntervalFast)
131+
_ = conn.Close()
89132
})
90133
}

enterprise/coderd/workspaceagents_test.go

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

33
import (
44
"context"
5+
"crypto/tls"
56
"fmt"
67
"net/http"
78
"testing"
@@ -108,6 +109,14 @@ func setupWorkspaceAgent(t *testing.T, client *codersdk.Client, user codersdk.Cr
108109
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
109110
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
110111
agentClient := codersdk.New(client.URL)
112+
agentClient.HTTPClient = &http.Client{
113+
Transport: &http.Transport{
114+
TLSClientConfig: &tls.Config{
115+
//nolint:gosec
116+
InsecureSkipVerify: true,
117+
},
118+
},
119+
}
111120
agentClient.SessionToken = authToken
112121
agentCloser := agent.New(agent.Options{
113122
FetchMetadata: agentClient.WorkspaceAgentMetadata,

enterprise/derpmesh/derpmesh_test.go

Lines changed: 2 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,13 @@
11
package derpmesh_test
22

33
import (
4-
"bytes"
54
"context"
6-
"crypto/ecdsa"
7-
"crypto/elliptic"
8-
"crypto/rand"
95
"crypto/tls"
106
"crypto/x509"
11-
"crypto/x509/pkix"
12-
"encoding/pem"
137
"errors"
148
"io"
15-
"math/big"
16-
"net"
179
"net/http/httptest"
1810
"testing"
19-
"time"
2011

2112
"github.com/stretchr/testify/assert"
2213
"github.com/stretchr/testify/require"
@@ -29,6 +20,7 @@ import (
2920
"cdr.dev/slog/sloggers/slogtest"
3021
"github.com/coder/coder/enterprise/derpmesh"
3122
"github.com/coder/coder/tailnet"
23+
"github.com/coder/coder/testutil"
3224
)
3325

3426
func TestMain(m *testing.M) {
@@ -38,7 +30,7 @@ func TestMain(m *testing.M) {
3830
func TestDERPMesh(t *testing.T) {
3931
t.Parallel()
4032
commonName := "something.org"
41-
rawCert := generateTLSCertificate(t, commonName)
33+
rawCert := testutil.GenerateTLSCertificate(t, commonName)
4234
certificate, err := x509.ParseCertificate(rawCert.Certificate[0])
4335
require.NoError(t, err)
4436
pool := x509.NewCertPool()
@@ -174,38 +166,3 @@ func startDERP(t *testing.T, tlsConfig *tls.Config) (*derp.Server, string) {
174166
t.Cleanup(server.Close)
175167
return d, server.URL
176168
}
177-
178-
func generateTLSCertificate(t testing.TB, commonName string) tls.Certificate {
179-
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
180-
require.NoError(t, err)
181-
template := x509.Certificate{
182-
SerialNumber: big.NewInt(1),
183-
Subject: pkix.Name{
184-
Organization: []string{"Acme Co"},
185-
CommonName: commonName,
186-
},
187-
DNSNames: []string{commonName},
188-
NotBefore: time.Now(),
189-
NotAfter: time.Now().Add(time.Hour * 24 * 180),
190-
191-
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
192-
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
193-
BasicConstraintsValid: true,
194-
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
195-
}
196-
197-
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
198-
require.NoError(t, err)
199-
var certFile bytes.Buffer
200-
require.NoError(t, err)
201-
_, err = certFile.Write(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}))
202-
require.NoError(t, err)
203-
privateKeyBytes, err := x509.MarshalPKCS8PrivateKey(privateKey)
204-
require.NoError(t, err)
205-
var keyFile bytes.Buffer
206-
err = pem.Encode(&keyFile, &pem.Block{Type: "PRIVATE KEY", Bytes: privateKeyBytes})
207-
require.NoError(t, err)
208-
cert, err := tls.X509KeyPair(certFile.Bytes(), keyFile.Bytes())
209-
require.NoError(t, err)
210-
return cert
211-
}

testutil/certificate.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package testutil
2+
3+
import (
4+
"bytes"
5+
"crypto/ecdsa"
6+
"crypto/elliptic"
7+
"crypto/rand"
8+
"crypto/tls"
9+
"crypto/x509"
10+
"crypto/x509/pkix"
11+
"encoding/pem"
12+
"math/big"
13+
"net"
14+
"testing"
15+
"time"
16+
17+
"github.com/stretchr/testify/require"
18+
)
19+
20+
func GenerateTLSCertificate(t testing.TB, commonName string) tls.Certificate {
21+
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
22+
require.NoError(t, err)
23+
template := x509.Certificate{
24+
SerialNumber: big.NewInt(1),
25+
Subject: pkix.Name{
26+
Organization: []string{"Acme Co"},
27+
CommonName: commonName,
28+
},
29+
DNSNames: []string{commonName},
30+
NotBefore: time.Now(),
31+
NotAfter: time.Now().Add(time.Hour * 24 * 180),
32+
33+
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
34+
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
35+
BasicConstraintsValid: true,
36+
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
37+
}
38+
39+
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
40+
require.NoError(t, err)
41+
var certFile bytes.Buffer
42+
require.NoError(t, err)
43+
_, err = certFile.Write(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}))
44+
require.NoError(t, err)
45+
privateKeyBytes, err := x509.MarshalPKCS8PrivateKey(privateKey)
46+
require.NoError(t, err)
47+
var keyFile bytes.Buffer
48+
err = pem.Encode(&keyFile, &pem.Block{Type: "PRIVATE KEY", Bytes: privateKeyBytes})
49+
require.NoError(t, err)
50+
cert, err := tls.X509KeyPair(certFile.Bytes(), keyFile.Bytes())
51+
require.NoError(t, err)
52+
return cert
53+
}

0 commit comments

Comments
 (0)