// Copyright (C) The Arvados Authors. All rights reserved. // // SPDX-License-Identifier: AGPL-3.0 package service import ( "crypto/tls" "errors" "fmt" "os" "os/signal" "strings" "syscall" "git.arvados.org/arvados.git/sdk/go/arvados" "github.com/sirupsen/logrus" ) func tlsConfigWithCertUpdater(cluster *arvados.Cluster, logger logrus.FieldLogger) (*tls.Config, error) { currentCert := make(chan *tls.Certificate, 1) loaded := false key, cert := cluster.TLS.Key, cluster.TLS.Certificate if !strings.HasPrefix(key, "file://") || !strings.HasPrefix(cert, "file://") { return nil, errors.New("cannot use TLS certificate: TLS.Key and TLS.Certificate must be specified with a 'file://' prefix") } key, cert = key[7:], cert[7:] update := func() error { cert, err := tls.LoadX509KeyPair(cert, key) if err != nil { return fmt.Errorf("error loading X509 key pair: %s", err) } if loaded { // Throw away old cert <-currentCert } currentCert <- &cert loaded = true return nil } err := update() if err != nil { return nil, err } go func() { reload := make(chan os.Signal, 1) signal.Notify(reload, syscall.SIGHUP) for range reload { err := update() if err != nil { logger.WithError(err).Warn("error updating TLS certificate") } } }() // https://blog.gopheracademy.com/advent-2016/exposing-go-on-the-internet/ return &tls.Config{ PreferServerCipherSuites: true, CurvePreferences: []tls.CurveID{ tls.CurveP256, tls.X25519, }, MinVersion: tls.VersionTLS12, CipherSuites: []uint16{ tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, }, GetCertificate: func(*tls.ClientHelloInfo) (*tls.Certificate, error) { cert := <-currentCert currentCert <- cert return cert, nil }, }, nil }