github.com/dbernstein1/tyk@v2.9.0-beta9-dl-apic+incompatible/gateway/cert.go (about)

     1  package gateway
     2  
     3  import (
     4  	"crypto/tls"
     5  	"crypto/x509"
     6  	"errors"
     7  	"io/ioutil"
     8  	"net"
     9  	"net/http"
    10  	"strings"
    11  
    12  	"github.com/TykTechnologies/tyk/certs"
    13  	"github.com/TykTechnologies/tyk/config"
    14  
    15  	"github.com/gorilla/mux"
    16  )
    17  
    18  type APICertificateStatusMessage struct {
    19  	CertID  string `json:"id"`
    20  	Status  string `json:"status"`
    21  	Message string `json:"message"`
    22  }
    23  
    24  type APIAllCertificates struct {
    25  	CertIDs []string `json:"certs"`
    26  }
    27  
    28  var cipherSuites = map[string]uint16{
    29  	"TLS_RSA_WITH_RC4_128_SHA":                0x0005,
    30  	"TLS_RSA_WITH_3DES_EDE_CBC_SHA":           0x000a,
    31  	"TLS_RSA_WITH_AES_128_CBC_SHA":            0x002f,
    32  	"TLS_RSA_WITH_AES_256_CBC_SHA":            0x0035,
    33  	"TLS_RSA_WITH_AES_128_CBC_SHA256":         0x003c,
    34  	"TLS_RSA_WITH_AES_128_GCM_SHA256":         0x009c,
    35  	"TLS_RSA_WITH_AES_256_GCM_SHA384":         0x009d,
    36  	"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA":        0xc007,
    37  	"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA":    0xc009,
    38  	"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA":    0xc00a,
    39  	"TLS_ECDHE_RSA_WITH_RC4_128_SHA":          0xc011,
    40  	"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA":     0xc012,
    41  	"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA":      0xc013,
    42  	"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA":      0xc014,
    43  	"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": 0xc023,
    44  	"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256":   0xc027,
    45  	"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256":   0xc02f,
    46  	"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": 0xc02b,
    47  	"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384":   0xc030,
    48  	"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": 0xc02c,
    49  	"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305":    0xcca8,
    50  	"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305":  0xcca9,
    51  }
    52  
    53  var certLog = log.WithField("prefix", "certs")
    54  
    55  func getUpstreamCertificate(host string, spec *APISpec) (cert *tls.Certificate) {
    56  	var certID string
    57  
    58  	certMaps := []map[string]string{config.Global().Security.Certificates.Upstream}
    59  
    60  	if spec != nil && spec.UpstreamCertificates != nil {
    61  		certMaps = append(certMaps, spec.UpstreamCertificates)
    62  	}
    63  
    64  	for _, m := range certMaps {
    65  		if len(m) == 0 {
    66  			continue
    67  		}
    68  
    69  		if id, ok := m["*"]; ok {
    70  			certID = id
    71  		}
    72  
    73  		hostParts := strings.SplitN(host, ".", 2)
    74  		if len(hostParts) > 1 {
    75  			hostPattern := "*." + hostParts[1]
    76  
    77  			if id, ok := m[hostPattern]; ok {
    78  				certID = id
    79  			}
    80  		}
    81  
    82  		if id, ok := m[host]; ok {
    83  			certID = id
    84  		}
    85  	}
    86  
    87  	if certID == "" {
    88  		return nil
    89  	}
    90  
    91  	certs := CertificateManager.List([]string{certID}, certs.CertificatePrivate)
    92  
    93  	if len(certs) == 0 {
    94  		return nil
    95  	}
    96  
    97  	return certs[0]
    98  }
    99  
   100  func verifyPeerCertificatePinnedCheck(spec *APISpec, tlsConfig *tls.Config) func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
   101  	if (spec == nil || len(spec.PinnedPublicKeys) == 0) && len(config.Global().Security.PinnedPublicKeys) == 0 {
   102  		return nil
   103  	}
   104  
   105  	tlsConfig.InsecureSkipVerify = true
   106  
   107  	whitelist := getPinnedPublicKeys("*", spec)
   108  	if len(whitelist) == 0 {
   109  		return nil
   110  	}
   111  
   112  	return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
   113  		certLog.Debug("Checking certificate public key")
   114  
   115  		for _, rawCert := range rawCerts {
   116  			cert, _ := x509.ParseCertificate(rawCert)
   117  			pub, err := x509.MarshalPKIXPublicKey(cert.PublicKey)
   118  			if err != nil {
   119  				continue
   120  			}
   121  
   122  			fingerprint := certs.HexSHA256(pub)
   123  
   124  			for _, w := range whitelist {
   125  				if w == fingerprint {
   126  					return nil
   127  				}
   128  			}
   129  		}
   130  
   131  		return errors.New("Certificate public key pinning error. Public keys do not match.")
   132  	}
   133  }
   134  
   135  func dialTLSPinnedCheck(spec *APISpec, tc *tls.Config) func(network, addr string) (net.Conn, error) {
   136  	if (spec == nil || len(spec.PinnedPublicKeys) == 0) && len(config.Global().Security.PinnedPublicKeys) == 0 {
   137  		return nil
   138  	}
   139  
   140  	return func(network, addr string) (net.Conn, error) {
   141  		clone := tc.Clone()
   142  		clone.InsecureSkipVerify = true
   143  
   144  		c, err := tls.Dial(network, addr, clone)
   145  		if err != nil {
   146  			return c, err
   147  		}
   148  
   149  		host, _, _ := net.SplitHostPort(addr)
   150  		whitelist := getPinnedPublicKeys(host, spec)
   151  		if len(whitelist) == 0 {
   152  			return c, nil
   153  		}
   154  
   155  		certLog.Debug("Checking certificate public key for host:", host)
   156  
   157  		state := c.ConnectionState()
   158  		for _, peercert := range state.PeerCertificates {
   159  			der, err := x509.MarshalPKIXPublicKey(peercert.PublicKey)
   160  			if err != nil {
   161  				continue
   162  			}
   163  			fingerprint := certs.HexSHA256(der)
   164  
   165  			for _, w := range whitelist {
   166  				if w == fingerprint {
   167  					return c, nil
   168  				}
   169  			}
   170  		}
   171  
   172  		return nil, errors.New("https://" + host + " certificate public key pinning error. Public keys do not match.")
   173  	}
   174  }
   175  
   176  func getPinnedPublicKeys(host string, spec *APISpec) (fingerprint []string) {
   177  	var keyIDs string
   178  
   179  	pinMaps := []map[string]string{config.Global().Security.PinnedPublicKeys}
   180  
   181  	if spec != nil && spec.PinnedPublicKeys != nil {
   182  		pinMaps = append(pinMaps, spec.PinnedPublicKeys)
   183  	}
   184  
   185  	for _, m := range pinMaps {
   186  		if len(m) == 0 {
   187  			continue
   188  		}
   189  
   190  		if id, ok := m["*"]; ok {
   191  			keyIDs = id
   192  		}
   193  
   194  		hostParts := strings.SplitN(host, ".", 2)
   195  		if len(hostParts) > 1 {
   196  			hostPattern := "*." + hostParts[1]
   197  
   198  			if id, ok := m[hostPattern]; ok {
   199  				keyIDs = id
   200  			}
   201  		}
   202  
   203  		if id, ok := m[host]; ok {
   204  			keyIDs = id
   205  		}
   206  	}
   207  
   208  	if keyIDs == "" {
   209  		return nil
   210  	}
   211  
   212  	return CertificateManager.ListPublicKeys(strings.Split(keyIDs, ","))
   213  }
   214  
   215  // dummyGetCertificate needed because TLSConfig require setting Certificates array or GetCertificate function from start, even if it get overriden by `getTLSConfigForClient`
   216  func dummyGetCertificate(*tls.ClientHelloInfo) (*tls.Certificate, error) {
   217  	return nil, nil
   218  }
   219  
   220  func getTLSConfigForClient(baseConfig *tls.Config, listenPort int) func(hello *tls.ClientHelloInfo) (*tls.Config, error) {
   221  
   222  	// Supporting legacy certificate configuration
   223  	serverCerts := []tls.Certificate{}
   224  	certNameMap := map[string]*tls.Certificate{}
   225  
   226  	for _, certData := range config.Global().HttpServerOptions.Certificates {
   227  		cert, err := tls.LoadX509KeyPair(certData.CertFile, certData.KeyFile)
   228  		if err != nil {
   229  			log.Errorf("Server error: loadkeys: %s", err)
   230  			continue
   231  		}
   232  		serverCerts = append(serverCerts, cert)
   233  		certNameMap[certData.Name] = &cert
   234  	}
   235  
   236  	for _, cert := range CertificateManager.List(config.Global().HttpServerOptions.SSLCertificates, certs.CertificatePrivate) {
   237  		serverCerts = append(serverCerts, *cert)
   238  	}
   239  
   240  	baseConfig.Certificates = serverCerts
   241  
   242  	baseConfig.BuildNameToCertificate()
   243  	for name, cert := range certNameMap {
   244  		baseConfig.NameToCertificate[name] = cert
   245  	}
   246  
   247  	return func(hello *tls.ClientHelloInfo) (*tls.Config, error) {
   248  		newConfig := baseConfig.Clone()
   249  
   250  		isControlAPI := (listenPort != 0 && config.Global().ControlAPIPort == listenPort) || (config.Global().ControlAPIHostname == hello.ServerName)
   251  
   252  		if isControlAPI && config.Global().Security.ControlAPIUseMutualTLS {
   253  			newConfig.ClientAuth = tls.RequireAndVerifyClientCert
   254  			newConfig.ClientCAs = CertificateManager.CertPool(config.Global().Security.Certificates.ControlAPI)
   255  
   256  			return newConfig, nil
   257  		}
   258  
   259  		apisMu.RLock()
   260  		defer apisMu.RUnlock()
   261  
   262  		// Dynamically add API specific certificates
   263  		for _, spec := range apiSpecs {
   264  			if len(spec.Certificates) != 0 {
   265  				for _, cert := range CertificateManager.List(spec.Certificates, certs.CertificatePrivate) {
   266  					newConfig.Certificates = append(newConfig.Certificates, *cert)
   267  
   268  					if cert != nil {
   269  						if len(cert.Leaf.Subject.CommonName) > 0 {
   270  							newConfig.NameToCertificate[cert.Leaf.Subject.CommonName] = cert
   271  						}
   272  						for _, san := range cert.Leaf.DNSNames {
   273  							newConfig.NameToCertificate[san] = cert
   274  						}
   275  					}
   276  				}
   277  			}
   278  		}
   279  
   280  		for _, spec := range apiSpecs {
   281  			if spec.UseMutualTLSAuth && spec.Domain != "" && spec.Domain == hello.ServerName {
   282  				newConfig.ClientAuth = tls.RequireAndVerifyClientCert
   283  				certIDs := append(spec.ClientCertificates, config.Global().Security.Certificates.API...)
   284  				newConfig.ClientCAs = CertificateManager.CertPool(certIDs)
   285  				break
   286  			}
   287  		}
   288  
   289  		// No mutual tls APIs with matched domain found
   290  		// Check if one of APIs without domain, require asking client cert
   291  		if newConfig.ClientAuth == tls.NoClientCert {
   292  			for _, spec := range apiSpecs {
   293  				if spec.Auth.UseCertificate || (spec.Domain == "" && spec.UseMutualTLSAuth) {
   294  					newConfig.ClientAuth = tls.RequestClientCert
   295  					break
   296  				}
   297  			}
   298  		}
   299  
   300  		return newConfig, nil
   301  	}
   302  }
   303  
   304  func certHandler(w http.ResponseWriter, r *http.Request) {
   305  	certID := mux.Vars(r)["certID"]
   306  
   307  	switch r.Method {
   308  	case "POST":
   309  		content, err := ioutil.ReadAll(r.Body)
   310  		if err != nil {
   311  			doJSONWrite(w, 405, apiError("Malformed request body"))
   312  			return
   313  		}
   314  
   315  		orgID := r.URL.Query().Get("org_id")
   316  		var certID string
   317  		if certID, err = CertificateManager.Add(content, orgID); err != nil {
   318  			doJSONWrite(w, http.StatusForbidden, apiError(err.Error()))
   319  			return
   320  		}
   321  
   322  		doJSONWrite(w, http.StatusOK, &APICertificateStatusMessage{certID, "ok", "Certificate added"})
   323  	case "GET":
   324  		if certID == "" {
   325  			orgID := r.URL.Query().Get("org_id")
   326  
   327  			certIds := CertificateManager.ListAllIds(orgID)
   328  			doJSONWrite(w, http.StatusOK, &APIAllCertificates{certIds})
   329  			return
   330  		}
   331  
   332  		certIDs := strings.Split(certID, ",")
   333  		certificates := CertificateManager.List(certIDs, certs.CertificateAny)
   334  
   335  		if len(certIDs) == 1 {
   336  			if certificates[0] == nil {
   337  				doJSONWrite(w, http.StatusNotFound, apiError("Certificate with given SHA256 fingerprint not found"))
   338  				return
   339  			}
   340  
   341  			doJSONWrite(w, http.StatusOK, certs.ExtractCertificateMeta(certificates[0], certIDs[0]))
   342  			return
   343  		} else {
   344  			var meta []*certs.CertificateMeta
   345  			for ci, cert := range certificates {
   346  				if cert != nil {
   347  					meta = append(meta, certs.ExtractCertificateMeta(cert, certIDs[ci]))
   348  				} else {
   349  					meta = append(meta, nil)
   350  				}
   351  			}
   352  
   353  			doJSONWrite(w, http.StatusOK, meta)
   354  			return
   355  		}
   356  	case "DELETE":
   357  		CertificateManager.Delete(certID)
   358  		doJSONWrite(w, http.StatusOK, &apiStatusMessage{"ok", "removed"})
   359  	}
   360  }
   361  
   362  func getCipherAliases(ciphers []string) (cipherCodes []uint16) {
   363  	for k, v := range cipherSuites {
   364  		for _, str := range ciphers {
   365  			if str == k {
   366  				cipherCodes = append(cipherCodes, v)
   367  			}
   368  		}
   369  	}
   370  	return cipherCodes
   371  }