@@ -354,7 +354,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
354
354
logger .Debug (ctx , "tracing closed" , slog .Error (traceCloseErr ))
355
355
}()
356
356
357
- httpServers , err := ConfigureHTTPServers (inv , vals )
357
+ httpServers , err := ConfigureHTTPServers (logger , inv , vals )
358
358
if err != nil {
359
359
return xerrors .Errorf ("configure http(s): %w" , err )
360
360
}
@@ -1021,7 +1021,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
1021
1021
r .Verbosef (inv , "Shutting down provisioner daemon %d..." , id )
1022
1022
err := shutdownWithTimeout (provisionerDaemon .Shutdown , 5 * time .Second )
1023
1023
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 )
1025
1025
return
1026
1026
}
1027
1027
err = provisionerDaemon .Close ()
@@ -1411,7 +1411,12 @@ func generateSelfSignedCertificate() (*tls.Certificate, error) {
1411
1411
return & cert , nil
1412
1412
}
1413
1413
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 ) {
1415
1420
tlsConfig := & tls.Config {
1416
1421
MinVersion : tls .VersionTLS12 ,
1417
1422
NextProtos : []string {"h2" , "http/1.1" },
@@ -1429,6 +1434,15 @@ func configureTLS(tlsMinVersion, tlsClientAuth string, tlsCertFiles, tlsKeyFiles
1429
1434
return nil , xerrors .Errorf ("unrecognized tls version: %q" , tlsMinVersion )
1430
1435
}
1431
1436
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
+
1432
1446
switch tlsClientAuth {
1433
1447
case "none" :
1434
1448
tlsConfig .ClientAuth = tls .NoClientCert
@@ -1487,6 +1501,160 @@ func configureTLS(tlsMinVersion, tlsClientAuth string, tlsCertFiles, tlsKeyFiles
1487
1501
return tlsConfig , nil
1488
1502
}
1489
1503
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
+
1490
1658
func configureOIDCPKI (orig * oauth2.Config , keyFile string , certFile string ) (* oauthpki.Config , error ) {
1491
1659
// Read the files
1492
1660
keyData , err := os .ReadFile (keyFile )
@@ -2078,7 +2246,8 @@ func ConfigureTraceProvider(
2078
2246
return tracerProvider , sqlDriver , closeTracing
2079
2247
}
2080
2248
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 ()
2082
2251
httpServers := & HTTPServers {}
2083
2252
defer func () {
2084
2253
if err != nil {
@@ -2154,16 +2323,20 @@ func ConfigureHTTPServers(inv *clibase.Invocation, cfg *codersdk.DeploymentValue
2154
2323
// DEPRECATED: This redirect used to default to true.
2155
2324
// It made more sense to have the redirect be opt-in.
2156
2325
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" )
2158
2327
cfg .RedirectToAccessURL = cfg .TLS .RedirectHTTP
2159
2328
}
2160
2329
2161
- tlsConfig , err := configureTLS (
2330
+ tlsConfig , err := configureServerTLS (
2331
+ ctx ,
2332
+ logger ,
2162
2333
cfg .TLS .MinVersion .String (),
2163
2334
cfg .TLS .ClientAuth .String (),
2164
2335
cfg .TLS .CertFiles ,
2165
2336
cfg .TLS .KeyFiles ,
2166
2337
cfg .TLS .ClientCAFile .String (),
2338
+ cfg .TLS .SupportedCiphers .Value (),
2339
+ cfg .TLS .AllowInsecureCiphers .Value (),
2167
2340
)
2168
2341
if err != nil {
2169
2342
return nil , xerrors .Errorf ("configure tls: %w" , err )
0 commit comments