Skip to content

Commit fedfd5c

Browse files
authored
Begin setting up a certificate manager (#7)
This will continue to evolve, but will contain an ACME client and the code to handle issuing certificates. For now, there's a bodge to load a static certificate for my own local testing, but that will be replaced.
1 parent eb2a66e commit fedfd5c

File tree

4 files changed

+81
-11
lines changed

4 files changed

+81
-11
lines changed

.golangci.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ linters:
66
- err113
77
- exhaustruct
88
- funlen
9+
- godot
10+
- godox
911
- lll
1012
- musttag
1113
- perfsprint

certs/certs.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Package certs handles issuing certificates specified in the configuration.
2+
package certs
3+
4+
import (
5+
"context"
6+
"crypto/tls"
7+
"fmt"
8+
"os"
9+
"sync"
10+
11+
"github.com/letsencrypt/test-certs-site/config"
12+
)
13+
14+
// CertManager manages the issued certificates
15+
type CertManager struct {
16+
// mu protects certs
17+
mu sync.Mutex
18+
19+
// certs is a map of domain to a cert struct
20+
certs map[string]cert
21+
}
22+
23+
// cert holds an individual certificate
24+
type cert struct {
25+
it *tls.Certificate
26+
}
27+
28+
// New sets up the certs issuer.
29+
// This will register an ACME account if needed.
30+
func New(_ context.Context, cfg *config.Config) (*CertManager, error) {
31+
c := CertManager{
32+
certs: make(map[string]cert),
33+
}
34+
35+
// This is just a temporary placeholder, using a single static test certificate
36+
certFile := os.Getenv("TEST_CERT")
37+
keyFile := os.Getenv("TEST_KEY")
38+
temporaryStaticCert, err := tls.LoadX509KeyPair(certFile, keyFile)
39+
if err != nil {
40+
return nil, fmt.Errorf("loading temporary certificate: %w", err)
41+
}
42+
43+
for _, site := range cfg.Sites {
44+
c.certs[site.Domains.Valid] = cert{
45+
// TODO: Set up the valid cert
46+
it: &temporaryStaticCert,
47+
}
48+
c.certs[site.Domains.Revoked] = cert{
49+
// TODO: Set up the revoked cert
50+
it: &temporaryStaticCert,
51+
}
52+
c.certs[site.Domains.Expired] = cert{
53+
// TODO: Set up the expired cert
54+
it: &temporaryStaticCert,
55+
}
56+
}
57+
58+
return &c, nil
59+
}
60+
61+
// GetCertificate implements the interface required by tls.Config
62+
func (c *CertManager) GetCertificate(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
63+
sni := info.ServerName
64+
65+
c.mu.Lock()
66+
defer c.mu.Unlock()
67+
68+
cert, ok := c.certs[info.ServerName]
69+
if !ok {
70+
return nil, fmt.Errorf("no certificate for %s", sni)
71+
}
72+
73+
return cert.it, nil
74+
}

go.sum

Whitespace-only changes.

main.go

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
package main
33

44
import (
5-
"crypto/tls"
5+
"context"
66
"flag"
77
"fmt"
88
"log/slog"
99
"os"
1010

11+
"github.com/letsencrypt/test-certs-site/certs"
1112
"github.com/letsencrypt/test-certs-site/config"
1213
"github.com/letsencrypt/test-certs-site/server"
1314
)
@@ -32,19 +33,12 @@ func run(args []string) error {
3233
return fmt.Errorf("loading config: %w", err)
3334
}
3435

35-
// This is just a temporary placeholder, using a single static test certificate
36-
// This will normally be provided by the key storage part of this program
37-
cert := os.Getenv("TEST_CERT")
38-
key := os.Getenv("TEST_KEY")
39-
temporaryStaticCert, err := tls.LoadX509KeyPair(cert, key)
36+
certManager, err := certs.New(context.Background(), cfg)
4037
if err != nil {
41-
return fmt.Errorf("loading temporary certificate: %w", err)
42-
}
43-
todoGetCert := func(_ *tls.ClientHelloInfo) (*tls.Certificate, error) {
44-
return &temporaryStaticCert, nil
38+
return err
4539
}
4640

47-
return server.Run(cfg.ListenAddr, todoGetCert)
41+
return server.Run(cfg.ListenAddr, certManager.GetCertificate)
4842
}
4943

5044
func main() {

0 commit comments

Comments
 (0)