github.com/avenga/couper@v1.12.2/server/http_mtls_test.go (about) 1 package server_test 2 3 import ( 4 "crypto/tls" 5 "crypto/x509" 6 "fmt" 7 "net/http" 8 "strings" 9 "testing" 10 "time" 11 12 "github.com/avenga/couper/internal/test" 13 "github.com/avenga/couper/server" 14 ) 15 16 func TestHTTPSServer_TLS_SelfSigned(t *testing.T) { 17 helper := test.New(t) 18 19 client := test.NewHTTPSClient(&tls.Config{ 20 RootCAs: x509.NewCertPool(), 21 }) 22 23 shutdown, _ := newCouper("testdata/mtls/01_couper.hcl", helper) 24 defer shutdown() 25 26 outreq, err := http.NewRequest(http.MethodGet, "https://localhost:4443/", nil) 27 helper.Must(err) 28 29 _, err = client.Do(outreq) 30 if err == nil { 31 t.Fatal("tls error expected, got nil") 32 } 33 34 if err.Error() != `Get "https://localhost:4443/": x509: certificate signed by unknown authority` { 35 t.Errorf("Want unknown authority error, got: %v", err) 36 } 37 } 38 39 func TestHTTPSServer_TLS_ServerCertificate(t *testing.T) { 40 helper := test.New(t) 41 42 selfSigned, err := server.NewCertificate(time.Minute, nil, nil) 43 helper.Must(err) 44 45 pool := x509.NewCertPool() 46 pool.AddCert(selfSigned.CA.Leaf) 47 client := test.NewHTTPSClient(&tls.Config{ 48 RootCAs: pool, 49 }) 50 51 shutdown, _, err := newCouperWithTemplate("testdata/mtls/02_couper.hcl", helper, map[string]interface{}{ 52 "publicKey": string(selfSigned.ServerCertificate.Certificate), // PEM 53 "privateKey": string(selfSigned.ServerCertificate.PrivateKey), // PEM 54 }) 55 helper.Must(err) 56 defer shutdown() 57 58 outreq, err := http.NewRequest(http.MethodGet, "https://localhost:4443/", nil) 59 helper.Must(err) 60 61 res, err := client.Do(outreq) 62 helper.Must(err) 63 64 if res.StatusCode != http.StatusOK { 65 t.Errorf("Expected statusOK, got: %d", res.StatusCode) 66 } 67 68 if !strings.HasPrefix(res.Header.Get("Location"), "https://") { 69 t.Errorf("Expected https scheme in url variable value, got: %s", res.Header.Get("Location")) 70 } 71 } 72 73 func TestHTTPSServer_TLS_ServerClientCertificate(t *testing.T) { 74 helper := test.New(t) 75 76 selfSigned, err := server.NewCertificate(time.Minute, nil, nil) 77 helper.Must(err) 78 79 pool := x509.NewCertPool() 80 pool.AddCert(selfSigned.CA.Leaf) 81 client := test.NewHTTPSClient(&tls.Config{ 82 RootCAs: pool, 83 Certificates: []tls.Certificate{*selfSigned.Client}, 84 }) 85 86 shutdown, _, err := newCouperWithTemplate("testdata/mtls/03_couper.hcl", helper, map[string]interface{}{ 87 "publicKey": string(selfSigned.ServerCertificate.Certificate), // PEM 88 "privateKey": string(selfSigned.ServerCertificate.PrivateKey), // PEM 89 "clientCA": string(selfSigned.ClientIntermediateCertificate.Certificate), // PEM 90 }) 91 helper.Must(err) 92 defer shutdown() 93 94 outreq, err := http.NewRequest(http.MethodGet, "https://localhost:4443/", nil) 95 helper.Must(err) 96 97 res, err := client.Do(outreq) 98 helper.Must(err) 99 100 if res.StatusCode != http.StatusOK { 101 t.Errorf("Expected statusOK, got: %d", res.StatusCode) 102 } 103 104 // without presenting the client certificate 105 client = test.NewHTTPSClient(&tls.Config{ 106 RootCAs: pool, 107 }) 108 109 _, err = client.Do(outreq) 110 111 var RemoteFailedCertCheck = func(err error) bool { 112 prefix := `Get "https://localhost:4443/": ` 113 suffix := "remote error: tls: bad certificate" 114 return err != nil && (err.Error() == prefix+"readLoopPeekFailLocked: "+suffix || 115 err.Error() == prefix+suffix) 116 } 117 118 if !RemoteFailedCertCheck(err) { 119 t.Errorf("Expected a tls handshake error, got: %v", err) 120 } 121 } 122 123 func TestHTTPSServer_TLS_ServerClientCertificateLeaf(t *testing.T) { 124 helper := test.New(t) 125 126 selfSigned, err := server.NewCertificate(time.Minute, nil, nil) 127 helper.Must(err) 128 129 pool := x509.NewCertPool() 130 pool.AddCert(selfSigned.CA.Leaf) 131 client := test.NewHTTPSClient(&tls.Config{ 132 RootCAs: pool, 133 Certificates: []tls.Certificate{*selfSigned.Client}, 134 }) 135 136 shutdown, _, err := newCouperWithTemplate("testdata/mtls/04_couper.hcl", helper, map[string]interface{}{ 137 "publicKey": string(selfSigned.ServerCertificate.Certificate), // PEM 138 "privateKey": string(selfSigned.ServerCertificate.PrivateKey), // PEM 139 "clientCA": string(selfSigned.ClientIntermediateCertificate.Certificate), // PEM 140 "clientLeaf": string(selfSigned.ClientCertificate.Certificate), // PEM 141 }) 142 helper.Must(err) 143 defer shutdown() 144 145 outreq, err := http.NewRequest(http.MethodGet, "https://localhost:4443/", nil) 146 helper.Must(err) 147 148 res, err := client.Do(outreq) 149 helper.Must(err) 150 151 if res.StatusCode != http.StatusOK { 152 t.Errorf("Expected statusOK, got: %d", res.StatusCode) 153 } 154 } 155 156 func TestHTTPSServer_TLS_ServerClientCertificateLeafNoMatch(t *testing.T) { 157 helper := test.New(t) 158 159 selfSigned, err := server.NewCertificate(time.Minute, nil, nil) 160 helper.Must(err) 161 162 pool := x509.NewCertPool() 163 pool.AddCert(selfSigned.CA.Leaf) 164 client := test.NewHTTPSClient(&tls.Config{ 165 RootCAs: pool, 166 Certificates: []tls.Certificate{*selfSigned.Client}, 167 }) 168 169 shutdown, hook, err := newCouperWithTemplate("testdata/mtls/04_couper.hcl", helper, map[string]interface{}{ 170 "publicKey": string(selfSigned.ServerCertificate.Certificate), // PEM 171 "privateKey": string(selfSigned.ServerCertificate.PrivateKey), // PEM 172 "clientCA": string(selfSigned.ClientIntermediateCertificate.Certificate), // PEM 173 "clientLeaf": string(selfSigned.CACertificate.Certificate), // PEM / just a non-matching one 174 }) 175 helper.Must(err) 176 defer shutdown() 177 178 outreq, err := http.NewRequest(http.MethodGet, "https://localhost:4443/", nil) 179 helper.Must(err) 180 181 hook.Reset() 182 183 _, err = client.Do(outreq) 184 if err == nil { 185 t.Error("expected a tls handshake error") 186 } 187 188 entries := hook.AllEntries() 189 if len(entries) == 0 { 190 t.Fatal("expected log entries") 191 } 192 193 if !strings.HasSuffix(entries[0].Message, "tls: client leaf certificate mismatch") { 194 t.Errorf("expected leaf mismatch err, got: %v", entries[0].Message) 195 } 196 } 197 198 func TestHTTPSServer_TLS_ServerClientLeafOnly(t *testing.T) { 199 helper := test.New(t) 200 201 selfSigned, err := server.NewCertificate(time.Minute, nil, nil) 202 helper.Must(err) 203 204 pool := x509.NewCertPool() 205 pool.AddCert(selfSigned.CA.Leaf) 206 client := test.NewHTTPSClient(&tls.Config{ 207 RootCAs: pool, 208 Certificates: []tls.Certificate{*selfSigned.Client}, 209 }) 210 211 shutdown, _, err := newCouperWithTemplate("testdata/mtls/06_couper.hcl", helper, map[string]interface{}{ 212 "publicKey": string(selfSigned.ServerCertificate.Certificate), // PEM 213 "privateKey": string(selfSigned.ServerCertificate.PrivateKey), // PEM 214 "clientLeaf": string(selfSigned.ClientCertificate.Certificate), // PEM 215 }) 216 helper.Must(err) 217 defer shutdown() 218 219 outreq, err := http.NewRequest(http.MethodGet, "https://localhost:4443/", nil) 220 helper.Must(err) 221 222 res, err := client.Do(outreq) 223 helper.Must(err) 224 225 if res.StatusCode != http.StatusOK { 226 t.Errorf("Expected statusOK, got: %d", res.StatusCode) 227 } 228 } 229 230 func TestHTTPSServer_TLS_ServerClientCertificateLeafMixed(t *testing.T) { 231 helper := test.New(t) 232 233 selfSigned1, err := server.NewCertificate(time.Hour, nil, nil) 234 helper.Must(err) 235 236 selfSigned2, err := server.NewCertificate(time.Hour, nil, nil) 237 helper.Must(err) 238 239 selfSigned3, err := server.NewCertificate(time.Hour, nil, nil) 240 helper.Must(err) 241 242 pool := x509.NewCertPool() 243 pool.AddCert(selfSigned1.CA.Leaf) 244 245 shutdown, _, err := newCouperWithTemplate("testdata/mtls/07_couper.hcl", helper, map[string]interface{}{ 246 "publicKey": string(selfSigned1.ServerCertificate.Certificate), // PEM 247 "privateKey": string(selfSigned1.ServerCertificate.PrivateKey), // PEM 248 "client1_Leaf": string(selfSigned1.ClientCertificate.Certificate), // PEM 249 "client2_CA": string(selfSigned2.ClientIntermediateCertificate.Certificate), // PEM 250 "client2_Leaf": string(selfSigned2.ClientCertificate.Certificate), // PEM 251 "client3_CA": string(selfSigned3.ClientIntermediateCertificate.Certificate), // PEM 252 }) 253 helper.Must(err) 254 defer shutdown() 255 256 for i, clientCert := range []*tls.Certificate{selfSigned1.Client, selfSigned2.Client, selfSigned3.Client} { 257 t.Run(fmt.Sprintf("client%d", i+1), func(st *testing.T) { 258 h := test.New(st) 259 c := clientCert 260 client := test.NewHTTPSClient(&tls.Config{ 261 RootCAs: pool, 262 GetClientCertificate: func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) { 263 return c, nil 264 }, 265 }) 266 267 outreq, e := http.NewRequest(http.MethodGet, "https://localhost:4443/", nil) 268 h.Must(e) 269 270 res, e := client.Do(outreq) 271 h.Must(e) 272 273 if res.StatusCode != http.StatusOK { 274 st.Errorf("Expected statusOK, got: %d", res.StatusCode) 275 } 276 }) 277 } 278 } 279 280 func TestHTTPSServer_TLS_ServerBackendClient(t *testing.T) { 281 helper := test.New(t) 282 283 selfSigned, err := server.NewCertificate(time.Minute, nil, nil) 284 helper.Must(err) 285 286 pool := x509.NewCertPool() 287 pool.AddCert(selfSigned.CA.Leaf) 288 client := test.NewHTTPSClient(&tls.Config{ 289 RootCAs: pool, 290 Certificates: []tls.Certificate{*selfSigned.Client}, 291 }) 292 293 shutdown, _, err := newCouperWithTemplate("testdata/mtls/05_couper.hcl", helper, map[string]interface{}{ 294 "publicKey": string(selfSigned.ServerCertificate.Certificate), // PEM 295 "privateKey": string(selfSigned.ServerCertificate.PrivateKey), // PEM 296 "clientCA": string(selfSigned.ClientIntermediateCertificate.Certificate), // PEM 297 "clientLeaf": string(selfSigned.ClientCertificate.Certificate), // PEM 298 "clientKey": string(selfSigned.ClientCertificate.PrivateKey), // PEM 299 "rootCA": string(selfSigned.CACertificate.Certificate), // PEM 300 }) 301 helper.Must(err) 302 defer shutdown() 303 304 outreq, err := http.NewRequest(http.MethodGet, "https://localhost:4443/inception", nil) 305 helper.Must(err) 306 307 res, err := client.Do(outreq) 308 helper.Must(err) 309 310 if res.StatusCode != http.StatusOK { 311 t.Errorf("Expected statusOK, got: %d", res.StatusCode) 312 } 313 }