Allow for ACME EAB (External Account Binding) credentials #98

Closed
opened 2025-12-29 14:24:51 +01:00 by adam · 4 comments
Owner

Originally created by @casperfrx on GitHub (Aug 2, 2025).

Had to dig to find the config parameter ca_dir_url that allows me to use a custom ACME directory which is nice but it's limited to those that require no EAB credentials as far as I can tell.

See for example: https://cloud.google.com/certificate-manager/docs/public-ca-tutorial#register-acme

Would be nice if it could be added. You're already halfway there.

Originally created by @casperfrx on GitHub (Aug 2, 2025). Had to dig to find the config parameter `ca_dir_url` that allows me to use a custom ACME directory which is nice but it's limited to those that require no EAB credentials as far as I can tell. See for example: https://cloud.google.com/certificate-manager/docs/public-ca-tutorial#register-acme Would be nice if it could be added. You're already halfway there.
adam added the enhancement label 2025-12-29 14:24:51 +01:00
adam closed this issue 2025-12-29 14:24:51 +01:00
Author
Owner

@baltazartroisville commented on GitHub (Aug 2, 2025):

godoxy uses lego under the hood, which already supports eab. would be nice if you could try to implement it casperfrx, thank you in advance!

@baltazartroisville commented on GitHub (Aug 2, 2025): godoxy uses lego under the hood, which already supports eab. would be nice if you could try to implement it casperfrx, thank you in advance!
Author
Owner

@casperfrx commented on GitHub (Aug 3, 2025):

Haha cheeky, as much as I can read and hack into code, along with Rust, Go has been at the top of my would-like to spend time mastering but I just can't find enough time for it.

However due to a comment from @yusing about EAB_ environment variable (that I typically couldn't find anymore where I saw them mention it) I took a look at the way the ca_dir_url and the objects were made along with the lego library.

It seems that it's actually not even that difficult:

https://github.com/go-acme/lego/blob/master/cmd/flags.go#L56C20-L56C28

const (
	envEAB         = "LEGO_EAB"
	envEABHMAC     = "LEGO_EAB_HMAC"
	envEABKID      = "LEGO_EAB_KID"
	envEmail       = "LEGO_EMAIL"
	envPath        = "LEGO_PATH"
	envPFX         = "LEGO_PFX"
	envPFXFormat   = "LEGO_PFX_FORMAT"
	envPFXPassword = "LEGO_PFX_PASSWORD"
	envServer      = "LEGO_SERVER"
)
		&cli.BoolFlag{
			Name:    flgEAB,
			EnvVars: []string{envEAB},
			Usage:   "Use External Account Binding for account registration. Requires --kid and --hmac.",
		},
		&cli.StringFlag{
			Name:    flgKID,
			EnvVars: []string{envEABKID},
			Usage:   "Key identifier from External CA. Used for External Account Binding.",
		},
		&cli.StringFlag{
			Name:    flgHMAC,
			EnvVars: []string{envEABHMAC},
			Usage:   "MAC key from External CA. Should be in Base64 URL Encoding without padding format. Used for External Account Binding.",
		},

So I of course went ahead and added these

LEGO_EAB=true
LEGO_EAB_KID=<my_eab_kid>
LEGO_EAB_HMAC=<my_eab_hmac>

to my .env making sure it was loaded by the proxy but no dice. I think it could be as simple as a new env load somewhere. Also I checked where this resource is made and

	if cfg.Provider != ProviderLocal && cfg.Provider != ProviderPseudo {
		if privKey, err = cfg.LoadACMEKey(); err != nil {
			log.Info().Err(err).Msg("failed to load ACME private key, generating a now one")
			privKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
			if err != nil {
				return nil, nil, gperr.New("generate ACME private key").With(err)
			}
			if err = cfg.SaveACMEKey(privKey); err != nil {
				return nil, nil, gperr.New("save ACME private key").With(err)
			}
		}
	}

	user := &User{
		Email: cfg.Email,
		Key:   privKey,
	}

	legoCfg := lego.NewConfig(user)
	legoCfg.Certificate.KeyType = certcrypto.EC256

is close to

import (
	"crypto"

	"github.com/go-acme/lego/v4/registration"
)

type User struct {
	Email        string
	Registration *registration.Resource
	Key          crypto.PrivateKey
}

func (u *User) GetEmail() string {
	return u.Email
}

That references the Registration.User but the parameters are in Registration.Registar https://github.com/go-acme/lego/blob/master/registration/registar.go#L26

// Resource represents all important information about a registration
// of which the client needs to keep track itself.
// WARNING: will be removed in the future (acme.ExtendedAccount), https://github.com/go-acme/lego/issues/855.
type Resource struct {
	Body acme.Account `json:"body,omitempty"`
	URI  string       `json:"uri,omitempty"`
}

type RegisterOptions struct {
	TermsOfServiceAgreed bool
}

type RegisterEABOptions struct {
	TermsOfServiceAgreed bool
	Kid                  string
	HmacEncoded          string
}

type Registrar struct {
	core *api.Core
	user User
}

I'll let @yusing decide what's most elegant/stable to use. I think the environment variables with a small tweak could already do it.

@casperfrx commented on GitHub (Aug 3, 2025): Haha cheeky, as much as I can read and hack into code, along with Rust, Go has been at the top of my would-like to spend time mastering but I just can't find enough time for it. However due to a comment from @yusing about `EAB_` environment variable (that I typically couldn't find anymore where I saw them mention it) I took a look at the way the `ca_dir_url` and the objects were made along with the lego library. It seems that it's actually not even that difficult: https://github.com/go-acme/lego/blob/master/cmd/flags.go#L56C20-L56C28 ```go const ( envEAB = "LEGO_EAB" envEABHMAC = "LEGO_EAB_HMAC" envEABKID = "LEGO_EAB_KID" envEmail = "LEGO_EMAIL" envPath = "LEGO_PATH" envPFX = "LEGO_PFX" envPFXFormat = "LEGO_PFX_FORMAT" envPFXPassword = "LEGO_PFX_PASSWORD" envServer = "LEGO_SERVER" ) ``` ```go &cli.BoolFlag{ Name: flgEAB, EnvVars: []string{envEAB}, Usage: "Use External Account Binding for account registration. Requires --kid and --hmac.", }, &cli.StringFlag{ Name: flgKID, EnvVars: []string{envEABKID}, Usage: "Key identifier from External CA. Used for External Account Binding.", }, &cli.StringFlag{ Name: flgHMAC, EnvVars: []string{envEABHMAC}, Usage: "MAC key from External CA. Should be in Base64 URL Encoding without padding format. Used for External Account Binding.", }, ``` So I of course went ahead and added these ```ini LEGO_EAB=true LEGO_EAB_KID=<my_eab_kid> LEGO_EAB_HMAC=<my_eab_hmac> ``` to my `.env` making sure it was loaded by the proxy but no dice. I think it could be as simple as a new env load somewhere. Also I checked where this resource is made and ```go if cfg.Provider != ProviderLocal && cfg.Provider != ProviderPseudo { if privKey, err = cfg.LoadACMEKey(); err != nil { log.Info().Err(err).Msg("failed to load ACME private key, generating a now one") privKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, nil, gperr.New("generate ACME private key").With(err) } if err = cfg.SaveACMEKey(privKey); err != nil { return nil, nil, gperr.New("save ACME private key").With(err) } } } user := &User{ Email: cfg.Email, Key: privKey, } legoCfg := lego.NewConfig(user) legoCfg.Certificate.KeyType = certcrypto.EC256 ``` is close to ```go import ( "crypto" "github.com/go-acme/lego/v4/registration" ) type User struct { Email string Registration *registration.Resource Key crypto.PrivateKey } func (u *User) GetEmail() string { return u.Email } ``` That references the `Registration.User` but the parameters are in `Registration.Registar` https://github.com/go-acme/lego/blob/master/registration/registar.go#L26 ```go // Resource represents all important information about a registration // of which the client needs to keep track itself. // WARNING: will be removed in the future (acme.ExtendedAccount), https://github.com/go-acme/lego/issues/855. type Resource struct { Body acme.Account `json:"body,omitempty"` URI string `json:"uri,omitempty"` } type RegisterOptions struct { TermsOfServiceAgreed bool } type RegisterEABOptions struct { TermsOfServiceAgreed bool Kid string HmacEncoded string } type Registrar struct { core *api.Core user User } ``` I'll let @yusing decide what's most elegant/stable to use. I think the environment variables with a small tweak could already do it.
Author
Owner

@yusing commented on GitHub (Aug 17, 2025):

Implemented, please check if it works. Docs here: https://docs.godoxy.dev/Certificates-and-domain-matching#eab

@yusing commented on GitHub (Aug 17, 2025): Implemented, please check if it works. Docs here: <https://docs.godoxy.dev/Certificates-and-domain-matching#eab>
Author
Owner

@yusing commented on GitHub (Sep 10, 2025):

Since this issue has been staled for 3 weeks, I'm closing it now. Feel free to reopen it when needed. Thank you all.

@yusing commented on GitHub (Sep 10, 2025): Since this issue has been staled for 3 weeks, I'm closing it now. Feel free to reopen it when needed. Thank you all.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/godoxy-yusing#98