Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ linters:
- err113
- exhaustruct
- funlen
- godot
- godox
- lll
- musttag
- perfsprint
Expand Down
74 changes: 74 additions & 0 deletions certs/certs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Package certs handles issuing certificates specified in the configuration.
package certs

import (
"context"
"crypto/tls"
"fmt"
"os"
"sync"

"github.com/letsencrypt/test-certs-site/config"
)

// CertManager manages the issued certificates
type CertManager struct {
// mu protects certs
mu sync.Mutex

// certs is a map of domain to a cert struct
certs map[string]cert
}

// cert holds an individual certificate
type cert struct {
it *tls.Certificate
}

// New sets up the certs issuer.
// This will register an ACME account if needed.
func New(_ context.Context, cfg *config.Config) (*CertManager, error) {
c := CertManager{
certs: make(map[string]cert),
}

// This is just a temporary placeholder, using a single static test certificate
certFile := os.Getenv("TEST_CERT")
keyFile := os.Getenv("TEST_KEY")
temporaryStaticCert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, fmt.Errorf("loading temporary certificate: %w", err)
}

for _, site := range cfg.Sites {
c.certs[site.Domains.Valid] = cert{
// TODO: Set up the valid cert
it: &temporaryStaticCert,
}
c.certs[site.Domains.Revoked] = cert{
// TODO: Set up the revoked cert
it: &temporaryStaticCert,
}
c.certs[site.Domains.Expired] = cert{
// TODO: Set up the expired cert
it: &temporaryStaticCert,
}
}

return &c, nil
}

// GetCertificate implements the interface required by tls.Config
func (c *CertManager) GetCertificate(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
sni := info.ServerName

c.mu.Lock()
defer c.mu.Unlock()

cert, ok := c.certs[info.ServerName]
if !ok {
return nil, fmt.Errorf("no certificate for %s", sni)
}

return cert.it, nil
}
Empty file added go.sum
Empty file.
16 changes: 5 additions & 11 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
package main

import (
"crypto/tls"
"context"
"flag"
"fmt"
"log/slog"
"os"

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

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

return server.Run(cfg.ListenAddr, todoGetCert)
return server.Run(cfg.ListenAddr, certManager.GetCertificate)
}

func main() {
Expand Down