Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for ZeroSSL account registration #1501

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
53 changes: 53 additions & 0 deletions registration/registar.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package registration

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

"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/acme/api"
Expand Down Expand Up @@ -69,6 +74,54 @@ 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 := "https://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("sending request: %w", err)
}
defer resp.Body.Close()

// ZeroSSL might return errors as plain-text messages instead of JSON,
// so we buffer the response to be able to return it as error.
var rawResp bytes.Buffer
r := io.TeeReader(io.LimitReader(resp.Body, 10*1024), &rawResp) // Limit response to 10KB
if err := json.NewDecoder(r).Decode(&data); err != nil {
// It is likely not a JSON but a plain-text error message
_, _ = io.ReadAll(r) // read the rest of the body
return "", "", fmt.Errorf("parsing response: %w. Original response:\n%s", err, rawResp.String())
}

if !data.Success {
return "", "", errors.New("received 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