Skip to content

Commit

Permalink
Add support for ZeroSSL account registration
Browse files Browse the repository at this point in the history
This commit extends lego library and cli tool to support issuing
certificates from ZeroSSL without having to manually create an account.

Without this commit ZeroSSL can be used but users need to manually
create ZeroSSL account and start `lego` in EAB (External Account
Binding) mode.

From the `lego` cli tool perspective this commit:

Detects if `lego` ir running with ZeroSSL ACME directory `--server
https://acme.zerossl.com/v2/DV90` and uses ZeroSSL API to issue keys for
EAB. There is no need to provide `--eab`, `--kid`, `--hmac` values
anymore.

From the library perspective this commit:

Creates new method `RegisterWithZeroSSL()` in the `registration`
package which takes care of creating ZeroSSL account with a given email.
Internally it re-uses `RegisterWithExternalAccountBinding()` method
after KID and HMAC are retrieved from ZeroSSL registration endpoint.
  • Loading branch information
fln committed Nov 7, 2023
1 parent a6ddcac commit cd63b32
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 1 deletion.
4 changes: 4 additions & 0 deletions cmd/cmd_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ func register(ctx *cli.Context, client *lego.Client) (*registration.Resource, er
log.Fatal("You did not accept the TOS. Unable to proceed.")
}

if ctx.String("server") == lego.ZeroSSLDirectory {
return client.Registration.RegisterWithZeroSSL(registration.RegisterOptions{TermsOfServiceAgreed: true})
}

if ctx.Bool("eab") {
kid := ctx.String("kid")
hmacEncoded := ctx.String("hmac")
Expand Down
2 changes: 1 addition & 1 deletion cmd/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func newClient(ctx *cli.Context, acc registration.User, keyType certcrypto.KeyTy
log.Fatalf("Could not create client: %v", err)
}

if client.GetExternalAccountRequired() && !ctx.IsSet("eab") {
if client.GetExternalAccountRequired() && !ctx.IsSet("eab") && config.CADirURL != lego.ZeroSSLDirectory {
log.Fatal("Server requires External Account Binding. Use --eab with --kid and --hmac.")
}

Expand Down
3 changes: 3 additions & 0 deletions lego/client_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ const (

// LEDirectoryStaging URL to the Let's Encrypt staging.
LEDirectoryStaging = "https://acme-staging-v02.api.letsencrypt.org/directory"

// ZeroSSLDirectory URL to the ZeroSSL production.
ZeroSSLDirectory = "https://acme.zerossl.com/v2/DV90"
)

type Config struct {
Expand Down
44 changes: 44 additions & 0 deletions registration/registar.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package registration

import (
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"

"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/acme/api"
Expand Down Expand Up @@ -67,6 +70,47 @@ func (r *Registrar) Register(options RegisterOptions) (*Resource, error) {
return &Resource{URI: account.Location, Body: account.Account}, nil
}

func createZeroSSLAccount(email string) (string, string, error) {
newAccountURL := "http://api.zerossl.com/acme/eab-credentials-email"
data := struct {
Success bool `json:"success"`
KID string `json:"eab_kid"`
HMAC string `json:"eab_hmac_key"`
}{}

resp, err := http.PostForm(newAccountURL, url.Values{"email": {email}})
if err != nil {
return "", "", fmt.Errorf("acme: error creating ZeroSSL account EAB details request: %w", err)
}
defer resp.Body.Close()
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
return "", "", fmt.Errorf("acme: error reading ZeroSSL account EAB details response: %w", err)
}

if !data.Success {
return "", "", fmt.Errorf("acme: error in ZeroSSL account EAB details response, success=false")
}
return data.KID, data.HMAC, nil
}

// RegisterWithZeroSSL Register the current account to the ZeroSSL server.
func (r *Registrar) RegisterWithZeroSSL(options RegisterOptions) (*Resource, error) {
if r.user.GetEmail() == "" {
return nil, errors.New("acme: cannot register ZeroSSL account without email address")
}

kid, hmac, err := createZeroSSLAccount(r.user.GetEmail())
if err != nil {
return nil, fmt.Errorf("acme: error registering new ZeroSSL account: %w", err)
}

return r.RegisterWithExternalAccountBinding(RegisterEABOptions{
TermsOfServiceAgreed: options.TermsOfServiceAgreed,
Kid: kid,
HmacEncoded: hmac,
})
}

// RegisterWithExternalAccountBinding Register the current account to the ACME server.
func (r *Registrar) RegisterWithExternalAccountBinding(options RegisterEABOptions) (*Resource, error) {
accMsg := acme.Account{
Expand Down

0 comments on commit cd63b32

Please sign in to comment.