arche / internal/archesrv/mtls.go

commit 154431fd
 1package archesrv
 2
 3import (
 4	"crypto/ecdsa"
 5	"crypto/elliptic"
 6	"crypto/rand"
 7	"crypto/tls"
 8	"crypto/x509"
 9	"crypto/x509/pkix"
10	"encoding/pem"
11	"fmt"
12	"math/big"
13	"net/http"
14	"time"
15)
16
17func (s *forgeServer) RunMTLS(addr, certFile, keyFile string) error {
18	tlsCfg := tlsConfigMTLS()
19
20	if certFile != "" && keyFile != "" {
21		cert, err := tls.LoadX509KeyPair(certFile, keyFile)
22		if err != nil {
23			return fmt.Errorf("mTLS: load server cert: %w", err)
24		}
25		tlsCfg.Certificates = []tls.Certificate{cert}
26	} else {
27		cert, err := generateEphemeralCert()
28		if err != nil {
29			return fmt.Errorf("mTLS: generate self-signed cert: %w", err)
30		}
31		tlsCfg.Certificates = []tls.Certificate{cert}
32		s.log.Warn("mTLS using ephemeral self-signed cert", "hint", "set tls_cert/tls_key for a stable cert")
33	}
34
35	ln, err := tls.Listen("tcp", addr, tlsCfg)
36	if err != nil {
37		return fmt.Errorf("mTLS listen %s: %w", addr, err)
38	}
39	s.log.Info("mTLS listening", "addr", addr)
40
41	srv := &http.Server{Handler: s.routes()}
42	return srv.Serve(ln)
43}
44
45func generateEphemeralCert() (tls.Certificate, error) {
46	priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
47	if err != nil {
48		return tls.Certificate{}, err
49	}
50	tmpl := &x509.Certificate{
51		SerialNumber: big.NewInt(1),
52		Subject:      pkix.Name{CommonName: "arche-server"},
53		NotBefore:    time.Now().Add(-time.Minute),
54		NotAfter:     time.Now().Add(10 * 365 * 24 * time.Hour),
55		KeyUsage:     x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
56		ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
57	}
58	certDER, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &priv.PublicKey, priv)
59	if err != nil {
60		return tls.Certificate{}, err
61	}
62	privDER, err := x509.MarshalECPrivateKey(priv)
63	if err != nil {
64		return tls.Certificate{}, err
65	}
66	certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
67	keyPEM := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: privDER})
68	return tls.X509KeyPair(certPEM, keyPEM)
69}