Add renew to acme

This commit is contained in:
Pablu23
2025-07-20 22:08:32 +02:00
parent 77a880cee1
commit 21e28fb60b
4 changed files with 94 additions and 23 deletions

View File

@@ -8,9 +8,9 @@ import (
"crypto/x509" "crypto/x509"
"encoding/pem" "encoding/pem"
"errors" "errors"
"fmt"
"net/http" "net/http"
"os" "os"
"time"
"github.com/go-acme/lego/v4/certcrypto" "github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/certificate" "github.com/go-acme/lego/v4/certificate"
@@ -21,25 +21,33 @@ import (
domainrouter "github.com/pablu23/domain-router" domainrouter "github.com/pablu23/domain-router"
) )
func SetupAcme(config *domainrouter.Config) error { type Acme struct {
acme := config.Server.Ssl.Acme user *User
client *lego.Client
domains []string
certFilePath string
keyFilePath string
renewTicker *time.Ticker
}
func SetupAcme(config *domainrouter.Config) (*Acme, error) {
acme := config.Server.Ssl.Acme
var privateKey *ecdsa.PrivateKey var privateKey *ecdsa.PrivateKey
if _, err := os.Stat(acme.KeyFile); errors.Is(err, os.ErrNotExist) { if _, err := os.Stat(acme.KeyFile); errors.Is(err, os.ErrNotExist) {
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil { if err != nil {
return err return nil, err
} }
err = os.WriteFile(acme.KeyFile, []byte(encode(privateKey)), 0666) err = os.WriteFile(acme.KeyFile, []byte(encodePrivKey(privateKey)), 0666)
if err != nil { if err != nil {
return err return nil, err
} }
} else { } else {
keyBytes, err := os.ReadFile(acme.KeyFile) keyBytes, err := os.ReadFile(acme.KeyFile)
if err != nil { if err != nil {
return err return nil, err
} }
privateKey = decode(string(keyBytes)) privateKey = decodePrivKey(string(keyBytes))
} }
user := User{ user := User{
@@ -56,23 +64,23 @@ func SetupAcme(config *domainrouter.Config) error {
client, err := lego.NewClient(leConfig) client, err := lego.NewClient(leConfig)
if err != nil { if err != nil {
return err return nil, err
} }
// strconv.Itoa(config.Server.Port) // strconv.Itoa(config.Server.Port)
err = client.Challenge.SetHTTP01Provider(http01.NewProviderServer("", "5002")) err = client.Challenge.SetHTTP01Provider(http01.NewProviderServer("", acme.Http01Port))
if err != nil { if err != nil {
return err return nil, err
} }
err = client.Challenge.SetTLSALPN01Provider(tlsalpn01.NewProviderServer("", "5001")) err = client.Challenge.SetTLSALPN01Provider(tlsalpn01.NewProviderServer("", acme.TlsAlpn01Port))
if err != nil { if err != nil {
return err return nil, err
} }
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
if err != nil { if err != nil {
return err return nil, err
} }
user.Registration = reg user.Registration = reg
@@ -87,22 +95,76 @@ func SetupAcme(config *domainrouter.Config) error {
} }
certificates, err := client.Certificate.Obtain(request) certificates, err := client.Certificate.Obtain(request)
if err != nil {
return nil, err
}
err = os.WriteFile(config.Server.Ssl.CertFile, certificates.Certificate, 0666)
if err != nil {
return nil, err
}
err = os.WriteFile(config.Server.Ssl.KeyFile, certificates.PrivateKey, 0666)
if err != nil {
return nil, err
}
d, err := time.ParseDuration(acme.RenewTime)
if err != nil {
return nil, err
}
return &Acme{
user: &user,
client: client,
domains: domains,
certFilePath: config.Server.Ssl.CertFile,
keyFilePath: config.Server.Ssl.KeyFile,
renewTicker: time.NewTicker(d),
}, nil
}
func (a *Acme) RenewAcme() error {
request := certificate.ObtainRequest{
Domains: a.domains,
Bundle: true,
}
certificates, err := a.client.Certificate.Obtain(request)
if err != nil {
return err
}
err = os.WriteFile(a.certFilePath, certificates.Certificate, 0666)
if err != nil {
return err
}
err = os.WriteFile(a.keyFilePath, certificates.PrivateKey, 0666)
if err != nil { if err != nil {
return err return err
} }
fmt.Printf("%#v\n", certificates)
return nil return nil
} }
func encode(privateKey *ecdsa.PrivateKey) string { func (a *Acme) RegisterTicker() {
for {
select {
case <-a.renewTicker.C:
a.RenewAcme()
}
}
}
func encodePrivKey(privateKey *ecdsa.PrivateKey) string {
x509Encoded, _ := x509.MarshalECPrivateKey(privateKey) x509Encoded, _ := x509.MarshalECPrivateKey(privateKey)
pemEncoded := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: x509Encoded}) pemEncoded := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: x509Encoded})
return string(pemEncoded) return string(pemEncoded)
} }
func decode(pemEncoded string) *ecdsa.PrivateKey { func decodePrivKey(pemEncoded string) *ecdsa.PrivateKey {
block, _ := pem.Decode([]byte(pemEncoded)) block, _ := pem.Decode([]byte(pemEncoded))
x509Encoded := block.Bytes x509Encoded := block.Bytes
privateKey, _ := x509.ParseECPrivateKey(x509Encoded) privateKey, _ := x509.ParseECPrivateKey(x509Encoded)

View File

@@ -70,12 +70,15 @@ func main() {
} }
if config.Server.Ssl.Acme.Enabled { if config.Server.Ssl.Acme.Enabled {
err := acme.SetupAcme(config) acmeRenewer, err := acme.SetupAcme(config)
if err != nil { if err != nil {
log.Fatal().Err(err).Msg("unable to setup acme") log.Fatal().Err(err).Msg("unable to setup acme")
} }
}
go func() {
acmeRenewer.RegisterTicker()
}()
}
log.Info().Int("port", config.Server.Port).Str("cert", config.Server.Ssl.CertFile).Str("key", config.Server.Ssl.KeyFile).Msg("Starting server") log.Info().Int("port", config.Server.Port).Str("cert", config.Server.Ssl.CertFile).Str("key", config.Server.Ssl.KeyFile).Msg("Starting server")
err := server.ListenAndServeTLS("", "") err := server.ListenAndServeTLS("", "")
log.Fatal().Err(err).Str("cert", config.Server.Ssl.CertFile).Str("key", config.Server.Ssl.KeyFile).Int("port", config.Server.Port).Msg("Could not start server") log.Fatal().Err(err).Str("cert", config.Server.Ssl.CertFile).Str("key", config.Server.Ssl.KeyFile).Int("port", config.Server.Port).Msg("Could not start server")

View File

@@ -12,10 +12,13 @@ type Config struct {
CertFile string `yaml:"certFile"` CertFile string `yaml:"certFile"`
KeyFile string `yaml:"keyFile"` KeyFile string `yaml:"keyFile"`
Acme struct { Acme struct {
Enabled bool `yaml:"enabled"` Enabled bool `yaml:"enabled"`
Email string `yaml:"email"` Email string `yaml:"email"`
KeyFile string `yaml:"keyFile"` KeyFile string `yaml:"keyFile"`
CADirURL string `yaml:"caDirUrl"` CADirURL string `yaml:"caDirUrl"`
Http01Port string `yaml:"http01Port"`
TlsAlpn01Port string `yaml:"tlsAlpn01Port"`
RenewTime string `yaml:"renewTime"`
} `yaml:"acme"` } `yaml:"acme"`
} `yaml:"ssl"` } `yaml:"ssl"`
} `yaml:"server"` } `yaml:"server"`

View File

@@ -9,6 +9,9 @@ server:
email: me@pablu.de email: me@pablu.de
keyFile: userKey.key keyFile: userKey.key
caDirUrl: https://192.168.2.154:14000/dir caDirUrl: https://192.168.2.154:14000/dir
tlsAlpn01Port: 5001
http01Port: 5002
renewTime: 30s
logging: logging: