diff options
Diffstat (limited to 'core/https/setup.go')
-rw-r--r-- | core/https/setup.go | 321 |
1 files changed, 0 insertions, 321 deletions
diff --git a/core/https/setup.go b/core/https/setup.go deleted file mode 100644 index ec90e0284..000000000 --- a/core/https/setup.go +++ /dev/null @@ -1,321 +0,0 @@ -package https - -import ( - "bytes" - "crypto/tls" - "encoding/pem" - "io/ioutil" - "log" - "os" - "path/filepath" - "strconv" - "strings" - - "github.com/miekg/coredns/core/setup" - "github.com/miekg/coredns/middleware" - "github.com/miekg/coredns/server" -) - -// Setup sets up the TLS configuration and installs certificates that -// are specified by the user in the config file. All the automatic HTTPS -// stuff comes later outside of this function. -func Setup(c *setup.Controller) (middleware.Middleware, error) { - if c.Port == "80" { - c.TLS.Enabled = false - log.Printf("[WARNING] TLS disabled for %s.", c.Address()) - return nil, nil - } - c.TLS.Enabled = true - - // TODO(miek): disabled for now - return nil, nil - - for c.Next() { - var certificateFile, keyFile, loadDir, maxCerts string - - args := c.RemainingArgs() - switch len(args) { - case 1: - c.TLS.LetsEncryptEmail = args[0] - - // user can force-disable managed TLS this way - if c.TLS.LetsEncryptEmail == "off" { - c.TLS.Enabled = false - return nil, nil - } - case 2: - certificateFile = args[0] - keyFile = args[1] - c.TLS.Manual = true - } - - // Optional block with extra parameters - var hadBlock bool - for c.NextBlock() { - hadBlock = true - switch c.Val() { - case "protocols": - args := c.RemainingArgs() - if len(args) != 2 { - return nil, c.ArgErr() - } - value, ok := supportedProtocols[strings.ToLower(args[0])] - if !ok { - return nil, c.Errf("Wrong protocol name or protocol not supported '%s'", c.Val()) - } - c.TLS.ProtocolMinVersion = value - value, ok = supportedProtocols[strings.ToLower(args[1])] - if !ok { - return nil, c.Errf("Wrong protocol name or protocol not supported '%s'", c.Val()) - } - c.TLS.ProtocolMaxVersion = value - case "ciphers": - for c.NextArg() { - value, ok := supportedCiphersMap[strings.ToUpper(c.Val())] - if !ok { - return nil, c.Errf("Wrong cipher name or cipher not supported '%s'", c.Val()) - } - c.TLS.Ciphers = append(c.TLS.Ciphers, value) - } - case "clients": - c.TLS.ClientCerts = c.RemainingArgs() - if len(c.TLS.ClientCerts) == 0 { - return nil, c.ArgErr() - } - case "load": - c.Args(&loadDir) - c.TLS.Manual = true - case "max_certs": - c.Args(&maxCerts) - c.TLS.OnDemand = true - default: - return nil, c.Errf("Unknown keyword '%s'", c.Val()) - } - } - - // tls requires at least one argument if a block is not opened - if len(args) == 0 && !hadBlock { - return nil, c.ArgErr() - } - - // set certificate limit if on-demand TLS is enabled - if maxCerts != "" { - maxCertsNum, err := strconv.Atoi(maxCerts) - if err != nil || maxCertsNum < 1 { - return nil, c.Err("max_certs must be a positive integer") - } - if onDemandMaxIssue == 0 || int32(maxCertsNum) < onDemandMaxIssue { // keep the minimum; TODO: We have to do this because it is global; should be per-server or per-vhost... - onDemandMaxIssue = int32(maxCertsNum) - } - } - - // don't try to load certificates unless we're supposed to - if !c.TLS.Enabled || !c.TLS.Manual { - continue - } - - // load a single certificate and key, if specified - if certificateFile != "" && keyFile != "" { - err := cacheUnmanagedCertificatePEMFile(certificateFile, keyFile) - if err != nil { - return nil, c.Errf("Unable to load certificate and key files for %s: %v", c.Host, err) - } - log.Printf("[INFO] Successfully loaded TLS assets from %s and %s", certificateFile, keyFile) - } - - // load a directory of certificates, if specified - if loadDir != "" { - err := loadCertsInDir(c, loadDir) - if err != nil { - return nil, err - } - } - } - - setDefaultTLSParams(c.Config) - - return nil, nil -} - -// loadCertsInDir loads all the certificates/keys in dir, as long as -// the file ends with .pem. This method of loading certificates is -// modeled after haproxy, which expects the certificate and key to -// be bundled into the same file: -// https://cbonte.github.io/haproxy-dconv/configuration-1.5.html#5.1-crt -// -// This function may write to the log as it walks the directory tree. -func loadCertsInDir(c *setup.Controller, dir string) error { - return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { - if err != nil { - log.Printf("[WARNING] Unable to traverse into %s; skipping", path) - return nil - } - if info.IsDir() { - return nil - } - if strings.HasSuffix(strings.ToLower(info.Name()), ".pem") { - certBuilder, keyBuilder := new(bytes.Buffer), new(bytes.Buffer) - var foundKey bool // use only the first key in the file - - bundle, err := ioutil.ReadFile(path) - if err != nil { - return err - } - - for { - // Decode next block so we can see what type it is - var derBlock *pem.Block - derBlock, bundle = pem.Decode(bundle) - if derBlock == nil { - break - } - - if derBlock.Type == "CERTIFICATE" { - // Re-encode certificate as PEM, appending to certificate chain - pem.Encode(certBuilder, derBlock) - } else if derBlock.Type == "EC PARAMETERS" { - // EC keys generated from openssl can be composed of two blocks: - // parameters and key (parameter block should come first) - if !foundKey { - // Encode parameters - pem.Encode(keyBuilder, derBlock) - - // Key must immediately follow - derBlock, bundle = pem.Decode(bundle) - if derBlock == nil || derBlock.Type != "EC PRIVATE KEY" { - return c.Errf("%s: expected elliptic private key to immediately follow EC parameters", path) - } - pem.Encode(keyBuilder, derBlock) - foundKey = true - } - } else if derBlock.Type == "PRIVATE KEY" || strings.HasSuffix(derBlock.Type, " PRIVATE KEY") { - // RSA key - if !foundKey { - pem.Encode(keyBuilder, derBlock) - foundKey = true - } - } else { - return c.Errf("%s: unrecognized PEM block type: %s", path, derBlock.Type) - } - } - - certPEMBytes, keyPEMBytes := certBuilder.Bytes(), keyBuilder.Bytes() - if len(certPEMBytes) == 0 { - return c.Errf("%s: failed to parse PEM data", path) - } - if len(keyPEMBytes) == 0 { - return c.Errf("%s: no private key block found", path) - } - - err = cacheUnmanagedCertificatePEMBytes(certPEMBytes, keyPEMBytes) - if err != nil { - return c.Errf("%s: failed to load cert and key for %s: %v", path, c.Host, err) - } - log.Printf("[INFO] Successfully loaded TLS assets from %s", path) - } - return nil - }) -} - -// setDefaultTLSParams sets the default TLS cipher suites, protocol versions, -// and server preferences of a server.Config if they were not previously set -// (it does not overwrite; only fills in missing values). It will also set the -// port to 443 if not already set, TLS is enabled, TLS is manual, and the host -// does not equal localhost. -func setDefaultTLSParams(c *server.Config) { - // If no ciphers provided, use default list - if len(c.TLS.Ciphers) == 0 { - c.TLS.Ciphers = defaultCiphers - } - - // Not a cipher suite, but still important for mitigating protocol downgrade attacks - // (prepend since having it at end breaks http2 due to non-h2-approved suites before it) - c.TLS.Ciphers = append([]uint16{tls.TLS_FALLBACK_SCSV}, c.TLS.Ciphers...) - - // Set default protocol min and max versions - must balance compatibility and security - if c.TLS.ProtocolMinVersion == 0 { - c.TLS.ProtocolMinVersion = tls.VersionTLS10 - } - if c.TLS.ProtocolMaxVersion == 0 { - c.TLS.ProtocolMaxVersion = tls.VersionTLS12 - } - - // Prefer server cipher suites - c.TLS.PreferServerCipherSuites = true - - // Default TLS port is 443; only use if port is not manually specified, - // TLS is enabled, and the host is not localhost - if c.Port == "" && c.TLS.Enabled && (!c.TLS.Manual || c.TLS.OnDemand) && c.Host != "localhost" { - c.Port = "443" - } -} - -// Map of supported protocols. -// SSLv3 will be not supported in future release. -// HTTP/2 only supports TLS 1.2 and higher. -var supportedProtocols = map[string]uint16{ - "ssl3.0": tls.VersionSSL30, - "tls1.0": tls.VersionTLS10, - "tls1.1": tls.VersionTLS11, - "tls1.2": tls.VersionTLS12, -} - -// Map of supported ciphers, used only for parsing config. -// -// Note that, at time of writing, HTTP/2 blacklists 276 cipher suites, -// including all but two of the suites below (the two GCM suites). -// See https://http2.github.io/http2-spec/#BadCipherSuites -// -// TLS_FALLBACK_SCSV is not in this list because we manually ensure -// it is always added (even though it is not technically a cipher suite). -// -// This map, like any map, is NOT ORDERED. Do not range over this map. -var supportedCiphersMap = map[string]uint16{ - "ECDHE-RSA-AES256-GCM-SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - "ECDHE-ECDSA-AES256-GCM-SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - "ECDHE-RSA-AES128-GCM-SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - "ECDHE-ECDSA-AES128-GCM-SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - "ECDHE-RSA-AES128-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - "ECDHE-RSA-AES256-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - "ECDHE-ECDSA-AES256-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - "ECDHE-ECDSA-AES128-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, - "RSA-AES128-CBC-SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA, - "RSA-AES256-CBC-SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA, - "ECDHE-RSA-3DES-EDE-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - "RSA-3DES-EDE-CBC-SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, -} - -// List of supported cipher suites in descending order of preference. -// Ordering is very important! Getting the wrong order will break -// mainstream clients, especially with HTTP/2. -// -// Note that TLS_FALLBACK_SCSV is not in this list since it is always -// added manually. -var supportedCiphers = []uint16{ - tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, - tls.TLS_RSA_WITH_AES_256_CBC_SHA, - tls.TLS_RSA_WITH_AES_128_CBC_SHA, - tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, -} - -// List of all the ciphers we want to use by default -var defaultCiphers = []uint16{ - tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, - tls.TLS_RSA_WITH_AES_256_CBC_SHA, - tls.TLS_RSA_WITH_AES_128_CBC_SHA, -} |