github.com/Tyktechnologies/tyk@v2.9.5+incompatible/gateway/cert.go (about) 1 package gateway 2 3 import ( 4 "crypto/sha256" 5 "crypto/tls" 6 "crypto/x509" 7 "errors" 8 "io/ioutil" 9 "net" 10 "net/http" 11 "strconv" 12 "strings" 13 "sync" 14 "time" 15 16 "github.com/TykTechnologies/tyk/certs" 17 "github.com/TykTechnologies/tyk/config" 18 19 "github.com/gorilla/mux" 20 cache "github.com/pmylund/go-cache" 21 ) 22 23 type APICertificateStatusMessage struct { 24 CertID string `json:"id"` 25 Status string `json:"status"` 26 Message string `json:"message"` 27 } 28 29 type APIAllCertificates struct { 30 CertIDs []string `json:"certs"` 31 } 32 33 var cipherSuites = map[string]uint16{ 34 "TLS_RSA_WITH_RC4_128_SHA": 0x0005, 35 "TLS_RSA_WITH_3DES_EDE_CBC_SHA": 0x000a, 36 "TLS_RSA_WITH_AES_128_CBC_SHA": 0x002f, 37 "TLS_RSA_WITH_AES_256_CBC_SHA": 0x0035, 38 "TLS_RSA_WITH_AES_128_CBC_SHA256": 0x003c, 39 "TLS_RSA_WITH_AES_128_GCM_SHA256": 0x009c, 40 "TLS_RSA_WITH_AES_256_GCM_SHA384": 0x009d, 41 "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": 0xc007, 42 "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": 0xc009, 43 "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": 0xc00a, 44 "TLS_ECDHE_RSA_WITH_RC4_128_SHA": 0xc011, 45 "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": 0xc012, 46 "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": 0xc013, 47 "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": 0xc014, 48 "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": 0xc023, 49 "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": 0xc027, 50 "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": 0xc02f, 51 "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": 0xc02b, 52 "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": 0xc030, 53 "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": 0xc02c, 54 "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": 0xcca8, 55 "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": 0xcca9, 56 } 57 58 var certLog = log.WithField("prefix", "certs") 59 60 func getUpstreamCertificate(host string, spec *APISpec) (cert *tls.Certificate) { 61 var certID string 62 63 certMaps := []map[string]string{config.Global().Security.Certificates.Upstream} 64 65 if spec != nil && spec.UpstreamCertificates != nil { 66 certMaps = append(certMaps, spec.UpstreamCertificates) 67 } 68 69 for _, m := range certMaps { 70 if len(m) == 0 { 71 continue 72 } 73 74 if id, ok := m["*"]; ok { 75 certID = id 76 } 77 78 hostParts := strings.SplitN(host, ".", 2) 79 if len(hostParts) > 1 { 80 hostPattern := "*." + hostParts[1] 81 82 if id, ok := m[hostPattern]; ok { 83 certID = id 84 } 85 } 86 87 if id, ok := m[host]; ok { 88 certID = id 89 } 90 } 91 92 if certID == "" { 93 return nil 94 } 95 96 certs := CertificateManager.List([]string{certID}, certs.CertificatePrivate) 97 98 if len(certs) == 0 { 99 return nil 100 } 101 102 return certs[0] 103 } 104 105 func verifyPeerCertificatePinnedCheck(spec *APISpec, tlsConfig *tls.Config) func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { 106 if (spec == nil || len(spec.PinnedPublicKeys) == 0) && len(config.Global().Security.PinnedPublicKeys) == 0 { 107 return nil 108 } 109 110 tlsConfig.InsecureSkipVerify = true 111 112 whitelist := getPinnedPublicKeys("*", spec) 113 if len(whitelist) == 0 { 114 return nil 115 } 116 117 return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { 118 certLog.Debug("Checking certificate public key") 119 120 for _, rawCert := range rawCerts { 121 cert, _ := x509.ParseCertificate(rawCert) 122 pub, err := x509.MarshalPKIXPublicKey(cert.PublicKey) 123 if err != nil { 124 continue 125 } 126 127 fingerprint := certs.HexSHA256(pub) 128 129 for _, w := range whitelist { 130 if w == fingerprint { 131 return nil 132 } 133 } 134 } 135 136 return errors.New("Certificate public key pinning error. Public keys do not match.") 137 } 138 } 139 140 func validatePublicKeys(host string, conn *tls.Conn, spec *APISpec) bool { 141 certLog.Debug("Checking certificate public key for host:", host) 142 143 whitelist := getPinnedPublicKeys(host, spec) 144 if len(whitelist) == 0 { 145 return true 146 } 147 148 isValid := false 149 150 state := conn.ConnectionState() 151 for _, peercert := range state.PeerCertificates { 152 der, err := x509.MarshalPKIXPublicKey(peercert.PublicKey) 153 if err != nil { 154 continue 155 } 156 fingerprint := certs.HexSHA256(der) 157 158 for _, w := range whitelist { 159 if w == fingerprint { 160 isValid = true 161 break 162 } 163 } 164 } 165 166 return isValid 167 } 168 169 func validateCommonName(host string, cert *x509.Certificate) error { 170 certLog.Debug("Checking certificate CommonName for host :", host) 171 172 if cert.Subject.CommonName != host { 173 return errors.New("certificate had CN " + cert.Subject.CommonName + "expected " + host) 174 } 175 176 return nil 177 } 178 179 func customDialTLSCheck(spec *APISpec, tc *tls.Config) func(network, addr string) (net.Conn, error) { 180 var checkPinnedKeys, checkCommonName bool 181 182 if (spec != nil && len(spec.PinnedPublicKeys) != 0) || len(config.Global().Security.PinnedPublicKeys) != 0 { 183 checkPinnedKeys = true 184 } 185 186 if (spec != nil && spec.Proxy.Transport.SSLForceCommonNameCheck) || config.Global().SSLForceCommonNameCheck { 187 checkCommonName = true 188 } 189 190 if !checkCommonName && !checkPinnedKeys { 191 return nil 192 } 193 194 return func(network, addr string) (net.Conn, error) { 195 clone := tc.Clone() 196 clone.InsecureSkipVerify = true 197 198 c, err := tls.Dial(network, addr, clone) 199 if err != nil { 200 return c, err 201 } 202 203 host, _, _ := net.SplitHostPort(addr) 204 205 if checkPinnedKeys { 206 isValid := validatePublicKeys(host, c, spec) 207 if !isValid { 208 return nil, errors.New("https://" + host + " certificate public key pinning error. Public keys do not match.") 209 } 210 } 211 212 if checkCommonName { 213 state := c.ConnectionState() 214 leafCert := state.PeerCertificates[0] 215 err := validateCommonName(host, leafCert) 216 if err != nil { 217 return nil, err 218 } 219 } 220 221 return c, nil 222 } 223 } 224 225 func getPinnedPublicKeys(host string, spec *APISpec) (fingerprint []string) { 226 var keyIDs string 227 228 pinMaps := []map[string]string{config.Global().Security.PinnedPublicKeys} 229 230 if spec != nil && spec.PinnedPublicKeys != nil { 231 pinMaps = append(pinMaps, spec.PinnedPublicKeys) 232 } 233 234 for _, m := range pinMaps { 235 if len(m) == 0 { 236 continue 237 } 238 239 if id, ok := m["*"]; ok { 240 keyIDs = id 241 } 242 243 hostParts := strings.SplitN(host, ".", 2) 244 if len(hostParts) > 1 { 245 hostPattern := "*." + hostParts[1] 246 247 if id, ok := m[hostPattern]; ok { 248 keyIDs = id 249 } 250 } 251 252 if id, ok := m[host]; ok { 253 keyIDs = id 254 } 255 } 256 257 if keyIDs == "" { 258 return nil 259 } 260 261 return CertificateManager.ListPublicKeys(strings.Split(keyIDs, ",")) 262 } 263 264 // dummyGetCertificate needed because TLSConfig require setting Certificates array or GetCertificate function from start, even if it get overriden by `getTLSConfigForClient` 265 func dummyGetCertificate(*tls.ClientHelloInfo) (*tls.Certificate, error) { 266 return nil, nil 267 } 268 269 var tlsConfigCache = cache.New(60*time.Second, 60*time.Minute) 270 271 var tlsConfigMu sync.Mutex 272 273 func getTLSConfigForClient(baseConfig *tls.Config, listenPort int) func(hello *tls.ClientHelloInfo) (*tls.Config, error) { 274 // Supporting legacy certificate configuration 275 serverCerts := []tls.Certificate{} 276 certNameMap := map[string]*tls.Certificate{} 277 278 for _, certData := range config.Global().HttpServerOptions.Certificates { 279 cert, err := tls.LoadX509KeyPair(certData.CertFile, certData.KeyFile) 280 if err != nil { 281 log.Errorf("Server error: loadkeys: %s", err) 282 continue 283 } 284 serverCerts = append(serverCerts, cert) 285 certNameMap[certData.Name] = &cert 286 } 287 288 for _, cert := range CertificateManager.List(config.Global().HttpServerOptions.SSLCertificates, certs.CertificatePrivate) { 289 if cert != nil { 290 serverCerts = append(serverCerts, *cert) 291 } 292 } 293 294 baseConfig.Certificates = serverCerts 295 baseConfig.BuildNameToCertificate() 296 297 for name, cert := range certNameMap { 298 baseConfig.NameToCertificate[name] = cert 299 } 300 301 listenPortStr := strconv.Itoa(listenPort) 302 303 return func(hello *tls.ClientHelloInfo) (*tls.Config, error) { 304 if config, found := tlsConfigCache.Get(hello.ServerName + listenPortStr); found { 305 return config.(*tls.Config).Clone(), nil 306 } 307 308 newConfig := baseConfig.Clone() 309 310 // Avoiding Race 311 newConfig.Certificates = []tls.Certificate{} 312 for _, cert := range baseConfig.Certificates { 313 newConfig.Certificates = append(newConfig.Certificates, cert) 314 } 315 newConfig.BuildNameToCertificate() 316 for name, cert := range certNameMap { 317 newConfig.NameToCertificate[name] = cert 318 } 319 320 isControlAPI := (listenPort != 0 && config.Global().ControlAPIPort == listenPort) || (config.Global().ControlAPIHostname == hello.ServerName) 321 322 if isControlAPI && config.Global().Security.ControlAPIUseMutualTLS { 323 newConfig.ClientAuth = tls.RequireAndVerifyClientCert 324 newConfig.ClientCAs = CertificateManager.CertPool(config.Global().Security.Certificates.ControlAPI) 325 326 tlsConfigCache.Set(hello.ServerName, newConfig, cache.DefaultExpiration) 327 return newConfig, nil 328 } 329 330 apisMu.RLock() 331 defer apisMu.RUnlock() 332 333 newConfig.ClientCAs = x509.NewCertPool() 334 335 domainRequireCert := map[string]tls.ClientAuthType{} 336 for _, spec := range apiSpecs { 337 switch { 338 case spec.UseMutualTLSAuth: 339 if domainRequireCert[spec.Domain] == 0 { 340 // Require verification only if there is a single known domain for TLS auth, otherwise use previous value 341 domainRequireCert[spec.Domain] = tls.RequireAndVerifyClientCert 342 } else if domainRequireCert[spec.Domain] != tls.RequireAndVerifyClientCert { 343 // If we have another API on this domain, which is not mutual tls enabled, just ask for cert 344 domainRequireCert[spec.Domain] = tls.RequestClientCert 345 } 346 347 // If current domain match or empty, whitelist client certificates 348 if spec.Domain == "" || spec.Domain == hello.ServerName { 349 certIDs := append(spec.ClientCertificates, config.Global().Security.Certificates.API...) 350 351 for _, cert := range CertificateManager.List(certIDs, certs.CertificatePublic) { 352 if cert != nil { 353 newConfig.ClientCAs.AddCert(cert.Leaf) 354 } 355 } 356 } 357 case spec.Auth.UseCertificate: 358 // Dynamic certificate check required, falling back to HTTP level check 359 // TODO: Change to VerifyPeerCertificate hook instead, when possible 360 if domainRequireCert[spec.Domain] < tls.RequestClientCert { 361 domainRequireCert[spec.Domain] = tls.RequestClientCert 362 } 363 default: 364 // For APIs which do not use certificates, indicate that there is API for such domain already 365 if domainRequireCert[spec.Domain] == 0 { 366 domainRequireCert[spec.Domain] = -1 367 } else { 368 domainRequireCert[spec.Domain] = tls.RequestClientCert 369 } 370 } 371 372 // Dynamically add API specific certificates 373 if len(spec.Certificates) != 0 { 374 for _, cert := range CertificateManager.List(spec.Certificates, certs.CertificatePrivate) { 375 if cert == nil { 376 continue 377 } 378 newConfig.Certificates = append(newConfig.Certificates, *cert) 379 380 if cert != nil { 381 if len(cert.Leaf.Subject.CommonName) > 0 { 382 newConfig.NameToCertificate[cert.Leaf.Subject.CommonName] = cert 383 } 384 for _, san := range cert.Leaf.DNSNames { 385 newConfig.NameToCertificate[san] = cert 386 } 387 } 388 } 389 } 390 } 391 392 newConfig.ClientAuth = domainRequireCert[hello.ServerName] 393 if newConfig.ClientAuth == 0 { 394 newConfig.ClientAuth = domainRequireCert[""] 395 } 396 397 // Cache the config 398 tlsConfigCache.Set(hello.ServerName+listenPortStr, newConfig, cache.DefaultExpiration) 399 return newConfig, nil 400 } 401 } 402 403 func certHandler(w http.ResponseWriter, r *http.Request) { 404 certID := mux.Vars(r)["certID"] 405 406 switch r.Method { 407 case "POST": 408 content, err := ioutil.ReadAll(r.Body) 409 if err != nil { 410 doJSONWrite(w, 405, apiError("Malformed request body")) 411 return 412 } 413 414 orgID := r.URL.Query().Get("org_id") 415 var certID string 416 if certID, err = CertificateManager.Add(content, orgID); err != nil { 417 doJSONWrite(w, http.StatusForbidden, apiError(err.Error())) 418 return 419 } 420 421 doJSONWrite(w, http.StatusOK, &APICertificateStatusMessage{certID, "ok", "Certificate added"}) 422 case "GET": 423 if certID == "" { 424 orgID := r.URL.Query().Get("org_id") 425 426 certIds := CertificateManager.ListAllIds(orgID) 427 doJSONWrite(w, http.StatusOK, &APIAllCertificates{certIds}) 428 return 429 } 430 431 certIDs := strings.Split(certID, ",") 432 certificates := CertificateManager.List(certIDs, certs.CertificateAny) 433 434 if len(certIDs) == 1 { 435 if certificates[0] == nil { 436 doJSONWrite(w, http.StatusNotFound, apiError("Certificate with given SHA256 fingerprint not found")) 437 return 438 } 439 440 doJSONWrite(w, http.StatusOK, certs.ExtractCertificateMeta(certificates[0], certIDs[0])) 441 return 442 } else { 443 var meta []*certs.CertificateMeta 444 for ci, cert := range certificates { 445 if cert != nil { 446 meta = append(meta, certs.ExtractCertificateMeta(cert, certIDs[ci])) 447 } else { 448 meta = append(meta, nil) 449 } 450 } 451 452 doJSONWrite(w, http.StatusOK, meta) 453 return 454 } 455 case "DELETE": 456 orgID := r.URL.Query().Get("org_id") 457 if orgID == "" && len(certID) >= sha256.Size*2 { 458 orgID = certID[:len(certID)-sha256.Size*2] 459 } 460 CertificateManager.Delete(certID, orgID) 461 doJSONWrite(w, http.StatusOK, &apiStatusMessage{"ok", "removed"}) 462 } 463 } 464 465 func getCipherAliases(ciphers []string) (cipherCodes []uint16) { 466 for k, v := range cipherSuites { 467 for _, str := range ciphers { 468 if str == k { 469 cipherCodes = append(cipherCodes, v) 470 } 471 } 472 } 473 return cipherCodes 474 }