16561: Handle implicit port number in ws:// and wss:// urls.
[arvados.git] / lib / service / tls.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package service
6
7 import (
8         "crypto/tls"
9         "errors"
10         "fmt"
11         "os"
12         "os/signal"
13         "strings"
14         "syscall"
15
16         "git.arvados.org/arvados.git/sdk/go/arvados"
17         "github.com/sirupsen/logrus"
18 )
19
20 func tlsConfigWithCertUpdater(cluster *arvados.Cluster, logger logrus.FieldLogger) (*tls.Config, error) {
21         currentCert := make(chan *tls.Certificate, 1)
22         loaded := false
23
24         key, cert := cluster.TLS.Key, cluster.TLS.Certificate
25         if !strings.HasPrefix(key, "file://") || !strings.HasPrefix(cert, "file://") {
26                 return nil, errors.New("cannot use TLS certificate: TLS.Key and TLS.Certificate must be specified with a 'file://' prefix")
27         }
28         key, cert = key[7:], cert[7:]
29
30         update := func() error {
31                 cert, err := tls.LoadX509KeyPair(cert, key)
32                 if err != nil {
33                         return fmt.Errorf("error loading X509 key pair: %s", err)
34                 }
35                 if loaded {
36                         // Throw away old cert
37                         <-currentCert
38                 }
39                 currentCert <- &cert
40                 loaded = true
41                 return nil
42         }
43         err := update()
44         if err != nil {
45                 return nil, err
46         }
47
48         go func() {
49                 reload := make(chan os.Signal, 1)
50                 signal.Notify(reload, syscall.SIGHUP)
51                 for range reload {
52                         err := update()
53                         if err != nil {
54                                 logger.WithError(err).Warn("error updating TLS certificate")
55                         }
56                 }
57         }()
58
59         // https://blog.gopheracademy.com/advent-2016/exposing-go-on-the-internet/
60         return &tls.Config{
61                 PreferServerCipherSuites: true,
62                 CurvePreferences: []tls.CurveID{
63                         tls.CurveP256,
64                         tls.X25519,
65                 },
66                 MinVersion: tls.VersionTLS12,
67                 CipherSuites: []uint16{
68                         tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
69                         tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
70                         tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
71                         tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
72                         tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
73                         tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
74                 },
75                 GetCertificate: func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
76                         cert := <-currentCert
77                         currentCert <- cert
78                         return cert, nil
79                 },
80         }, nil
81 }