Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 64398de

Browse files
authored
feat: add configurable cipher suites for tls listening (coder#10505)
* feat: add configurable cipher suites for tls listening * tls.VersionName is go 1.21, copy the function
1 parent e36503a commit 64398de

File tree

13 files changed

+478
-33
lines changed

13 files changed

+478
-33
lines changed

cli/server.go

+179-6
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
354354
logger.Debug(ctx, "tracing closed", slog.Error(traceCloseErr))
355355
}()
356356

357-
httpServers, err := ConfigureHTTPServers(inv, vals)
357+
httpServers, err := ConfigureHTTPServers(logger, inv, vals)
358358
if err != nil {
359359
return xerrors.Errorf("configure http(s): %w", err)
360360
}
@@ -1021,7 +1021,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
10211021
r.Verbosef(inv, "Shutting down provisioner daemon %d...", id)
10221022
err := shutdownWithTimeout(provisionerDaemon.Shutdown, 5*time.Second)
10231023
if err != nil {
1024-
cliui.Errorf(inv.Stderr, "Failed to shutdown provisioner daemon %d: %s\n", id, err)
1024+
cliui.Errorf(inv.Stderr, "Failed to shut down provisioner daemon %d: %s\n", id, err)
10251025
return
10261026
}
10271027
err = provisionerDaemon.Close()
@@ -1411,7 +1411,12 @@ func generateSelfSignedCertificate() (*tls.Certificate, error) {
14111411
return &cert, nil
14121412
}
14131413

1414-
func configureTLS(tlsMinVersion, tlsClientAuth string, tlsCertFiles, tlsKeyFiles []string, tlsClientCAFile string) (*tls.Config, error) {
1414+
// configureServerTLS returns the TLS config used for the Coderd server
1415+
// connections to clients. A logger is passed in to allow printing warning
1416+
// messages that do not block startup.
1417+
//
1418+
//nolint:revive
1419+
func configureServerTLS(ctx context.Context, logger slog.Logger, tlsMinVersion, tlsClientAuth string, tlsCertFiles, tlsKeyFiles []string, tlsClientCAFile string, ciphers []string, allowInsecureCiphers bool) (*tls.Config, error) {
14151420
tlsConfig := &tls.Config{
14161421
MinVersion: tls.VersionTLS12,
14171422
NextProtos: []string{"h2", "http/1.1"},
@@ -1429,6 +1434,15 @@ func configureTLS(tlsMinVersion, tlsClientAuth string, tlsCertFiles, tlsKeyFiles
14291434
return nil, xerrors.Errorf("unrecognized tls version: %q", tlsMinVersion)
14301435
}
14311436

1437+
// A custom set of supported ciphers.
1438+
if len(ciphers) > 0 {
1439+
cipherIDs, err := configureCipherSuites(ctx, logger, ciphers, allowInsecureCiphers, tlsConfig.MinVersion, tls.VersionTLS13)
1440+
if err != nil {
1441+
return nil, err
1442+
}
1443+
tlsConfig.CipherSuites = cipherIDs
1444+
}
1445+
14321446
switch tlsClientAuth {
14331447
case "none":
14341448
tlsConfig.ClientAuth = tls.NoClientCert
@@ -1487,6 +1501,160 @@ func configureTLS(tlsMinVersion, tlsClientAuth string, tlsCertFiles, tlsKeyFiles
14871501
return tlsConfig, nil
14881502
}
14891503

1504+
//nolint:revive
1505+
func configureCipherSuites(ctx context.Context, logger slog.Logger, ciphers []string, allowInsecureCiphers bool, minTLS, maxTLS uint16) ([]uint16, error) {
1506+
if minTLS > maxTLS {
1507+
return nil, xerrors.Errorf("minimum tls version (%s) cannot be greater than maximum tls version (%s)", versionName(minTLS), versionName(maxTLS))
1508+
}
1509+
if minTLS >= tls.VersionTLS13 {
1510+
// The cipher suites config option is ignored for tls 1.3 and higher.
1511+
// So this user flag is a no-op if the min version is 1.3.
1512+
return nil, xerrors.Errorf("'--tls-ciphers' cannot be specified when using minimum tls version 1.3 or higher, %d ciphers found as input.", len(ciphers))
1513+
}
1514+
// Configure the cipher suites which parses the strings and converts them
1515+
// to golang cipher suites.
1516+
supported, err := parseTLSCipherSuites(ciphers)
1517+
if err != nil {
1518+
return nil, xerrors.Errorf("tls ciphers: %w", err)
1519+
}
1520+
1521+
// allVersions is all tls versions the server supports.
1522+
// We enumerate these to ensure if ciphers are configured, at least
1523+
// 1 cipher for each version exists.
1524+
allVersions := make(map[uint16]bool)
1525+
for v := minTLS; v <= maxTLS; v++ {
1526+
allVersions[v] = false
1527+
}
1528+
1529+
var insecure []string
1530+
cipherIDs := make([]uint16, 0, len(supported))
1531+
for _, cipher := range supported {
1532+
if cipher.Insecure {
1533+
// Always show this warning, even if they have allowInsecureCiphers
1534+
// specified.
1535+
logger.Warn(ctx, "insecure tls cipher specified for server use", slog.F("cipher", cipher.Name))
1536+
insecure = append(insecure, cipher.Name)
1537+
}
1538+
1539+
// This is a warning message to tell the user if they are specifying
1540+
// a cipher that does not support the tls versions they have specified.
1541+
// This makes the cipher essentially a "noop" cipher.
1542+
if !hasSupportedVersion(minTLS, maxTLS, cipher.SupportedVersions) {
1543+
versions := make([]string, 0, len(cipher.SupportedVersions))
1544+
for _, sv := range cipher.SupportedVersions {
1545+
versions = append(versions, versionName(sv))
1546+
}
1547+
logger.Warn(ctx, "cipher not supported for tls versions enabled, cipher will not be used",
1548+
slog.F("cipher", cipher.Name),
1549+
slog.F("cipher_supported_versions", strings.Join(versions, ",")),
1550+
slog.F("server_min_version", versionName(minTLS)),
1551+
slog.F("server_max_version", versionName(maxTLS)),
1552+
)
1553+
}
1554+
1555+
for _, v := range cipher.SupportedVersions {
1556+
allVersions[v] = true
1557+
}
1558+
1559+
cipherIDs = append(cipherIDs, cipher.ID)
1560+
}
1561+
1562+
if len(insecure) > 0 && !allowInsecureCiphers {
1563+
return nil, xerrors.Errorf("insecure tls ciphers specified, must use '--tls-allow-insecure-ciphers' to allow these: %s", strings.Join(insecure, ", "))
1564+
}
1565+
1566+
// This is an additional sanity check. The user can specify ciphers that
1567+
// do not cover the full range of tls versions they have specified.
1568+
// They can unintentionally break TLS for some tls configured versions.
1569+
var missedVersions []string
1570+
for version, covered := range allVersions {
1571+
if version == tls.VersionTLS13 {
1572+
continue // v1.3 ignores configured cipher suites.
1573+
}
1574+
if !covered {
1575+
missedVersions = append(missedVersions, versionName(version))
1576+
}
1577+
}
1578+
if len(missedVersions) > 0 {
1579+
return nil, xerrors.Errorf("no tls ciphers supported for tls versions %q."+
1580+
"Add additional ciphers, set the minimum version to 'tls13, or remove the ciphers configured and rely on the default",
1581+
strings.Join(missedVersions, ","))
1582+
}
1583+
1584+
return cipherIDs, nil
1585+
}
1586+
1587+
// parseTLSCipherSuites will parse cipher suite names like 'TLS_RSA_WITH_AES_128_CBC_SHA'
1588+
// to their tls cipher suite structs. If a cipher suite that is unsupported is
1589+
// passed in, this function will return an error.
1590+
// This function can return insecure cipher suites.
1591+
func parseTLSCipherSuites(ciphers []string) ([]tls.CipherSuite, error) {
1592+
if len(ciphers) == 0 {
1593+
return nil, nil
1594+
}
1595+
1596+
var unsupported []string
1597+
var supported []tls.CipherSuite
1598+
// A custom set of supported ciphers.
1599+
allCiphers := append(tls.CipherSuites(), tls.InsecureCipherSuites()...)
1600+
for _, cipher := range ciphers {
1601+
// For each cipher specified by the client, find the cipher in the
1602+
// list of golang supported ciphers.
1603+
var found *tls.CipherSuite
1604+
for _, supported := range allCiphers {
1605+
if strings.EqualFold(supported.Name, cipher) {
1606+
found = supported
1607+
break
1608+
}
1609+
}
1610+
1611+
if found == nil {
1612+
unsupported = append(unsupported, cipher)
1613+
continue
1614+
}
1615+
1616+
supported = append(supported, *found)
1617+
}
1618+
1619+
if len(unsupported) > 0 {
1620+
return nil, xerrors.Errorf("unsupported tls ciphers specified, see https://github.com/golang/go/blob/master/src/crypto/tls/cipher_suites.go#L53-L75: %s", strings.Join(unsupported, ", "))
1621+
}
1622+
1623+
return supported, nil
1624+
}
1625+
1626+
// hasSupportedVersion is a helper function that returns true if the list
1627+
// of supported versions contains a version between min and max.
1628+
// If the versions list is outside the min/max, then it returns false.
1629+
func hasSupportedVersion(min, max uint16, versions []uint16) bool {
1630+
for _, v := range versions {
1631+
if v >= min && v <= max {
1632+
// If one version is in between min/max, return true.
1633+
return true
1634+
}
1635+
}
1636+
return false
1637+
}
1638+
1639+
// versionName is tls.VersionName in go 1.21.
1640+
// Until the switch, the function is copied locally.
1641+
func versionName(version uint16) string {
1642+
switch version {
1643+
case tls.VersionSSL30:
1644+
return "SSLv3"
1645+
case tls.VersionTLS10:
1646+
return "TLS 1.0"
1647+
case tls.VersionTLS11:
1648+
return "TLS 1.1"
1649+
case tls.VersionTLS12:
1650+
return "TLS 1.2"
1651+
case tls.VersionTLS13:
1652+
return "TLS 1.3"
1653+
default:
1654+
return fmt.Sprintf("0x%04X", version)
1655+
}
1656+
}
1657+
14901658
func configureOIDCPKI(orig *oauth2.Config, keyFile string, certFile string) (*oauthpki.Config, error) {
14911659
// Read the files
14921660
keyData, err := os.ReadFile(keyFile)
@@ -2078,7 +2246,8 @@ func ConfigureTraceProvider(
20782246
return tracerProvider, sqlDriver, closeTracing
20792247
}
20802248

2081-
func ConfigureHTTPServers(inv *clibase.Invocation, cfg *codersdk.DeploymentValues) (_ *HTTPServers, err error) {
2249+
func ConfigureHTTPServers(logger slog.Logger, inv *clibase.Invocation, cfg *codersdk.DeploymentValues) (_ *HTTPServers, err error) {
2250+
ctx := inv.Context()
20822251
httpServers := &HTTPServers{}
20832252
defer func() {
20842253
if err != nil {
@@ -2154,16 +2323,20 @@ func ConfigureHTTPServers(inv *clibase.Invocation, cfg *codersdk.DeploymentValue
21542323
// DEPRECATED: This redirect used to default to true.
21552324
// It made more sense to have the redirect be opt-in.
21562325
if inv.Environ.Get("CODER_TLS_REDIRECT_HTTP") == "true" || inv.ParsedFlags().Changed("tls-redirect-http-to-https") {
2157-
cliui.Warn(inv.Stderr, "--tls-redirect-http-to-https is deprecated, please use --redirect-to-access-url instead")
2326+
logger.Warn(ctx, "--tls-redirect-http-to-https is deprecated, please use --redirect-to-access-url instead")
21582327
cfg.RedirectToAccessURL = cfg.TLS.RedirectHTTP
21592328
}
21602329

2161-
tlsConfig, err := configureTLS(
2330+
tlsConfig, err := configureServerTLS(
2331+
ctx,
2332+
logger,
21622333
cfg.TLS.MinVersion.String(),
21632334
cfg.TLS.ClientAuth.String(),
21642335
cfg.TLS.CertFiles,
21652336
cfg.TLS.KeyFiles,
21662337
cfg.TLS.ClientCAFile.String(),
2338+
cfg.TLS.SupportedCiphers.Value(),
2339+
cfg.TLS.AllowInsecureCiphers.Value(),
21672340
)
21682341
if err != nil {
21692342
return nil, xerrors.Errorf("configure tls: %w", err)

0 commit comments

Comments
 (0)