Add renew to acme
This commit is contained in:
96
acme/acme.go
96
acme/acme.go
@@ -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)
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
11
config.go
11
config.go
@@ -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"`
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user