-
-
Notifications
You must be signed in to change notification settings - Fork 5.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make SSL cipher suite configurable #17440
Changes from 4 commits
b7843af
94500d5
0c547f0
9369a75
7ac1540
7cb3835
01f13e5
8934cd9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
// Copyright 2021 The Gitea Authors. All rights reserved. | ||
// Use of this source code is governed by a MIT-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package cmd | ||
|
||
import ( | ||
"crypto/tls" | ||
"net/http" | ||
"os" | ||
"strings" | ||
|
||
"code.gitea.io/gitea/modules/graceful" | ||
"code.gitea.io/gitea/modules/log" | ||
"code.gitea.io/gitea/modules/setting" | ||
"github.com/klauspost/cpuid/v2" | ||
) | ||
|
||
func toTLSVersion(version string) uint16 { | ||
switch strings.TrimSpace(strings.ToLower(version)) { | ||
case "tlsv1.0": | ||
return tls.VersionTLS10 | ||
case "tlsv1.1": | ||
return tls.VersionTLS11 | ||
case "tlsv1.2", "": // Set TLSv1.2 as our default | ||
return tls.VersionTLS12 | ||
case "tlsv1.3": | ||
return tls.VersionTLS13 | ||
default: | ||
log.Warn("Unknown tls version: %s", version) | ||
return 0 | ||
} | ||
} | ||
|
||
func toCurvePreferences(preferences []string) []tls.CurveID { | ||
ids := make([]tls.CurveID, 0, len(preferences)) | ||
for _, pref := range preferences { | ||
var id tls.CurveID | ||
switch strings.TrimSpace(strings.ToLower(pref)) { | ||
case "x25519": | ||
id = tls.X25519 | ||
case "p256": | ||
id = tls.CurveP256 | ||
case "p384": | ||
id = tls.CurveP384 | ||
case "p521": | ||
id = tls.CurveP521 | ||
default: | ||
log.Warn("Unknown curve: %s", pref) | ||
} | ||
if id != 0 { | ||
ids = append(ids, id) | ||
} | ||
} | ||
return ids | ||
} | ||
|
||
func toTLSCiphers(cipherStrings []string) []uint16 { | ||
ciphers := make([]uint16, 0, len(cipherStrings)) | ||
for _, cipherString := range cipherStrings { | ||
var cipher uint16 | ||
switch strings.TrimSpace(strings.ToLower(cipherString)) { | ||
case "rsa_with_rc4_128_sha": | ||
cipher = tls.TLS_RSA_WITH_RC4_128_SHA | ||
case "rsa_with_3des_ede_cbc_sha": | ||
cipher = tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA | ||
case "rsa_with_aes_128_cbc_sha": | ||
cipher = tls.TLS_RSA_WITH_AES_128_CBC_SHA | ||
case "rsa_with_aes_256_cbc_sha": | ||
cipher = tls.TLS_RSA_WITH_AES_256_CBC_SHA | ||
case "rsa_with_aes_128_cbc_sha256": | ||
cipher = tls.TLS_RSA_WITH_AES_128_CBC_SHA256 | ||
case "rsa_with_aes_128_gcm_sha256": | ||
cipher = tls.TLS_RSA_WITH_AES_128_GCM_SHA256 | ||
case "rsa_with_aes_256_gcm_sha384": | ||
cipher = tls.TLS_RSA_WITH_AES_256_GCM_SHA384 | ||
case "ecdhe_ecdsa_with_rc4_128_sha": | ||
cipher = tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA | ||
case "ecdhe_ecdsa_with_aes_128_cbc_sha": | ||
cipher = tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA | ||
case "ecdhe_ecdsa_with_aes_256_cbc_sha": | ||
cipher = tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA | ||
case "ecdhe_rsa_with_rc4_128_sha": | ||
cipher = tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA | ||
case "ecdhe_rsa_with_3des_ede_cbc_sha": | ||
cipher = tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA | ||
case "ecdhe_rsa_with_aes_128_cbc_sha": | ||
cipher = tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA | ||
case "ecdhe_rsa_with_aes_256_cbc_sha": | ||
cipher = tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA | ||
case "ecdhe_ecdsa_with_aes_128_cbc_sha256": | ||
cipher = tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 | ||
case "ecdhe_rsa_with_aes_128_cbc_sha256": | ||
cipher = tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 | ||
case "ecdhe_rsa_with_aes_128_gcm_sha256": | ||
cipher = tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 | ||
case "ecdhe_ecdsa_with_aes_128_gcm_sha256": | ||
cipher = tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 | ||
case "ecdhe_rsa_with_aes_256_gcm_sha384": | ||
cipher = tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 | ||
case "ecdhe_ecdsa_with_aes_256_gcm_sha384": | ||
cipher = tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 | ||
case "ecdhe_rsa_with_chacha20_poly1305_sha256": | ||
cipher = tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 | ||
case "ecdhe_ecdsa_with_chacha20_poly1305_sha256": | ||
cipher = tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 | ||
case "ecdhe_rsa_with_chacha20_poly1305": | ||
cipher = tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 | ||
case "ecdhe_ecdsa_with_chacha20_poly1305": | ||
cipher = tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 | ||
case "aes_128_gcm_sha256": | ||
cipher = tls.TLS_AES_128_GCM_SHA256 | ||
case "aes_256_gcm_sha384": | ||
cipher = tls.TLS_AES_256_GCM_SHA384 | ||
case "chacha20_poly1305_sha256": | ||
cipher = tls.TLS_CHACHA20_POLY1305_SHA256 | ||
default: | ||
log.Warn("Unknown cipher: %s", cipherString) | ||
} | ||
if cipher != 0 { | ||
ciphers = append(ciphers, cipher) | ||
} | ||
} | ||
|
||
return ciphers | ||
} | ||
|
||
// defaultCiphers uses hardware support to check if AES is specifically | ||
// supported by the CPU. | ||
// | ||
// If it is AES ciphers will be preferred over ChaCha based ciphers | ||
func defaultCiphers() []uint16 { | ||
if cpuid.CPU.Supports(cpuid.AESNI) { | ||
return defaultCiphersAESfirst | ||
} | ||
return defaultCiphersChaChaFirst | ||
} | ||
|
||
var ( | ||
defaultCiphersAES = []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, | ||
} | ||
|
||
defaultCiphersChaCha = []uint16{ | ||
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, | ||
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, | ||
} | ||
|
||
defaultCiphersAESfirst = append(defaultCiphersAES, defaultCiphersChaCha...) | ||
defaultCiphersChaChaFirst = append(defaultCiphersChaCha, defaultCiphersAES...) | ||
Comment on lines
+127
to
+128
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We might want to add the whole list as well which golang uses as well. https://github.com/golang/go/blob/master/src/crypto/tls/cipher_suites.go#L271 (Skip the first 6 as they are "defaultCiphersChaCha" and "defaultCiphersAES") There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have copied these defaults from They represent a reasonable choice of default of ciphers when we default to MinTLSVersion 1.2. |
||
) | ||
|
||
// runHTTPs listens on the provided network address and then calls | ||
// Serve to handle requests on incoming TLS connections. | ||
// | ||
// Filenames containing a certificate and matching private key for the server must | ||
// be provided. If the certificate is signed by a certificate authority, the | ||
// certFile should be the concatenation of the server's certificate followed by the | ||
// CA's certificate. | ||
func runHTTPS(network, listenAddr, name, certFile, keyFile string, m http.Handler) error { | ||
tlsConfig := &tls.Config{} | ||
if tlsConfig.NextProtos == nil { | ||
tlsConfig.NextProtos = []string{"h2", "http/1.1"} | ||
} | ||
|
||
if version := toTLSVersion(setting.SSLMinimumVersion); version != 0 { | ||
tlsConfig.MinVersion = version | ||
} | ||
zeripath marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if version := toTLSVersion(setting.SSLMaximumVersion); version != 0 { | ||
tlsConfig.MaxVersion = version | ||
} | ||
|
||
// Set curve preferences | ||
tlsConfig.CurvePreferences = []tls.CurveID{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Golang will by default set a good curvePreference which is bit less restrictive than this one, not sure if it would make sense to set our own then: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have copied these defaults from They represent a reasonable choice of default of ciphers when we default to MinTLSVersion 1.2. If users want to change their TLS version they need to know how to set things themselves. |
||
tls.X25519, | ||
tls.CurveP256, | ||
} | ||
if curves := toCurvePreferences(setting.SSLCurvePreferences); len(curves) > 0 { | ||
tlsConfig.CurvePreferences = curves | ||
} | ||
|
||
// Set cipher suites | ||
tlsConfig.CipherSuites = defaultCiphers() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto as above. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The less we say the better. |
||
if ciphers := toTLSCiphers(setting.SSLCipherSuites); len(ciphers) > 0 { | ||
tlsConfig.CipherSuites = ciphers | ||
} | ||
|
||
tlsConfig.Certificates = make([]tls.Certificate, 1) | ||
|
||
certPEMBlock, err := os.ReadFile(certFile) | ||
if err != nil { | ||
log.Error("Failed to load https cert file %s for %s:%s: %v", certFile, network, listenAddr, err) | ||
return err | ||
} | ||
|
||
keyPEMBlock, err := os.ReadFile(keyFile) | ||
if err != nil { | ||
log.Error("Failed to load https key file %s for %s:%s: %v", keyFile, network, listenAddr, err) | ||
return err | ||
} | ||
|
||
tlsConfig.Certificates[0], err = tls.X509KeyPair(certPEMBlock, keyPEMBlock) | ||
if err != nil { | ||
log.Error("Failed to create certificate from cert file %s and key file %s for %s:%s: %v", certFile, keyFile, network, listenAddr, err) | ||
return err | ||
} | ||
|
||
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m) | ||
} | ||
|
||
func runHTTPSWithTLSConfig(network, listenAddr, name string, tlsConfig *tls.Config, m http.Handler) error { | ||
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,6 +51,16 @@ RUN_MODE = ; prod | |
;REDIRECT_OTHER_PORT = false | ||
;PORT_TO_REDIRECT = 80 | ||
;; | ||
;; Minimum and maximum supported TLS versions | ||
;SSL_MIN_VERSION=TLSv1.2 | ||
;SSL_MAX_VERSION= | ||
;; | ||
;; SSL Curve Preferences | ||
;SSL_CURVE_PREFERENCES=X25519,P256 | ||
;; | ||
;; SSL Cipher Suites | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be noted here about TLSv1.3 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I disagree. The less we say the better. |
||
;SSL_CIPHER_SUITES=; Will default to "ecdhe_ecdsa_with_aes_256_gcm_sha384,ecdhe_rsa_with_aes_256_gcm_sha384,ecdhe_ecdsa_with_aes_128_gcm_sha256,ecdhe_rsa_with_aes_128_gcm_sha256,ecdhe_ecdsa_with_chacha20_poly1305,ecdhe_rsa_with_chacha20_poly1305" if aes is supported by hardware, otherwise chacha will be first. | ||
;; | ||
;; Timeout for any write to the connection. (Set to 0 to disable all timeouts.) | ||
;PER_WRITE_TIMEOUT = 30s | ||
;; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to note that these default ciphers are recommended for TLS v1.0-v.1.2
TLS v1.3 has another default cipher recommendation IIRC.
IIRC(please check it up) AES support:
AES-128 GCM with SHA256
AES-256 GCM with SHA384
No-AES support:
ChaCha20-Poly1305 with SHA256
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To give another note on TLS v1.3 - all chiper suites are secure enough that TLS v1.3 supports, thus golang currently will disregard custom cipher suites when TLS v1.3 is used - Might be worth noting somewhere to avoid confusion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have copied these defaults from
https://github.com/caddyserver/certmagic/blob/f83201861a3710cd9e062ea86954928854a0b57b/crypto.go#L289-L318
They represent a reasonable choice of default of ciphers when we default to MinTLSVersion 1.2. If users want to change their TLS version they need to know how to set things themselves.