From 9369237229fa42e8424fc96d9a73bac160edc3c2 Mon Sep 17 00:00:00 2001 From: Alan Yeung Date: Sun, 20 Aug 2023 22:29:15 -0700 Subject: [PATCH 1/2] updated EAB --- .gitignore | 4 +++- src/acme.go | 2 +- src/mod/acme/acme.go | 41 +++++++++++++++++++++++++++++++++------ src/mod/acme/autorenew.go | 2 +- 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index e164ceb..42aede7 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,6 @@ src/rules/* src/README.md docker/ContainerTester.sh docker/ImagePublisher.sh -src/mod/acme/test/stackoverflow.pem \ No newline at end of file +src/mod/acme/test/stackoverflow.pem +src/sys.uuid +src/sys.db.lock diff --git a/src/acme.go b/src/acme.go index 72e5883..1bacb51 100644 --- a/src/acme.go +++ b/src/acme.go @@ -38,7 +38,7 @@ func initACME() *acme.ACMEHandler { port = getRandomPort(30000) } - return acme.NewACME("https://acme-staging-v02.api.letsencrypt.org/directory", strconv.Itoa(port)) + return acme.NewACME("https://acme-staging-v02.api.letsencrypt.org/directory", strconv.Itoa(port), "", "") } // create the special routing rule for ACME diff --git a/src/mod/acme/acme.go b/src/mod/acme/acme.go index ec8bc1c..12f8581 100644 --- a/src/mod/acme/acme.go +++ b/src/mod/acme/acme.go @@ -54,18 +54,22 @@ func (u *ACMEUser) GetPrivateKey() crypto.PrivateKey { type ACMEHandler struct { DefaultAcmeServer string Port string + Kid string + HmacEncoded string } // NewACME creates a new ACMEHandler instance. -func NewACME(acmeServer string, port string) *ACMEHandler { +func NewACME(acmeServer string, port string, kid string, hmacEncoded string) *ACMEHandler { return &ACMEHandler{ DefaultAcmeServer: acmeServer, Port: port, + Kid: kid, + HmacEncoded: hmacEncoded, } } // ObtainCert obtains a certificate for the specified domains. -func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email string, ca string) (bool, error) { +func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email string, ca string, kid string, hmacEncoded string) (bool, error) { log.Println("[ACME] Obtaining certificate...") // generate private key @@ -113,12 +117,37 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email return false, err } + var reg *registration.Resource // New users will need to register - reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) - if err != nil { - log.Println(err) - return false, err + if client.GetExternalAccountRequired() { + log.Println("External Account Required for this ACME Provider.") + // IF KID and HmacEncoded is overidden + if kid != "" && hmacEncoded != "" { + reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ + TermsOfServiceAgreed: true, + Kid: kid, + HmacEncoded: hmacEncoded, + }) + } else { + reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ + TermsOfServiceAgreed: true, + Kid: a.Kid, + HmacEncoded: a.HmacEncoded, + }) + } + if err != nil { + log.Println(err) + return false, err + } + //return false, errors.New("External Account Required for this ACME Provider.") + } else { + reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) + if err != nil { + log.Println(err) + return false, err + } } + adminUser.Registration = reg // obtain the certificate diff --git a/src/mod/acme/autorenew.go b/src/mod/acme/autorenew.go index 211e168..28bee75 100644 --- a/src/mod/acme/autorenew.go +++ b/src/mod/acme/autorenew.go @@ -355,7 +355,7 @@ func (a *AutoRenewer) renewExpiredDomains(certs []*ExpiredCerts) ([]string, erro log.Println("Renewing " + expiredCert.Filepath + " (Might take a few minutes)") fileName := filepath.Base(expiredCert.Filepath) certName := fileName[:len(fileName)-len(filepath.Ext(fileName))] - _, err := a.AcmeHandler.ObtainCert(expiredCert.Domains, certName, a.RenewerConfig.Email, expiredCert.CA) + _, err := a.AcmeHandler.ObtainCert(expiredCert.Domains, certName, a.RenewerConfig.Email, expiredCert.CA, "", "") if err != nil { log.Println("Renew " + fileName + "(" + strings.Join(expiredCert.Domains, ",") + ") failed: " + err.Error()) } else { From b1a14872c3fe7ce41e15606d4a2012422131b57e Mon Sep 17 00:00:00 2001 From: Alan Yeung Date: Sat, 2 Sep 2023 18:56:04 -0700 Subject: [PATCH 2/2] EAB implementation done --- src/acme.go | 2 +- src/mod/acme/acme.go | 72 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 57 insertions(+), 17 deletions(-) diff --git a/src/acme.go b/src/acme.go index 1bacb51..3b13a8c 100644 --- a/src/acme.go +++ b/src/acme.go @@ -38,7 +38,7 @@ func initACME() *acme.ACMEHandler { port = getRandomPort(30000) } - return acme.NewACME("https://acme-staging-v02.api.letsencrypt.org/directory", strconv.Itoa(port), "", "") + return acme.NewACME("https://acme-staging-v02.api.letsencrypt.org/directory", strconv.Itoa(port), "./conf/acme_auth.json") } // create the special routing rule for ACME diff --git a/src/mod/acme/acme.go b/src/mod/acme/acme.go index b25c4ac..049f941 100644 --- a/src/mod/acme/acme.go +++ b/src/mod/acme/acme.go @@ -9,6 +9,7 @@ import ( "crypto/x509" "encoding/json" "encoding/pem" + "errors" "fmt" "io/ioutil" "log" @@ -41,6 +42,11 @@ type ACMEUser struct { key crypto.PrivateKey } +type EABConfig struct { + Kid string `json:"kid"` + HmacKey string `json:"HmacKey"` +} + // GetEmail returns the email of the ACMEUser. func (u *ACMEUser) GetEmail() string { return u.Email @@ -58,24 +64,35 @@ func (u *ACMEUser) GetPrivateKey() crypto.PrivateKey { // ACMEHandler handles ACME-related operations. type ACMEHandler struct { - DefaultAcmeServer string - Port string - Kid string - HmacEncoded string + DefaultAcmeServer string + Port string + AuthConfigLocation string } // NewACME creates a new ACMEHandler instance. -func NewACME(acmeServer string, port string, kid string, hmacEncoded string) *ACMEHandler { +func NewACME(acmeServer string, port string, authConfigLocation string) *ACMEHandler { + + if !utils.FileExists(authConfigLocation) { + //Create one + os.MkdirAll(filepath.Dir(authConfigLocation), 0775) + js := []byte("{ \"EXAMPLE_CONFIG_PUT_YOUR_SERVICE_DIRECTORY_URL_HERE\": { \"kid\": \"PUT_YOUR_KID_HERE\", \"HmacKey\": \"PUT_YOUR_HMAC_HERE\" } }") + err := os.WriteFile(authConfigLocation, js, 0775) + if err != nil { + log.Fatal("[ACME] Failed to write ACME EAB config") + return nil + } + } + return &ACMEHandler{ - DefaultAcmeServer: acmeServer, - Port: port, - Kid: kid, - HmacEncoded: hmacEncoded, + DefaultAcmeServer: acmeServer, + Port: port, + AuthConfigLocation: authConfigLocation, } } // ObtainCert obtains a certificate for the specified domains. -func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email string, caName string, caUrl string, skipTLS bool, kid string, hmacEncoded string) (bool, error) { +// , kid string, hmacEncoded string +func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email string, caName string, caUrl string, skipTLS bool) (bool, error) { log.Println("[ACME] Obtaining certificate...") // generate private key @@ -152,18 +169,18 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email if client.GetExternalAccountRequired() { log.Println("External Account Required for this ACME Provider.") // IF KID and HmacEncoded is overidden + kid, hmacEncoded, err := a.getKidHmac(config.CADirURL) + if err != nil { + log.Println(err) + return false, err + } + log.Println("EAB Credential retrieved.") if kid != "" && hmacEncoded != "" { reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ TermsOfServiceAgreed: true, Kid: kid, HmacEncoded: hmacEncoded, }) - } else { - reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ - TermsOfServiceAgreed: true, - Kid: a.Kid, - HmacEncoded: a.HmacEncoded, - }) } if err != nil { log.Println(err) @@ -400,3 +417,26 @@ func loadCertInfoJSON(filename string) (*CertificateInfoJSON, error) { return certInfo, nil } + +func (a *ACMEHandler) getKidHmac(caName string) (string, string, error) { + var config map[string]EABConfig + + jsonData, err := ioutil.ReadFile(a.AuthConfigLocation) + if err != nil { + return "", "", err + } + + err = json.Unmarshal([]byte(jsonData), &config) + if err != nil { + return "", "", err + } + + // Access the values for dynamic keys + for key, value := range config { + if key == caName { + return value.Kid, value.HmacKey, nil + } + } + + return "", "", errors.New("Unable to find appropiate EAB information") +}