Skip to content

Commit 45837b9

Browse files
committed
ACME based certificates, BYOC.
1 parent 347ca40 commit 45837b9

File tree

2 files changed

+55
-3
lines changed

2 files changed

+55
-3
lines changed

vhttp/server.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"get.pme.sh/pmesh/security"
2121
"get.pme.sh/pmesh/urlsigner"
2222
"get.pme.sh/pmesh/xlog"
23+
"golang.org/x/crypto/acme"
2324
)
2425

2526
type ipinfoWrapper struct{ netx.IPInfoProvider }
@@ -189,7 +190,7 @@ func NewServer(ctx context.Context) (s *Server) {
189190
GetCertificate: s.GetCertificate,
190191
PreferServerCipherSuites: true,
191192
CurvePreferences: []tls.CurveID{tls.CurveP256, tls.X25519},
192-
NextProtos: []string{"h2", "http/1.1"},
193+
NextProtos: []string{"h2", "http/1.1", acme.ALPNProto},
193194
}),
194195
}
195196
s.Server.RegisterOnShutdown(func() { logw.Flush() })

vhttp/vhost.go

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,67 @@ package vhttp
22

33
import (
44
"crypto/tls"
5+
"errors"
56
"fmt"
67
"net/http"
78
"strings"
9+
"sync"
810
"sync/atomic"
911

1012
"get.pme.sh/pmesh/config"
1113
"get.pme.sh/pmesh/hosts"
1214
"get.pme.sh/pmesh/netx"
1315
"get.pme.sh/pmesh/security"
1416
"get.pme.sh/pmesh/xlog"
17+
"golang.org/x/crypto/acme/autocert"
1518

1619
"github.com/samber/lo"
1720
)
1821

22+
type CertProvider struct {
23+
CertPath string `yaml:"cert,omitempty"` // Path to the TLS certificate.
24+
KeyPath string `yaml:"key,omitempty"` // Path to the TLS key.
25+
certMu sync.Mutex
26+
cache atomic.Pointer[tls.Certificate]
27+
}
28+
29+
var getLetsEncrypt = sync.OnceValue(func() *autocert.Manager {
30+
return &autocert.Manager{
31+
Prompt: autocert.AcceptTOS,
32+
Cache: autocert.DirCache(config.CertDir.Path()),
33+
}
34+
})
35+
36+
func (cp *CertProvider) GetCertificate(chi *tls.ClientHelloInfo) (cert *tls.Certificate, err error) {
37+
if cp.CertPath == "letsencrypt" {
38+
return getLetsEncrypt().GetCertificate(chi)
39+
} else if cp.CertPath != "" {
40+
if cert = cp.cache.Load(); cert != nil {
41+
return
42+
}
43+
44+
cp.certMu.Lock()
45+
defer cp.certMu.Unlock()
46+
if cert = cp.cache.Load(); cert != nil {
47+
return
48+
}
49+
50+
scert, err := security.LoadCertificate(cp.CertPath, cp.KeyPath)
51+
if err == nil {
52+
cp.cache.Store(scert.TLS)
53+
cert = scert.TLS
54+
}
55+
return cert, err
56+
}
57+
return nil, errors.New("no certificate")
58+
}
59+
1960
type VirtualHostOptions struct {
20-
Hostnames []string `yaml:"-"`
21-
NoUpgrade bool `yaml:"no_upgrade,omitempty"` // Do not upgrade HTTP to HTTPS.
61+
Hostnames []string `yaml:"-"`
62+
NoUpgrade bool `yaml:"no_upgrade,omitempty"` // Do not upgrade HTTP to HTTPS.
63+
Certs map[string]*CertProvider `yaml:"certs,omitempty"` // TLS certificates.
2264
}
65+
2366
type VirtualHost struct {
2467
VirtualHostOptions
2568
Mux
@@ -140,6 +183,14 @@ func (mux *TopLevelMux) GetCertificate(chi *tls.ClientHelloInfo) (cert *tls.Cert
140183
if sni := chi.ServerName; sni != "" {
141184
_, _, vhg = hosts.MatchMap(unique, sni)
142185
if vhg != nil {
186+
for _, vh := range vhg.hosts {
187+
if vh.Certs != nil {
188+
_, _, prov := hosts.MatchMap(vh.Certs, sni)
189+
if prov != nil {
190+
return prov.GetCertificate(chi)
191+
}
192+
}
193+
}
143194
cert = security.ObtainCertificate(config.Get().Secret, sni).TLS
144195
}
145196
}

0 commit comments

Comments
 (0)