@marcel91200 / caddy Mirror · read-only
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 01:00 UTC

master
go 312 lines 10.7 KB
Raw
1 package caddytls
2
3 import (
4 "context"
5 "crypto"
6 "crypto/ecdsa"
7 "crypto/elliptic"
8 "crypto/rand"
9 "crypto/tls"
10 "crypto/x509"
11 "crypto/x509/pkix"
12 "encoding/json"
13 "errors"
14 "math/big"
15 "net"
16 "reflect"
17 "testing"
18 "time"
19
20 "github.com/caddyserver/caddy/v2"
21 )
22
23 type testClientCertificateVerifier struct {
24 called bool
25 rawCerts [][]byte
26 verifiedChains [][]*x509.Certificate
27 err error
28 }
29
30 func (v *testClientCertificateVerifier) VerifyClientCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
31 v.called = true
32 v.rawCerts = rawCerts
33 v.verifiedChains = verifiedChains
34 return v.err
35 }
36
37 func TestClientAuthenticationVerifyConnectionPassesRawCertsToVerifiers(t *testing.T) {
38 verifier := &testClientCertificateVerifier{}
39 clientauth := &ClientAuthentication{
40 verifiers: []ClientCertificateVerifier{verifier},
41 }
42
43 peerCert := &x509.Certificate{Raw: []byte("peer-cert-raw")}
44 verifiedChains := [][]*x509.Certificate{{peerCert}}
45 connState := tls.ConnectionState{
46 PeerCertificates: []*x509.Certificate{peerCert},
47 VerifiedChains: verifiedChains,
48 }
49
50 if err := clientauth.verifyConnection(connState); err != nil {
51 t.Fatalf("verifyConnection failed: %v", err)
52 }
53
54 if !reflect.DeepEqual(verifier.rawCerts, [][]byte{[]byte("peer-cert-raw")}) {
55 t.Fatalf("unexpected raw certs: got %#v", verifier.rawCerts)
56 }
57 if !reflect.DeepEqual(verifier.verifiedChains, verifiedChains) {
58 t.Fatalf("unexpected verified chains: got %#v", verifier.verifiedChains)
59 }
60 }
61
62 func TestClientAuthenticationVerifyConnectionReturnsVerifierError(t *testing.T) {
63 wantErr := errors.New("verify failed")
64 verifier := &testClientCertificateVerifier{err: wantErr}
65 clientauth := &ClientAuthentication{
66 verifiers: []ClientCertificateVerifier{verifier},
67 }
68
69 peerCert := &x509.Certificate{Raw: []byte("peer-cert-raw")}
70 err := clientauth.verifyConnection(tls.ConnectionState{
71 PeerCertificates: []*x509.Certificate{peerCert},
72 })
73 if !errors.Is(err, wantErr) {
74 t.Fatalf("expected error %v, got %v", wantErr, err)
75 }
76 }
77
78 func TestClientAuthenticationVerifyConnectionSkipsVerifiersWithoutPeerCert(t *testing.T) {
79 verifier := &testClientCertificateVerifier{err: errors.New("verify failed")}
80 clientauth := &ClientAuthentication{
81 verifiers: []ClientCertificateVerifier{verifier},
82 }
83
84 if err := clientauth.verifyConnection(tls.ConnectionState{}); err != nil {
85 t.Fatalf("verifyConnection failed: %v", err)
86 }
87 if verifier.called {
88 t.Fatal("verifier was called without a peer certificate")
89 }
90 }
91
92 func TestClientAuthenticationModeWithLeafVerifier(t *testing.T) {
93 ca := issueTestCert(t, nil, 0)
94 serverCert := issueTestCert(t, &ca, x509.ExtKeyUsageServerAuth)
95 trusted := issueTestCert(t, &ca, x509.ExtKeyUsageClientAuth)
96 untrusted := issueTestCert(t, &ca, x509.ExtKeyUsageClientAuth)
97
98 caPool := x509.NewCertPool()
99 caPool.AddCert(ca.Leaf)
100
101 for _, tc := range []struct {
102 mode string
103 cert *tls.Certificate
104 wantError bool
105 }{
106 {mode: "request", cert: nil, wantError: false},
107 {mode: "request", cert: &trusted, wantError: false},
108 {mode: "request", cert: &untrusted, wantError: true},
109 {mode: "require", cert: nil, wantError: true},
110 {mode: "require", cert: &trusted, wantError: false},
111 {mode: "require", cert: &untrusted, wantError: true},
112 {mode: "verify_if_given", cert: nil, wantError: false},
113 {mode: "verify_if_given", cert: &trusted, wantError: false},
114 {mode: "verify_if_given", cert: &untrusted, wantError: true},
115 {mode: "require_and_verify", cert: nil, wantError: true},
116 {mode: "require_and_verify", cert: &trusted, wantError: false},
117 {mode: "require_and_verify", cert: &untrusted, wantError: true},
118 } {
119 for _, version := range []uint16{tls.VersionTLS12, tls.VersionTLS13} {
120 clientauth := &ClientAuthentication{
121 Mode: tc.mode,
122 ca: InlineCAPool{pool: caPool},
123 verifiers: []ClientCertificateVerifier{LeafCertClientAuth{trustedLeafCerts: []*x509.Certificate{trusted.Leaf}}},
124 }
125 serverConfig := &tls.Config{Certificates: []tls.Certificate{serverCert}}
126 if err := clientauth.ConfigureTLSConfig(serverConfig); err != nil {
127 t.Fatalf("configuring TLS: %v", err)
128 }
129 clientConfig := &tls.Config{
130 InsecureSkipVerify: true,
131 MinVersion: version,
132 MaxVersion: version,
133 }
134 if tc.cert != nil {
135 clientConfig.Certificates = []tls.Certificate{*tc.cert}
136 }
137
138 err := serverHandshake(t, serverConfig, clientConfig)
139 certName := "no certificate"
140 if tc.cert != nil {
141 certName = tc.cert.Leaf.Subject.CommonName
142 }
143 if tc.wantError && err == nil {
144 t.Errorf("mode %s, %s, %s: expected handshake error but got none", tc.mode, tls.VersionName(version), certName)
145 }
146 if !tc.wantError && err != nil {
147 t.Errorf("mode %s, %s, %s: unexpected handshake error: %v", tc.mode, tls.VersionName(version), certName, err)
148 }
149 }
150 }
151 }
152
153 func TestClientAuthenticationVerifyModesNeedTrustPool(t *testing.T) {
154 ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
155 defer cancel()
156
157 // verifiers are loaded by the connection policy, so set both fields like a provisioned config
158 verifiersRaw := []json.RawMessage{json.RawMessage(`{"verifier":"leaf"}`)}
159 verifiers := []ClientCertificateVerifier{LeafCertClientAuth{trustedLeafCerts: []*x509.Certificate{issueTestCert(t, nil, 0).Leaf}}}
160 customPool := json.RawMessage(`{"provider":"inline","trusted_ca_certs":["` + test_der_1 + `"]}`)
161 systemPool := json.RawMessage(`{"provider":"system"}`)
162
163 for i, tc := range []struct {
164 clientauth ClientAuthentication
165 want tls.ClientAuthType
166 wantErr bool
167 }{
168 {clientauth: ClientAuthentication{Mode: "verify_if_given"}, wantErr: true},
169 {clientauth: ClientAuthentication{Mode: "require_and_verify"}, wantErr: true},
170 {clientauth: ClientAuthentication{Mode: "require_and_verify", VerifiersRaw: verifiersRaw, verifiers: verifiers}, wantErr: true},
171 {clientauth: ClientAuthentication{Mode: "verify_if_given", CARaw: systemPool}, want: tls.VerifyClientCertIfGiven},
172 {clientauth: ClientAuthentication{Mode: "require_and_verify", CARaw: systemPool}, want: tls.RequireAndVerifyClientCert},
173 {clientauth: ClientAuthentication{Mode: "verify_if_given", CARaw: customPool}, want: tls.VerifyClientCertIfGiven},
174 {clientauth: ClientAuthentication{Mode: "require_and_verify", CARaw: customPool}, want: tls.RequireAndVerifyClientCert},
175 {clientauth: ClientAuthentication{CARaw: customPool}, want: tls.RequireAndVerifyClientCert},
176 {clientauth: ClientAuthentication{Mode: "request"}, want: tls.RequestClientCert},
177 {clientauth: ClientAuthentication{Mode: "require"}, want: tls.RequireAnyClientCert},
178 {clientauth: ClientAuthentication{VerifiersRaw: verifiersRaw, verifiers: verifiers}, want: tls.RequireAnyClientCert},
179 {clientauth: ClientAuthentication{TrustedLeafCerts: []string{test_der_1}}, want: tls.RequireAnyClientCert},
180 } {
181 if err := tc.clientauth.provision(ctx); err != nil {
182 t.Fatalf("Test %d: provisioning: %v", i, err)
183 }
184 cfg := new(tls.Config)
185 err := tc.clientauth.ConfigureTLSConfig(cfg)
186 if tc.wantErr {
187 if err == nil {
188 t.Errorf("Test %d: expected error but got none", i)
189 }
190 continue
191 }
192 if err != nil {
193 t.Errorf("Test %d: unexpected error: %v", i, err)
194 continue
195 }
196 if cfg.ClientAuth != tc.want {
197 t.Errorf("Test %d: expected %v, got %v", i, tc.want, cfg.ClientAuth)
198 }
199 if tc.clientauth.CARaw != nil && cfg.ClientCAs == nil {
200 t.Errorf("Test %d: expected ClientCAs to be set", i)
201 }
202 }
203 }
204
205 func TestClientAuthenticationVerifierOnlyHandshake(t *testing.T) {
206 ca := issueTestCert(t, nil, 0)
207 serverCert := issueTestCert(t, &ca, x509.ExtKeyUsageServerAuth)
208 trusted := issueTestCert(t, &ca, x509.ExtKeyUsageClientAuth)
209 untrusted := issueTestCert(t, &ca, x509.ExtKeyUsageClientAuth)
210
211 for _, tc := range []struct {
212 cert *tls.Certificate
213 wantError bool
214 }{
215 {cert: nil, wantError: true},
216 {cert: &trusted, wantError: false},
217 {cert: &untrusted, wantError: true},
218 } {
219 // no mode and no trust pool, so only the verifier checks the cert
220 clientauth := &ClientAuthentication{
221 VerifiersRaw: []json.RawMessage{json.RawMessage(`{"verifier":"leaf"}`)},
222 verifiers: []ClientCertificateVerifier{LeafCertClientAuth{trustedLeafCerts: []*x509.Certificate{trusted.Leaf}}},
223 }
224 serverConfig := &tls.Config{Certificates: []tls.Certificate{serverCert}}
225 if err := clientauth.ConfigureTLSConfig(serverConfig); err != nil {
226 t.Fatalf("configuring TLS: %v", err)
227 }
228 clientConfig := &tls.Config{InsecureSkipVerify: true}
229 if tc.cert != nil {
230 clientConfig.Certificates = []tls.Certificate{*tc.cert}
231 }
232
233 err := serverHandshake(t, serverConfig, clientConfig)
234 if tc.wantError && err == nil {
235 t.Errorf("cert %v: expected handshake error but got none", tc.cert != nil)
236 }
237 if !tc.wantError && err != nil {
238 t.Errorf("cert %v: unexpected handshake error: %v", tc.cert != nil, err)
239 }
240 }
241 }
242
243 // serverHandshake runs one TLS handshake over loopback TCP and returns
244 // the server's handshake error.
245 func serverHandshake(t *testing.T, serverConfig, clientConfig *tls.Config) error {
246 t.Helper()
247 ln, err := net.Listen("tcp", "127.0.0.1:0")
248 if err != nil {
249 t.Fatal(err)
250 }
251 defer ln.Close()
252
253 errCh := make(chan error, 1)
254 go func() {
255 conn, err := ln.Accept()
256 if err != nil {
257 errCh <- err
258 return
259 }
260 tlsConn := tls.Server(conn, serverConfig)
261 errCh <- tlsConn.Handshake()
262 tlsConn.Close()
263 }()
264
265 conn, err := tls.Dial("tcp", ln.Addr().String(), clientConfig)
266 if err == nil {
267 // wait for the server to finish before closing
268 _, _ = conn.Read(make([]byte, 1))
269 conn.Close()
270 }
271 return <-errCh
272 }
273
274 // issueTestCert returns a certificate signed by parent, or a self-signed
275 // CA certificate if parent is nil.
276 func issueTestCert(t *testing.T, parent *tls.Certificate, extKeyUsage x509.ExtKeyUsage) tls.Certificate {
277 t.Helper()
278 key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
279 if err != nil {
280 t.Fatal(err)
281 }
282 serial, err := rand.Int(rand.Reader, big.NewInt(1<<62))
283 if err != nil {
284 t.Fatal(err)
285 }
286 template := &x509.Certificate{
287 SerialNumber: serial,
288 Subject: pkix.Name{CommonName: "test " + serial.String()},
289 DNSNames: []string{"localhost"},
290 NotBefore: time.Now().Add(-time.Hour),
291 NotAfter: time.Now().Add(time.Hour),
292 KeyUsage: x509.KeyUsageDigitalSignature,
293 }
294 signer, signerKey := template, crypto.Signer(key)
295 if parent == nil {
296 template.IsCA = true
297 template.BasicConstraintsValid = true
298 template.KeyUsage |= x509.KeyUsageCertSign
299 } else {
300 template.ExtKeyUsage = []x509.ExtKeyUsage{extKeyUsage}
301 signer, signerKey = parent.Leaf, parent.PrivateKey.(crypto.Signer)
302 }
303 der, err := x509.CreateCertificate(rand.Reader, template, signer, &key.PublicKey, signerKey)
304 if err != nil {
305 t.Fatal(err)
306 }
307 leaf, err := x509.ParseCertificate(der)
308 if err != nil {
309 t.Fatal(err)
310 }
311 return tls.Certificate{Certificate: [][]byte{der}, PrivateKey: key, Leaf: leaf}
312 }