Update server dependencies
This commit is contained in:
parent
c704ebb042
commit
1794e2680a
369 changed files with 23554 additions and 6306 deletions
0
vendor/github.com/go-acme/lego/LICENSE → vendor/github.com/go-acme/lego/v3/LICENSE
generated
vendored
0
vendor/github.com/go-acme/lego/LICENSE → vendor/github.com/go-acme/lego/v3/LICENSE
generated
vendored
|
@ -5,7 +5,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-acme/lego/acme"
|
||||
"github.com/go-acme/lego/v3/acme"
|
||||
)
|
||||
|
||||
type AccountService service
|
||||
|
@ -31,12 +31,12 @@ func (a *AccountService) New(req acme.Account) (acme.ExtendedAccount, error) {
|
|||
func (a *AccountService) NewEAB(accMsg acme.Account, kid string, hmacEncoded string) (acme.ExtendedAccount, error) {
|
||||
hmac, err := base64.RawURLEncoding.DecodeString(hmacEncoded)
|
||||
if err != nil {
|
||||
return acme.ExtendedAccount{}, fmt.Errorf("acme: could not decode hmac key: %v", err)
|
||||
return acme.ExtendedAccount{}, fmt.Errorf("acme: could not decode hmac key: %w", err)
|
||||
}
|
||||
|
||||
eabJWS, err := a.core.signEABContent(a.core.GetDirectory().NewAccountURL, kid, hmac)
|
||||
if err != nil {
|
||||
return acme.ExtendedAccount{}, fmt.Errorf("acme: error signing eab content: %v", err)
|
||||
return acme.ExtendedAccount{}, fmt.Errorf("acme: error signing eab content: %w", err)
|
||||
}
|
||||
accMsg.ExternalAccountBinding = eabJWS
|
||||
|
||||
|
@ -57,6 +57,20 @@ func (a *AccountService) Get(accountURL string) (acme.Account, error) {
|
|||
return account, nil
|
||||
}
|
||||
|
||||
// Update Updates an account.
|
||||
func (a *AccountService) Update(accountURL string, req acme.Account) (acme.ExtendedAccount, error) {
|
||||
if len(accountURL) == 0 {
|
||||
return acme.ExtendedAccount{}, errors.New("account[update]: empty URL")
|
||||
}
|
||||
|
||||
var account acme.ExtendedAccount
|
||||
_, err := a.core.post(accountURL, req, &account)
|
||||
if err != nil {
|
||||
return acme.ExtendedAccount{}, err
|
||||
}
|
||||
return account, nil
|
||||
}
|
||||
|
||||
// Deactivate Deactivates an account.
|
||||
func (a *AccountService) Deactivate(accountURL string) error {
|
||||
if len(accountURL) == 0 {
|
|
@ -10,12 +10,12 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff"
|
||||
"github.com/go-acme/lego/acme"
|
||||
"github.com/go-acme/lego/acme/api/internal/nonces"
|
||||
"github.com/go-acme/lego/acme/api/internal/secure"
|
||||
"github.com/go-acme/lego/acme/api/internal/sender"
|
||||
"github.com/go-acme/lego/log"
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
"github.com/go-acme/lego/v3/acme"
|
||||
"github.com/go-acme/lego/v3/acme/api/internal/nonces"
|
||||
"github.com/go-acme/lego/v3/acme/api/internal/secure"
|
||||
"github.com/go-acme/lego/v3/acme/api/internal/sender"
|
||||
"github.com/go-acme/lego/v3/log"
|
||||
)
|
||||
|
||||
// Core ACME/LE core API.
|
||||
|
@ -71,7 +71,7 @@ func (a *Core) post(uri string, reqBody, response interface{}) (*http.Response,
|
|||
}
|
||||
|
||||
// postAsGet performs an HTTP POST ("POST-as-GET") request.
|
||||
// https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-6.3
|
||||
// https://tools.ietf.org/html/rfc8555#section-6.3
|
||||
func (a *Core) postAsGet(uri string, response interface{}) (*http.Response, error) {
|
||||
return a.retrievablePost(uri, []byte{}, response)
|
||||
}
|
||||
|
@ -93,7 +93,6 @@ func (a *Core) retrievablePost(uri string, content []byte, response interface{})
|
|||
switch err.(type) {
|
||||
// Retry if the nonce was invalidated
|
||||
case *acme.NonceError:
|
||||
log.Infof("nonce error retry: %s", err)
|
||||
return err
|
||||
default:
|
||||
cancel()
|
||||
|
@ -104,7 +103,11 @@ func (a *Core) retrievablePost(uri string, content []byte, response interface{})
|
|||
return nil
|
||||
}
|
||||
|
||||
err := backoff.Retry(operation, backoff.WithContext(bo, ctx))
|
||||
notify := func(err error, duration time.Duration) {
|
||||
log.Infof("retry due to: %v", err)
|
||||
}
|
||||
|
||||
err := backoff.RetryNotify(operation, backoff.WithContext(bo, ctx), notify)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -115,7 +118,7 @@ func (a *Core) retrievablePost(uri string, content []byte, response interface{})
|
|||
func (a *Core) signedPost(uri string, content []byte, response interface{}) (*http.Response, error) {
|
||||
signedContent, err := a.jws.SignContent(uri, content)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to post JWS message -> failed to sign content -> %v", err)
|
||||
return nil, fmt.Errorf("failed to post JWS message: failed to sign content: %w", err)
|
||||
}
|
||||
|
||||
signedBody := bytes.NewBuffer([]byte(signedContent.FullSerialize()))
|
||||
|
@ -152,7 +155,7 @@ func (a *Core) GetDirectory() acme.Directory {
|
|||
func getDirectory(do *sender.Doer, caDirURL string) (acme.Directory, error) {
|
||||
var dir acme.Directory
|
||||
if _, err := do.Get(caDirURL, &dir); err != nil {
|
||||
return dir, fmt.Errorf("get directory at '%s': %v", caDirURL, err)
|
||||
return dir, fmt.Errorf("get directory at '%s': %w", caDirURL, err)
|
||||
}
|
||||
|
||||
if dir.NewAccountURL == "" {
|
|
@ -3,7 +3,7 @@ package api
|
|||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/go-acme/lego/acme"
|
||||
"github.com/go-acme/lego/v3/acme"
|
||||
)
|
||||
|
||||
type AuthorizationService service
|
|
@ -7,9 +7,9 @@ import (
|
|||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-acme/lego/acme"
|
||||
"github.com/go-acme/lego/certcrypto"
|
||||
"github.com/go-acme/lego/log"
|
||||
"github.com/go-acme/lego/v3/acme"
|
||||
"github.com/go-acme/lego/v3/certcrypto"
|
||||
"github.com/go-acme/lego/v3/log"
|
||||
)
|
||||
|
||||
// maxBodySize is the maximum size of body that we will read.
|
||||
|
@ -71,7 +71,7 @@ func (c *CertificateService) get(certURL string) ([]byte, string, error) {
|
|||
|
||||
// The issuer certificate link may be supplied via an "up" link
|
||||
// in the response headers of a new certificate.
|
||||
// See https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.4.2
|
||||
// See https://tools.ietf.org/html/rfc8555#section-7.4.2
|
||||
up := getLink(resp.Header, "up")
|
||||
|
||||
return cert, up, err
|
|
@ -3,7 +3,7 @@ package api
|
|||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/go-acme/lego/acme"
|
||||
"github.com/go-acme/lego/v3/acme"
|
||||
)
|
||||
|
||||
type ChallengeService service
|
|
@ -6,7 +6,7 @@ import (
|
|||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/go-acme/lego/acme/api/internal/sender"
|
||||
"github.com/go-acme/lego/v3/acme/api/internal/sender"
|
||||
)
|
||||
|
||||
// Manager Manages nonces.
|
||||
|
@ -57,7 +57,7 @@ func (n *Manager) Nonce() (string, error) {
|
|||
func (n *Manager) getNonce() (string, error) {
|
||||
resp, err := n.do.Head(n.nonceURL)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get nonce from HTTP HEAD -> %v", err)
|
||||
return "", fmt.Errorf("failed to get nonce from HTTP HEAD: %w", err)
|
||||
}
|
||||
|
||||
return GetFromResponse(resp)
|
||||
|
@ -71,7 +71,7 @@ func GetFromResponse(resp *http.Response) (string, error) {
|
|||
|
||||
nonce := resp.Header.Get("Replay-Nonce")
|
||||
if nonce == "" {
|
||||
return "", fmt.Errorf("server did not respond with a proper nonce header")
|
||||
return "", errors.New("server did not respond with a proper nonce header")
|
||||
}
|
||||
|
||||
return nonce, nil
|
|
@ -8,7 +8,7 @@ import (
|
|||
"encoding/base64"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-acme/lego/acme/api/internal/nonces"
|
||||
"github.com/go-acme/lego/v3/acme/api/internal/nonces"
|
||||
jose "gopkg.in/square/go-jose.v2"
|
||||
)
|
||||
|
||||
|
@ -65,12 +65,12 @@ func (j *JWS) SignContent(url string, content []byte) (*jose.JSONWebSignature, e
|
|||
|
||||
signer, err := jose.NewSigner(signKey, &options)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create jose signer -> %v", err)
|
||||
return nil, fmt.Errorf("failed to create jose signer: %w", err)
|
||||
}
|
||||
|
||||
signed, err := signer.Sign(content)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to sign content -> %v", err)
|
||||
return nil, fmt.Errorf("failed to sign content: %w", err)
|
||||
}
|
||||
return signed, nil
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ func (j *JWS) SignEABContent(url, kid string, hmac []byte) (*jose.JSONWebSignatu
|
|||
jwk := jose.JSONWebKey{Key: j.privKey}
|
||||
jwkJSON, err := jwk.Public().MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("acme: error encoding eab jwk key: %v", err)
|
||||
return nil, fmt.Errorf("acme: error encoding eab jwk key: %w", err)
|
||||
}
|
||||
|
||||
signer, err := jose.NewSigner(
|
||||
|
@ -94,12 +94,12 @@ func (j *JWS) SignEABContent(url, kid string, hmac []byte) (*jose.JSONWebSignatu
|
|||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create External Account Binding jose signer -> %v", err)
|
||||
return nil, fmt.Errorf("failed to create External Account Binding jose signer: %w", err)
|
||||
}
|
||||
|
||||
signed, err := signer.Sign(jwkJSON)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to External Account Binding sign content -> %v", err)
|
||||
return nil, fmt.Errorf("failed to External Account Binding sign content: %w", err)
|
||||
}
|
||||
|
||||
return signed, nil
|
|
@ -9,7 +9,7 @@ import (
|
|||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/go-acme/lego/acme"
|
||||
"github.com/go-acme/lego/v3/acme"
|
||||
)
|
||||
|
||||
type RequestOption func(*http.Request) error
|
||||
|
@ -70,7 +70,7 @@ func (d *Doer) Post(url string, body io.Reader, bodyType string, response interf
|
|||
func (d *Doer) newRequest(method, uri string, body io.Reader, opts ...RequestOption) (*http.Request, error) {
|
||||
req, err := http.NewRequest(method, uri, body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %v", err)
|
||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("User-Agent", d.formatUserAgent())
|
||||
|
@ -78,7 +78,7 @@ func (d *Doer) newRequest(method, uri string, body io.Reader, opts ...RequestOpt
|
|||
for _, opt := range opts {
|
||||
err = opt(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %v", err)
|
||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ func (d *Doer) do(req *http.Request, response interface{}) (*http.Response, erro
|
|||
|
||||
err = json.Unmarshal(raw, response)
|
||||
if err != nil {
|
||||
return resp, fmt.Errorf("failed to unmarshal %q to type %T: %v", raw, response, err)
|
||||
return resp, fmt.Errorf("failed to unmarshal %q to type %T: %w", raw, response, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,16 +120,15 @@ func (d *Doer) formatUserAgent() string {
|
|||
|
||||
func checkError(req *http.Request, resp *http.Response) error {
|
||||
if resp.StatusCode >= http.StatusBadRequest {
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%d :: %s :: %s :: %v", resp.StatusCode, req.Method, req.URL, err)
|
||||
return fmt.Errorf("%d :: %s :: %s :: %w", resp.StatusCode, req.Method, req.URL, err)
|
||||
}
|
||||
|
||||
var errorDetails *acme.ProblemDetails
|
||||
err = json.Unmarshal(body, &errorDetails)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%d ::%s :: %s :: %v :: %s", resp.StatusCode, req.Method, req.URL, err, string(body))
|
||||
return fmt.Errorf("%d ::%s :: %s :: %w :: %s", resp.StatusCode, req.Method, req.URL, err, string(body))
|
||||
}
|
||||
|
||||
errorDetails.Method = req.Method
|
|
@ -5,7 +5,7 @@ package sender
|
|||
|
||||
const (
|
||||
// ourUserAgent is the User-Agent of this underlying library package.
|
||||
ourUserAgent = "xenolf-acme/2.6.0"
|
||||
ourUserAgent = "xenolf-acme/3.6.0"
|
||||
|
||||
// ourUserAgentComment is part of the UA comment linked to the version status of this underlying library package.
|
||||
// values: detach|release
|
|
@ -4,7 +4,7 @@ import (
|
|||
"encoding/base64"
|
||||
"errors"
|
||||
|
||||
"github.com/go-acme/lego/acme"
|
||||
"github.com/go-acme/lego/v3/acme"
|
||||
)
|
||||
|
||||
type OrderService service
|
|
@ -1,5 +1,5 @@
|
|||
// Package acme contains all objects related the ACME endpoints.
|
||||
// https://tools.ietf.org/html/draft-ietf-acme-acme-16
|
||||
// https://tools.ietf.org/html/rfc8555
|
||||
package acme
|
||||
|
||||
import (
|
||||
|
@ -8,7 +8,7 @@ import (
|
|||
)
|
||||
|
||||
// Challenge statuses
|
||||
// https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.6
|
||||
// https://tools.ietf.org/html/rfc8555#section-7.1.6
|
||||
const (
|
||||
StatusPending = "pending"
|
||||
StatusInvalid = "invalid"
|
||||
|
@ -20,7 +20,7 @@ const (
|
|||
)
|
||||
|
||||
// Directory the ACME directory object.
|
||||
// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.1
|
||||
// - https://tools.ietf.org/html/rfc8555#section-7.1.1
|
||||
type Directory struct {
|
||||
NewNonceURL string `json:"newNonce"`
|
||||
NewAccountURL string `json:"newAccount"`
|
||||
|
@ -32,7 +32,7 @@ type Directory struct {
|
|||
}
|
||||
|
||||
// Meta the ACME meta object (related to Directory).
|
||||
// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.1
|
||||
// - https://tools.ietf.org/html/rfc8555#section-7.1.1
|
||||
type Meta struct {
|
||||
// termsOfService (optional, string):
|
||||
// A URL identifying the current terms of service.
|
||||
|
@ -65,8 +65,8 @@ type ExtendedAccount struct {
|
|||
}
|
||||
|
||||
// Account the ACME account Object.
|
||||
// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.2
|
||||
// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.3
|
||||
// - https://tools.ietf.org/html/rfc8555#section-7.1.2
|
||||
// - https://tools.ietf.org/html/rfc8555#section-7.3
|
||||
type Account struct {
|
||||
// status (required, string):
|
||||
// The status of this account.
|
||||
|
@ -111,7 +111,7 @@ type ExtendedOrder struct {
|
|||
}
|
||||
|
||||
// Order the ACME order Object.
|
||||
// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.3
|
||||
// - https://tools.ietf.org/html/rfc8555#section-7.1.3
|
||||
type Order struct {
|
||||
// status (required, string):
|
||||
// The status of this order.
|
||||
|
@ -164,7 +164,7 @@ type Order struct {
|
|||
}
|
||||
|
||||
// Authorization the ACME authorization object.
|
||||
// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.4
|
||||
// - https://tools.ietf.org/html/rfc8555#section-7.1.4
|
||||
type Authorization struct {
|
||||
// status (required, string):
|
||||
// The status of this authorization.
|
||||
|
@ -206,8 +206,8 @@ type ExtendedChallenge struct {
|
|||
}
|
||||
|
||||
// Challenge the ACME challenge object.
|
||||
// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.5
|
||||
// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-8
|
||||
// - https://tools.ietf.org/html/rfc8555#section-7.1.5
|
||||
// - https://tools.ietf.org/html/rfc8555#section-8
|
||||
type Challenge struct {
|
||||
// type (required, string):
|
||||
// The type of challenge encoded in the object.
|
||||
|
@ -240,23 +240,23 @@ type Challenge struct {
|
|||
// It MUST NOT contain any characters outside the base64url alphabet,
|
||||
// and MUST NOT include base64 padding characters ("=").
|
||||
// See [RFC4086] for additional information on randomness requirements.
|
||||
// https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-8.3
|
||||
// https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-8.4
|
||||
// https://tools.ietf.org/html/rfc8555#section-8.3
|
||||
// https://tools.ietf.org/html/rfc8555#section-8.4
|
||||
Token string `json:"token"`
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-8.1
|
||||
// https://tools.ietf.org/html/rfc8555#section-8.1
|
||||
KeyAuthorization string `json:"keyAuthorization"`
|
||||
}
|
||||
|
||||
// Identifier the ACME identifier object.
|
||||
// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-9.7.7
|
||||
// - https://tools.ietf.org/html/rfc8555#section-9.7.7
|
||||
type Identifier struct {
|
||||
Type string `json:"type"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// CSRMessage Certificate Signing Request
|
||||
// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.4
|
||||
// - https://tools.ietf.org/html/rfc8555#section-7.4
|
||||
type CSRMessage struct {
|
||||
// csr (required, string):
|
||||
// A CSR encoding the parameters for the certificate being requested [RFC2986].
|
||||
|
@ -266,7 +266,7 @@ type CSRMessage struct {
|
|||
}
|
||||
|
||||
// RevokeCertMessage a certificate revocation message
|
||||
// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.6
|
||||
// - https://tools.ietf.org/html/rfc8555#section-7.6
|
||||
// - https://tools.ietf.org/html/rfc5280#section-5.3.1
|
||||
type RevokeCertMessage struct {
|
||||
// certificate (required, string):
|
|
@ -12,7 +12,7 @@ const (
|
|||
|
||||
// ProblemDetails the problem details object
|
||||
// - https://tools.ietf.org/html/rfc7807#section-3.1
|
||||
// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.3.3
|
||||
// - https://tools.ietf.org/html/rfc8555#section-7.3.3
|
||||
type ProblemDetails struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Detail string `json:"detail,omitempty"`
|
||||
|
@ -26,7 +26,7 @@ type ProblemDetails struct {
|
|||
}
|
||||
|
||||
// SubProblem a "subproblems"
|
||||
// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-6.7.1
|
||||
// - https://tools.ietf.org/html/rfc8555#section-6.7.1
|
||||
type SubProblem struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Detail string `json:"detail,omitempty"`
|
|
@ -3,6 +3,7 @@ package certcrypto
|
|||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
|
@ -13,6 +14,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ocsp"
|
||||
|
@ -77,17 +79,35 @@ func ParsePEMBundle(bundle []byte) ([]*x509.Certificate, error) {
|
|||
return certificates, nil
|
||||
}
|
||||
|
||||
// ParsePEMPrivateKey parses a private key from key, which is a PEM block.
|
||||
// Borrowed from Go standard library, to handle various private key and PEM block types.
|
||||
// https://github.com/golang/go/blob/693748e9fa385f1e2c3b91ca9acbb6c0ad2d133d/src/crypto/tls/tls.go#L291-L308
|
||||
// https://github.com/golang/go/blob/693748e9fa385f1e2c3b91ca9acbb6c0ad2d133d/src/crypto/tls/tls.go#L238)
|
||||
func ParsePEMPrivateKey(key []byte) (crypto.PrivateKey, error) {
|
||||
keyBlock, _ := pem.Decode(key)
|
||||
keyBlockDER, _ := pem.Decode(key)
|
||||
|
||||
switch keyBlock.Type {
|
||||
case "RSA PRIVATE KEY":
|
||||
return x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
|
||||
case "EC PRIVATE KEY":
|
||||
return x509.ParseECPrivateKey(keyBlock.Bytes)
|
||||
default:
|
||||
return nil, errors.New("unknown PEM header value")
|
||||
if keyBlockDER.Type != "PRIVATE KEY" && !strings.HasSuffix(keyBlockDER.Type, " PRIVATE KEY") {
|
||||
return nil, fmt.Errorf("unknown PEM header %q", keyBlockDER.Type)
|
||||
}
|
||||
|
||||
if key, err := x509.ParsePKCS1PrivateKey(keyBlockDER.Bytes); err == nil {
|
||||
return key, nil
|
||||
}
|
||||
|
||||
if key, err := x509.ParsePKCS8PrivateKey(keyBlockDER.Bytes); err == nil {
|
||||
switch key := key.(type) {
|
||||
case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey:
|
||||
return key, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("found unknown private key type in PKCS#8 wrapping: %T", key)
|
||||
}
|
||||
}
|
||||
|
||||
if key, err := x509.ParseECPrivateKey(keyBlockDER.Bytes); err == nil {
|
||||
return key, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("failed to parse private key")
|
||||
}
|
||||
|
||||
func GeneratePrivateKey(keyType KeyType) (crypto.PrivateKey, error) {
|
||||
|
@ -147,7 +167,7 @@ func PEMBlock(data interface{}) *pem.Block {
|
|||
func pemDecode(data []byte) (*pem.Block, error) {
|
||||
pemBlock, _ := pem.Decode(data)
|
||||
if pemBlock == nil {
|
||||
return nil, fmt.Errorf("PEM decode did not yield a valid block. Is the certificate in the right format?")
|
||||
return nil, errors.New("PEM decode did not yield a valid block. Is the certificate in the right format?")
|
||||
}
|
||||
|
||||
return pemBlock, nil
|
||||
|
@ -160,7 +180,7 @@ func PemDecodeTox509CSR(pem []byte) (*x509.CertificateRequest, error) {
|
|||
}
|
||||
|
||||
if pemBlock.Type != "CERTIFICATE REQUEST" {
|
||||
return nil, fmt.Errorf("PEM block is not a certificate request")
|
||||
return nil, errors.New("PEM block is not a certificate request")
|
||||
}
|
||||
|
||||
return x509.ParseCertificateRequest(pemBlock.Bytes)
|
||||
|
@ -179,7 +199,10 @@ func ParsePEMCertificate(cert []byte) (*x509.Certificate, error) {
|
|||
}
|
||||
|
||||
func ExtractDomains(cert *x509.Certificate) []string {
|
||||
domains := []string{cert.Subject.CommonName}
|
||||
var domains []string
|
||||
if cert.Subject.CommonName != "" {
|
||||
domains = append(domains, cert.Subject.CommonName)
|
||||
}
|
||||
|
||||
// Check for SAN certificate
|
||||
for _, sanDomain := range cert.DNSNames {
|
||||
|
@ -193,7 +216,10 @@ func ExtractDomains(cert *x509.Certificate) []string {
|
|||
}
|
||||
|
||||
func ExtractDomainsCSR(csr *x509.CertificateRequest) []string {
|
||||
domains := []string{csr.Subject.CommonName}
|
||||
var domains []string
|
||||
if csr.Subject.CommonName != "" {
|
||||
domains = append(domains, csr.Subject.CommonName)
|
||||
}
|
||||
|
||||
// loop over the SubjectAltName DNS names
|
||||
for _, sanName := range csr.DNSNames {
|
|
@ -3,8 +3,8 @@ package certificate
|
|||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/acme"
|
||||
"github.com/go-acme/lego/log"
|
||||
"github.com/go-acme/lego/v3/acme"
|
||||
"github.com/go-acme/lego/v3/log"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -61,9 +61,21 @@ func (c *Certifier) getAuthorizations(order acme.ExtendedOrder) ([]acme.Authoriz
|
|||
}
|
||||
|
||||
func (c *Certifier) deactivateAuthorizations(order acme.ExtendedOrder) {
|
||||
for _, auth := range order.Authorizations {
|
||||
if err := c.core.Authorizations.Deactivate(auth); err != nil {
|
||||
log.Infof("Unable to deactivated authorizations: %s", auth)
|
||||
for _, authzURL := range order.Authorizations {
|
||||
auth, err := c.core.Authorizations.Get(authzURL)
|
||||
if err != nil {
|
||||
log.Infof("Unable to get the authorization for: %s", authzURL)
|
||||
continue
|
||||
}
|
||||
|
||||
if auth.Status == acme.StatusValid {
|
||||
log.Infof("Skipping deactivating of valid auth: %s", authzURL)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Infof("Deactivating auth: %s", authzURL)
|
||||
if c.core.Authorizations.Deactivate(authzURL) != nil {
|
||||
log.Infof("Unable to deactivate the authorization: %s", authzURL)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,12 +12,12 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/acme"
|
||||
"github.com/go-acme/lego/acme/api"
|
||||
"github.com/go-acme/lego/certcrypto"
|
||||
"github.com/go-acme/lego/challenge"
|
||||
"github.com/go-acme/lego/log"
|
||||
"github.com/go-acme/lego/platform/wait"
|
||||
"github.com/go-acme/lego/v3/acme"
|
||||
"github.com/go-acme/lego/v3/acme/api"
|
||||
"github.com/go-acme/lego/v3/certcrypto"
|
||||
"github.com/go-acme/lego/v3/challenge"
|
||||
"github.com/go-acme/lego/v3/log"
|
||||
"github.com/go-acme/lego/v3/platform/wait"
|
||||
"golang.org/x/crypto/ocsp"
|
||||
"golang.org/x/net/idna"
|
||||
)
|
||||
|
@ -210,8 +210,8 @@ func (c *Certifier) getForOrder(domains []string, order acme.ExtendedOrder, bund
|
|||
// Determine certificate name(s) based on the authorization resources
|
||||
commonName := domains[0]
|
||||
|
||||
// ACME draft Section 7.4 "Applying for Certificate Issuance"
|
||||
// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.4
|
||||
// RFC8555 Section 7.4 "Applying for Certificate Issuance"
|
||||
// https://tools.ietf.org/html/rfc8555#section-7.4
|
||||
// says:
|
||||
// Clients SHOULD NOT make any assumptions about the sort order of
|
||||
// "identifiers" or "authorizations" elements in the returned order
|
||||
|
@ -317,7 +317,7 @@ func (c *Certifier) Revoke(cert []byte) error {
|
|||
|
||||
x509Cert := certificates[0]
|
||||
if x509Cert.IsCA {
|
||||
return fmt.Errorf("certificate bundle starts with a CA certificate")
|
||||
return errors.New("certificate bundle starts with a CA certificate")
|
||||
}
|
||||
|
||||
revokeMsg := acme.RevokeCertMessage{
|
||||
|
@ -502,7 +502,7 @@ func checkOrderStatus(order acme.Order) (bool, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.4
|
||||
// https://tools.ietf.org/html/rfc8555#section-7.1.4
|
||||
// The domain name MUST be encoded
|
||||
// in the form in which it would appear in a certificate. That is, it
|
||||
// MUST be encoded according to the rules in Section 7 of [RFC5280].
|
|
@ -10,7 +10,7 @@ import (
|
|||
type obtainError map[string]error
|
||||
|
||||
func (e obtainError) Error() string {
|
||||
buffer := bytes.NewBufferString("acme: Error -> One or more domains had a problem:\n")
|
||||
buffer := bytes.NewBufferString("error: one or more domains had a problem:\n")
|
||||
|
||||
var domains []string
|
||||
for domain := range e {
|
|
@ -3,22 +3,22 @@ package challenge
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-acme/lego/acme"
|
||||
"github.com/go-acme/lego/v3/acme"
|
||||
)
|
||||
|
||||
// Type is a string that identifies a particular challenge type and version of ACME challenge.
|
||||
type Type string
|
||||
|
||||
const (
|
||||
// HTTP01 is the "http-01" ACME challenge https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-8.3
|
||||
// HTTP01 is the "http-01" ACME challenge https://tools.ietf.org/html/rfc8555#section-8.3
|
||||
// Note: ChallengePath returns the URL path to fulfill this challenge
|
||||
HTTP01 = Type("http-01")
|
||||
|
||||
// DNS01 is the "dns-01" ACME challenge https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-8.4
|
||||
// DNS01 is the "dns-01" ACME challenge https://tools.ietf.org/html/rfc8555#section-8.4
|
||||
// Note: GetRecord returns a DNS record which will fulfill this challenge
|
||||
DNS01 = Type("dns-01")
|
||||
|
||||
// TLSALPN01 is the "tls-alpn-01" ACME challenge https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05
|
||||
// TLSALPN01 is the "tls-alpn-01" ACME challenge https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-07
|
||||
TLSALPN01 = Type("tls-alpn-01")
|
||||
)
|
||||
|
|
@ -8,11 +8,11 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/acme"
|
||||
"github.com/go-acme/lego/acme/api"
|
||||
"github.com/go-acme/lego/challenge"
|
||||
"github.com/go-acme/lego/log"
|
||||
"github.com/go-acme/lego/platform/wait"
|
||||
"github.com/go-acme/lego/v3/acme"
|
||||
"github.com/go-acme/lego/v3/acme/api"
|
||||
"github.com/go-acme/lego/v3/challenge"
|
||||
"github.com/go-acme/lego/v3/log"
|
||||
"github.com/go-acme/lego/v3/platform/wait"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
|
@ -93,7 +93,7 @@ func (c *Challenge) PreSolve(authz acme.Authorization) error {
|
|||
|
||||
err = c.provider.Present(authz.Identifier.Value, chlng.Token, keyAuth)
|
||||
if err != nil {
|
||||
return fmt.Errorf("[%s] acme: error presenting token: %s", domain, err)
|
||||
return fmt.Errorf("[%s] acme: error presenting token: %w", domain, err)
|
||||
}
|
||||
|
||||
return nil
|
|
@ -1,6 +1,7 @@
|
|||
package dns01
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
@ -16,8 +17,8 @@ const defaultResolvConf = "/etc/resolv.conf"
|
|||
var dnsTimeout = 10 * time.Second
|
||||
|
||||
var (
|
||||
fqdnToZone = map[string]string{}
|
||||
muFqdnToZone sync.Mutex
|
||||
fqdnSoaCache = map[string]*soaCacheEntry{}
|
||||
muFqdnSoaCache sync.Mutex
|
||||
)
|
||||
|
||||
var defaultNameservers = []string{
|
||||
|
@ -28,11 +29,31 @@ var defaultNameservers = []string{
|
|||
// recursiveNameservers are used to pre-check DNS propagation
|
||||
var recursiveNameservers = getNameservers(defaultResolvConf, defaultNameservers)
|
||||
|
||||
// soaCacheEntry holds a cached SOA record (only selected fields)
|
||||
type soaCacheEntry struct {
|
||||
zone string // zone apex (a domain name)
|
||||
primaryNs string // primary nameserver for the zone apex
|
||||
expires time.Time // time when this cache entry should be evicted
|
||||
}
|
||||
|
||||
func newSoaCacheEntry(soa *dns.SOA) *soaCacheEntry {
|
||||
return &soaCacheEntry{
|
||||
zone: soa.Hdr.Name,
|
||||
primaryNs: soa.Ns,
|
||||
expires: time.Now().Add(time.Duration(soa.Refresh) * time.Second),
|
||||
}
|
||||
}
|
||||
|
||||
// isExpired checks whether a cache entry should be considered expired.
|
||||
func (cache *soaCacheEntry) isExpired() bool {
|
||||
return time.Now().After(cache.expires)
|
||||
}
|
||||
|
||||
// ClearFqdnCache clears the cache of fqdn to zone mappings. Primarily used in testing.
|
||||
func ClearFqdnCache() {
|
||||
muFqdnToZone.Lock()
|
||||
fqdnToZone = map[string]string{}
|
||||
muFqdnToZone.Unlock()
|
||||
muFqdnSoaCache.Lock()
|
||||
fqdnSoaCache = map[string]*soaCacheEntry{}
|
||||
muFqdnSoaCache.Unlock()
|
||||
}
|
||||
|
||||
func AddDNSTimeout(timeout time.Duration) ChallengeOption {
|
||||
|
@ -78,7 +99,7 @@ func lookupNameservers(fqdn string) ([]string, error) {
|
|||
|
||||
zone, err := FindZoneByFqdn(fqdn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not determine the zone: %v", err)
|
||||
return nil, fmt.Errorf("could not determine the zone: %w", err)
|
||||
}
|
||||
|
||||
r, err := dnsQuery(zone, dns.TypeNS, recursiveNameservers, true)
|
||||
|
@ -95,7 +116,23 @@ func lookupNameservers(fqdn string) ([]string, error) {
|
|||
if len(authoritativeNss) > 0 {
|
||||
return authoritativeNss, nil
|
||||
}
|
||||
return nil, fmt.Errorf("could not determine authoritative nameservers")
|
||||
return nil, errors.New("could not determine authoritative nameservers")
|
||||
}
|
||||
|
||||
// FindPrimaryNsByFqdn determines the primary nameserver of the zone apex for the given fqdn
|
||||
// by recursing up the domain labels until the nameserver returns a SOA record in the answer section.
|
||||
func FindPrimaryNsByFqdn(fqdn string) (string, error) {
|
||||
return FindPrimaryNsByFqdnCustom(fqdn, recursiveNameservers)
|
||||
}
|
||||
|
||||
// FindPrimaryNsByFqdnCustom determines the primary nameserver of the zone apex for the given fqdn
|
||||
// by recursing up the domain labels until the nameserver returns a SOA record in the answer section.
|
||||
func FindPrimaryNsByFqdnCustom(fqdn string, nameservers []string) (string, error) {
|
||||
soa, err := lookupSoaByFqdn(fqdn, nameservers)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return soa.primaryNs, nil
|
||||
}
|
||||
|
||||
// FindZoneByFqdn determines the zone apex for the given fqdn
|
||||
|
@ -107,14 +144,32 @@ func FindZoneByFqdn(fqdn string) (string, error) {
|
|||
// FindZoneByFqdnCustom determines the zone apex for the given fqdn
|
||||
// by recursing up the domain labels until the nameserver returns a SOA record in the answer section.
|
||||
func FindZoneByFqdnCustom(fqdn string, nameservers []string) (string, error) {
|
||||
muFqdnToZone.Lock()
|
||||
defer muFqdnToZone.Unlock()
|
||||
soa, err := lookupSoaByFqdn(fqdn, nameservers)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return soa.zone, nil
|
||||
}
|
||||
|
||||
// Do we have it cached?
|
||||
if zone, ok := fqdnToZone[fqdn]; ok {
|
||||
return zone, nil
|
||||
func lookupSoaByFqdn(fqdn string, nameservers []string) (*soaCacheEntry, error) {
|
||||
muFqdnSoaCache.Lock()
|
||||
defer muFqdnSoaCache.Unlock()
|
||||
|
||||
// Do we have it cached and is it still fresh?
|
||||
if ent := fqdnSoaCache[fqdn]; ent != nil && !ent.isExpired() {
|
||||
return ent, nil
|
||||
}
|
||||
|
||||
ent, err := fetchSoaByFqdn(fqdn, nameservers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fqdnSoaCache[fqdn] = ent
|
||||
return ent, nil
|
||||
}
|
||||
|
||||
func fetchSoaByFqdn(fqdn string, nameservers []string) (*soaCacheEntry, error) {
|
||||
var err error
|
||||
var in *dns.Msg
|
||||
|
||||
|
@ -134,7 +189,6 @@ func FindZoneByFqdnCustom(fqdn string, nameservers []string) (string, error) {
|
|||
switch in.Rcode {
|
||||
case dns.RcodeSuccess:
|
||||
// Check if we got a SOA RR in the answer section
|
||||
|
||||
if len(in.Answer) == 0 {
|
||||
continue
|
||||
}
|
||||
|
@ -147,20 +201,18 @@ func FindZoneByFqdnCustom(fqdn string, nameservers []string) (string, error) {
|
|||
|
||||
for _, ans := range in.Answer {
|
||||
if soa, ok := ans.(*dns.SOA); ok {
|
||||
zone := soa.Hdr.Name
|
||||
fqdnToZone[fqdn] = zone
|
||||
return zone, nil
|
||||
return newSoaCacheEntry(soa), nil
|
||||
}
|
||||
}
|
||||
case dns.RcodeNameError:
|
||||
// NXDOMAIN
|
||||
default:
|
||||
// Any response code other than NOERROR and NXDOMAIN is treated as error
|
||||
return "", fmt.Errorf("unexpected response code '%s' for %s", dns.RcodeToString[in.Rcode], domain)
|
||||
return nil, fmt.Errorf("unexpected response code '%s' for %s", dns.RcodeToString[in.Rcode], domain)
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("could not find the start of authority for %s%s", fqdn, formatDNSError(in, err))
|
||||
return nil, fmt.Errorf("could not find the start of authority for %s%s", fqdn, formatDNSError(in, err))
|
||||
}
|
||||
|
||||
// dnsMsgContainsCNAME checks for a CNAME answer in msg
|
184
vendor/github.com/go-acme/lego/v3/challenge/http01/domain_matcher.go
generated
vendored
Normal file
184
vendor/github.com/go-acme/lego/v3/challenge/http01/domain_matcher.go
generated
vendored
Normal file
|
@ -0,0 +1,184 @@
|
|||
package http01
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A domainMatcher tries to match a domain (the one we're requesting a certificate for)
|
||||
// in the HTTP request coming from the ACME validation servers.
|
||||
// This step is part of DNS rebind attack prevention,
|
||||
// where the webserver matches incoming requests to a list of domain the server acts authoritative for.
|
||||
//
|
||||
// The most simple check involves finding the domain in the HTTP Host header;
|
||||
// this is what hostMatcher does.
|
||||
// Use it, when the http01.ProviderServer is directly reachable from the internet,
|
||||
// or when it operates behind a transparent proxy.
|
||||
//
|
||||
// In many (reverse) proxy setups, Apache and NGINX traditionally move the Host header to a new header named X-Forwarded-Host.
|
||||
// Use arbitraryMatcher("X-Forwarded-Host") in this case,
|
||||
// or the appropriate header name for other proxy servers.
|
||||
//
|
||||
// RFC7239 has standardized the different forwarding headers into a single header named Forwarded.
|
||||
// The header value has a different format, so you should use forwardedMatcher
|
||||
// when the http01.ProviderServer operates behind a RFC7239 compatible proxy.
|
||||
// https://tools.ietf.org/html/rfc7239
|
||||
//
|
||||
// Note: RFC7239 also reminds us, "that an HTTP list [...] may be split over multiple header fields" (section 7.1),
|
||||
// meaning that
|
||||
// X-Header: a
|
||||
// X-Header: b
|
||||
// is equal to
|
||||
// X-Header: a, b
|
||||
//
|
||||
// All matcher implementations (explicitly not excluding arbitraryMatcher!)
|
||||
// have in common that they only match against the first value in such lists.
|
||||
type domainMatcher interface {
|
||||
// matches checks whether the request is valid for the given domain.
|
||||
matches(request *http.Request, domain string) bool
|
||||
|
||||
// name returns the header name used in the check.
|
||||
// This is primarily used to create meaningful error messages.
|
||||
name() string
|
||||
}
|
||||
|
||||
// hostMatcher checks whether (*net/http).Request.Host starts with a domain name.
|
||||
type hostMatcher struct{}
|
||||
|
||||
func (m *hostMatcher) name() string {
|
||||
return "Host"
|
||||
}
|
||||
|
||||
func (m *hostMatcher) matches(r *http.Request, domain string) bool {
|
||||
return strings.HasPrefix(r.Host, domain)
|
||||
}
|
||||
|
||||
// hostMatcher checks whether the specified (*net/http.Request).Header value starts with a domain name.
|
||||
type arbitraryMatcher string
|
||||
|
||||
func (m arbitraryMatcher) name() string {
|
||||
return string(m)
|
||||
}
|
||||
|
||||
func (m arbitraryMatcher) matches(r *http.Request, domain string) bool {
|
||||
return strings.HasPrefix(r.Header.Get(m.name()), domain)
|
||||
}
|
||||
|
||||
// forwardedMatcher checks whether the Forwarded header contains a "host" element starting with a domain name.
|
||||
// See https://tools.ietf.org/html/rfc7239 for details.
|
||||
type forwardedMatcher struct{}
|
||||
|
||||
func (m *forwardedMatcher) name() string {
|
||||
return "Forwarded"
|
||||
}
|
||||
|
||||
func (m *forwardedMatcher) matches(r *http.Request, domain string) bool {
|
||||
fwds, err := parseForwardedHeader(r.Header.Get(m.name()))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(fwds) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
host := fwds[0]["host"]
|
||||
return strings.HasPrefix(host, domain)
|
||||
}
|
||||
|
||||
// parsing requires some form of state machine
|
||||
func parseForwardedHeader(s string) (elements []map[string]string, err error) {
|
||||
cur := make(map[string]string)
|
||||
key := ""
|
||||
val := ""
|
||||
inquote := false
|
||||
|
||||
pos := 0
|
||||
l := len(s)
|
||||
for i := 0; i < l; i++ {
|
||||
r := rune(s[i])
|
||||
|
||||
if inquote {
|
||||
if r == '"' {
|
||||
cur[key] = s[pos:i]
|
||||
key = ""
|
||||
pos = i
|
||||
inquote = false
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case r == '"': // start of quoted-string
|
||||
if key == "" {
|
||||
return nil, fmt.Errorf("unexpected quoted string as pos %d", i)
|
||||
}
|
||||
inquote = true
|
||||
pos = i + 1
|
||||
|
||||
case r == ';': // end of forwarded-pair
|
||||
cur[key] = s[pos:i]
|
||||
key = ""
|
||||
i = skipWS(s, i)
|
||||
pos = i + 1
|
||||
|
||||
case r == '=': // end of token
|
||||
key = strings.ToLower(strings.TrimFunc(s[pos:i], isWS))
|
||||
i = skipWS(s, i)
|
||||
pos = i + 1
|
||||
|
||||
case r == ',': // end of forwarded-element
|
||||
if key != "" {
|
||||
if val == "" {
|
||||
val = s[pos:i]
|
||||
}
|
||||
cur[key] = val
|
||||
}
|
||||
elements = append(elements, cur)
|
||||
cur = make(map[string]string)
|
||||
key = ""
|
||||
val = ""
|
||||
|
||||
i = skipWS(s, i)
|
||||
pos = i + 1
|
||||
case tchar(r) || isWS(r): // valid token character or whitespace
|
||||
continue
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid token character at pos %d: %c", i, r)
|
||||
}
|
||||
}
|
||||
|
||||
if inquote {
|
||||
return nil, fmt.Errorf("unterminated quoted-string at pos %d", len(s))
|
||||
}
|
||||
|
||||
if key != "" {
|
||||
if pos < len(s) {
|
||||
val = s[pos:]
|
||||
}
|
||||
cur[key] = val
|
||||
}
|
||||
if len(cur) > 0 {
|
||||
elements = append(elements, cur)
|
||||
}
|
||||
return elements, nil
|
||||
}
|
||||
|
||||
func tchar(r rune) bool {
|
||||
return strings.ContainsRune("!#$%&'*+-.^_`|~", r) ||
|
||||
'0' <= r && r <= '9' ||
|
||||
'a' <= r && r <= 'z' ||
|
||||
'A' <= r && r <= 'Z'
|
||||
}
|
||||
|
||||
func skipWS(s string, i int) int {
|
||||
for isWS(rune(s[i+1])) {
|
||||
i++
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func isWS(r rune) bool {
|
||||
return strings.ContainsRune(" \t\v\r\n", r)
|
||||
}
|
|
@ -3,10 +3,10 @@ package http01
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-acme/lego/acme"
|
||||
"github.com/go-acme/lego/acme/api"
|
||||
"github.com/go-acme/lego/challenge"
|
||||
"github.com/go-acme/lego/log"
|
||||
"github.com/go-acme/lego/v3/acme"
|
||||
"github.com/go-acme/lego/v3/acme/api"
|
||||
"github.com/go-acme/lego/v3/challenge"
|
||||
"github.com/go-acme/lego/v3/log"
|
||||
)
|
||||
|
||||
type ValidateFunc func(core *api.Core, domain string, chlng acme.Challenge) error
|
||||
|
@ -51,12 +51,12 @@ func (c *Challenge) Solve(authz acme.Authorization) error {
|
|||
|
||||
err = c.provider.Present(authz.Identifier.Value, chlng.Token, keyAuth)
|
||||
if err != nil {
|
||||
return fmt.Errorf("[%s] acme: error presenting token: %v", domain, err)
|
||||
return fmt.Errorf("[%s] acme: error presenting token: %w", domain, err)
|
||||
}
|
||||
defer func() {
|
||||
err := c.provider.CleanUp(authz.Identifier.Value, chlng.Token, keyAuth)
|
||||
if err != nil {
|
||||
log.Warnf("[%s] acme: error cleaning up: %v", domain, err)
|
||||
log.Warnf("[%s] acme: cleaning up failed: %v", domain, err)
|
||||
}
|
||||
}()
|
||||
|
|
@ -4,9 +4,10 @@ import (
|
|||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"strings"
|
||||
|
||||
"github.com/go-acme/lego/log"
|
||||
"github.com/go-acme/lego/v3/log"
|
||||
)
|
||||
|
||||
// ProviderServer implements ChallengeProvider for `http-01` challenge
|
||||
|
@ -15,6 +16,7 @@ import (
|
|||
type ProviderServer struct {
|
||||
iface string
|
||||
port string
|
||||
matcher domainMatcher
|
||||
done chan bool
|
||||
listener net.Listener
|
||||
}
|
||||
|
@ -23,19 +25,19 @@ type ProviderServer struct {
|
|||
// Setting iface and / or port to an empty string will make the server fall back to
|
||||
// the "any" interface and port 80 respectively.
|
||||
func NewProviderServer(iface, port string) *ProviderServer {
|
||||
return &ProviderServer{iface: iface, port: port}
|
||||
if port == "" {
|
||||
port = "80"
|
||||
}
|
||||
|
||||
return &ProviderServer{iface: iface, port: port, matcher: &hostMatcher{}}
|
||||
}
|
||||
|
||||
// Present starts a web server and makes the token available at `ChallengePath(token)` for web requests.
|
||||
func (s *ProviderServer) Present(domain, token, keyAuth string) error {
|
||||
if s.port == "" {
|
||||
s.port = "80"
|
||||
}
|
||||
|
||||
var err error
|
||||
s.listener, err = net.Listen("tcp", s.GetAddress())
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not start HTTP server for challenge -> %v", err)
|
||||
return fmt.Errorf("could not start HTTP server for challenge: %w", err)
|
||||
}
|
||||
|
||||
s.done = make(chan bool)
|
||||
|
@ -57,14 +59,38 @@ func (s *ProviderServer) CleanUp(domain, token, keyAuth string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// SetProxyHeader changes the validation of incoming requests.
|
||||
// By default, s matches the "Host" header value to the domain name.
|
||||
//
|
||||
// When the server runs behind a proxy server, this is not the correct place to look at;
|
||||
// Apache and NGINX have traditionally moved the original Host header into a new header named "X-Forwarded-Host".
|
||||
// Other webservers might use different names;
|
||||
// and RFC7239 has standadized a new header named "Forwarded" (with slightly different semantics).
|
||||
//
|
||||
// The exact behavior depends on the value of headerName:
|
||||
// - "" (the empty string) and "Host" will restore the default and only check the Host header
|
||||
// - "Forwarded" will look for a Forwarded header, and inspect it according to https://tools.ietf.org/html/rfc7239
|
||||
// - any other value will check the header value with the same name
|
||||
func (s *ProviderServer) SetProxyHeader(headerName string) {
|
||||
switch h := textproto.CanonicalMIMEHeaderKey(headerName); h {
|
||||
case "", "Host":
|
||||
s.matcher = &hostMatcher{}
|
||||
case "Forwarded":
|
||||
s.matcher = &forwardedMatcher{}
|
||||
default:
|
||||
s.matcher = arbitraryMatcher(h)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ProviderServer) serve(domain, token, keyAuth string) {
|
||||
path := ChallengePath(token)
|
||||
|
||||
// The handler validates the HOST header and request type.
|
||||
// For validation it then writes the token the server returned with the challenge
|
||||
// The incoming request must will be validated to prevent DNS rebind attacks.
|
||||
// We only respond with the keyAuth, when we're receiving a GET requests with
|
||||
// the "Host" header matching the domain (the latter is configurable though SetProxyHeader).
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
|
||||
if strings.HasPrefix(r.Host, domain) && r.Method == http.MethodGet {
|
||||
if r.Method == http.MethodGet && s.matcher.matches(r, domain) {
|
||||
w.Header().Add("Content-Type", "text/plain")
|
||||
_, err := w.Write([]byte(keyAuth))
|
||||
if err != nil {
|
||||
|
@ -73,7 +99,7 @@ func (s *ProviderServer) serve(domain, token, keyAuth string) {
|
|||
}
|
||||
log.Infof("[%s] Served key authentication", domain)
|
||||
} else {
|
||||
log.Warnf("Received request for domain %s with method %s but the domain did not match any challenge. Please ensure your are passing the HOST header properly.", r.Host, r.Method)
|
||||
log.Warnf("Received request for domain %s with method %s but the domain did not match any challenge. Please ensure your are passing the %s header properly.", r.Host, r.Method, s.matcher.name())
|
||||
_, err := w.Write([]byte("TEST"))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
@ -10,7 +10,7 @@ import (
|
|||
type obtainError map[string]error
|
||||
|
||||
func (e obtainError) Error() string {
|
||||
buffer := bytes.NewBufferString("acme: Error -> One or more domains had a problem:\n")
|
||||
buffer := bytes.NewBufferString("error: one or more domains had a problem:\n")
|
||||
|
||||
var domains []string
|
||||
for domain := range e {
|
|
@ -4,9 +4,9 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/acme"
|
||||
"github.com/go-acme/lego/challenge"
|
||||
"github.com/go-acme/lego/log"
|
||||
"github.com/go-acme/lego/v3/acme"
|
||||
"github.com/go-acme/lego/v3/challenge"
|
||||
"github.com/go-acme/lego/v3/log"
|
||||
)
|
||||
|
||||
// Interface for all challenge solvers to implement.
|
||||
|
@ -167,7 +167,7 @@ func cleanUp(solvr solver, authz acme.Authorization) {
|
|||
domain := challenge.GetTargetedDomain(authz)
|
||||
err := solvr.CleanUp(authz)
|
||||
if err != nil {
|
||||
log.Warnf("[%s] acme: error cleaning up: %v ", domain, err)
|
||||
log.Warnf("[%s] acme: cleaning up failed: %v ", domain, err)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,14 +8,14 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff"
|
||||
"github.com/go-acme/lego/acme"
|
||||
"github.com/go-acme/lego/acme/api"
|
||||
"github.com/go-acme/lego/challenge"
|
||||
"github.com/go-acme/lego/challenge/dns01"
|
||||
"github.com/go-acme/lego/challenge/http01"
|
||||
"github.com/go-acme/lego/challenge/tlsalpn01"
|
||||
"github.com/go-acme/lego/log"
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
"github.com/go-acme/lego/v3/acme"
|
||||
"github.com/go-acme/lego/v3/acme/api"
|
||||
"github.com/go-acme/lego/v3/challenge"
|
||||
"github.com/go-acme/lego/v3/challenge/dns01"
|
||||
"github.com/go-acme/lego/v3/challenge/http01"
|
||||
"github.com/go-acme/lego/v3/challenge/tlsalpn01"
|
||||
"github.com/go-acme/lego/v3/log"
|
||||
)
|
||||
|
||||
type byType []acme.Challenge
|
||||
|
@ -79,7 +79,7 @@ func (c *SolverManager) chooseSolver(authz acme.Authorization) solver {
|
|||
func validate(core *api.Core, domain string, chlg acme.Challenge) error {
|
||||
chlng, err := core.Challenges.New(chlg.URL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initiate challenge: %v", err)
|
||||
return fmt.Errorf("failed to initiate challenge: %w", err)
|
||||
}
|
||||
|
||||
valid, err := checkChallengeStatus(chlng)
|
|
@ -8,15 +8,15 @@ import (
|
|||
"encoding/asn1"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-acme/lego/acme"
|
||||
"github.com/go-acme/lego/acme/api"
|
||||
"github.com/go-acme/lego/certcrypto"
|
||||
"github.com/go-acme/lego/challenge"
|
||||
"github.com/go-acme/lego/log"
|
||||
"github.com/go-acme/lego/v3/acme"
|
||||
"github.com/go-acme/lego/v3/acme/api"
|
||||
"github.com/go-acme/lego/v3/certcrypto"
|
||||
"github.com/go-acme/lego/v3/challenge"
|
||||
"github.com/go-acme/lego/v3/log"
|
||||
)
|
||||
|
||||
// idPeAcmeIdentifierV1 is the SMI Security for PKIX Certification Extension OID referencing the ACME extension.
|
||||
// Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05#section-5.1
|
||||
// Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-07#section-6.1
|
||||
var idPeAcmeIdentifierV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 31}
|
||||
|
||||
type ValidateFunc func(core *api.Core, domain string, chlng acme.Challenge) error
|
||||
|
@ -57,12 +57,12 @@ func (c *Challenge) Solve(authz acme.Authorization) error {
|
|||
|
||||
err = c.provider.Present(domain, chlng.Token, keyAuth)
|
||||
if err != nil {
|
||||
return fmt.Errorf("[%s] acme: error presenting token: %v", challenge.GetTargetedDomain(authz), err)
|
||||
return fmt.Errorf("[%s] acme: error presenting token: %w", challenge.GetTargetedDomain(authz), err)
|
||||
}
|
||||
defer func() {
|
||||
err := c.provider.CleanUp(domain, chlng.Token, keyAuth)
|
||||
if err != nil {
|
||||
log.Warnf("[%s] acme: error cleaning up: %v", challenge.GetTargetedDomain(authz), err)
|
||||
log.Warnf("[%s] acme: cleaning up failed: %v", challenge.GetTargetedDomain(authz), err)
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -83,7 +83,7 @@ func ChallengeBlocks(domain, keyAuth string) ([]byte, []byte, error) {
|
|||
|
||||
// Add the keyAuth digest as the acmeValidation-v1 extension
|
||||
// (marked as critical such that it won't be used by non-ACME software).
|
||||
// Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05#section-3
|
||||
// Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-07#section-3
|
||||
extensions := []pkix.Extension{
|
||||
{
|
||||
Id: idPeAcmeIdentifierV1,
|
|
@ -7,7 +7,7 @@ import (
|
|||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/go-acme/lego/log"
|
||||
"github.com/go-acme/lego/v3/log"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -60,13 +60,13 @@ func (s *ProviderServer) Present(domain, token, keyAuth string) error {
|
|||
|
||||
// We must set that the `acme-tls/1` application level protocol is supported
|
||||
// so that the protocol negotiation can succeed. Reference:
|
||||
// https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-01#section-5.2
|
||||
// https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-07#section-6.2
|
||||
tlsConf.NextProtos = []string{ACMETLS1Protocol}
|
||||
|
||||
// Create the listener with the created tls.Config.
|
||||
s.listener, err = tls.Listen("tcp", s.GetAddress(), tlsConf)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not start HTTPS server for challenge -> %v", err)
|
||||
return fmt.Errorf("could not start HTTPS server for challenge: %w", err)
|
||||
}
|
||||
|
||||
// Shut the server down when we're finished.
|
|
@ -4,10 +4,10 @@ import (
|
|||
"errors"
|
||||
"net/url"
|
||||
|
||||
"github.com/go-acme/lego/acme/api"
|
||||
"github.com/go-acme/lego/certificate"
|
||||
"github.com/go-acme/lego/challenge/resolver"
|
||||
"github.com/go-acme/lego/registration"
|
||||
"github.com/go-acme/lego/v3/acme/api"
|
||||
"github.com/go-acme/lego/v3/certificate"
|
||||
"github.com/go-acme/lego/v3/challenge/resolver"
|
||||
"github.com/go-acme/lego/v3/registration"
|
||||
)
|
||||
|
||||
// Client is the user-friendly way to ACME
|
|
@ -10,8 +10,8 @@ import (
|
|||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/certcrypto"
|
||||
"github.com/go-acme/lego/registration"
|
||||
"github.com/go-acme/lego/v3/certcrypto"
|
||||
"github.com/go-acme/lego/v3/registration"
|
||||
)
|
||||
|
||||
const (
|
|
@ -4,19 +4,19 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/log"
|
||||
"github.com/go-acme/lego/v3/log"
|
||||
)
|
||||
|
||||
// For polls the given function 'f', once every 'interval', up to 'timeout'.
|
||||
func For(msg string, timeout, interval time.Duration, f func() (bool, error)) error {
|
||||
log.Infof("Wait for %s [timeout: %s, interval: %s]", msg, timeout, interval)
|
||||
|
||||
var lastErr string
|
||||
var lastErr error
|
||||
timeUp := time.After(timeout)
|
||||
for {
|
||||
select {
|
||||
case <-timeUp:
|
||||
return fmt.Errorf("time limit exceeded: last error: %s", lastErr)
|
||||
return fmt.Errorf("time limit exceeded: last error: %w", lastErr)
|
||||
default:
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ func For(msg string, timeout, interval time.Duration, f func() (bool, error)) er
|
|||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
lastErr = err.Error()
|
||||
lastErr = err
|
||||
}
|
||||
|
||||
time.Sleep(interval)
|
|
@ -4,14 +4,14 @@ import (
|
|||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-acme/lego/acme"
|
||||
"github.com/go-acme/lego/acme/api"
|
||||
"github.com/go-acme/lego/log"
|
||||
"github.com/go-acme/lego/v3/acme"
|
||||
"github.com/go-acme/lego/v3/acme/api"
|
||||
"github.com/go-acme/lego/v3/log"
|
||||
)
|
||||
|
||||
// Resource represents all important information about a registration
|
||||
// of which the client needs to keep track itself.
|
||||
// Deprecated: will be remove in the future (acme.ExtendedAccount).
|
||||
// WARNING: will be remove 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"`
|
||||
|
@ -115,6 +115,30 @@ func (r *Registrar) QueryRegistration() (*Resource, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
// UpdateRegistration update the user registration on the ACME server.
|
||||
func (r *Registrar) UpdateRegistration(options RegisterOptions) (*Resource, error) {
|
||||
if r == nil || r.user == nil {
|
||||
return nil, errors.New("acme: cannot update a nil client or user")
|
||||
}
|
||||
|
||||
accMsg := acme.Account{
|
||||
TermsOfServiceAgreed: options.TermsOfServiceAgreed,
|
||||
Contact: []string{},
|
||||
}
|
||||
|
||||
if r.user.GetEmail() != "" {
|
||||
log.Infof("acme: Registering account for %s", r.user.GetEmail())
|
||||
accMsg.Contact = []string{"mailto:" + r.user.GetEmail()}
|
||||
}
|
||||
|
||||
account, err := r.core.Accounts.Update(r.user.GetRegistration().URI, accMsg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Resource{URI: account.Location, Body: account.Account}, nil
|
||||
}
|
||||
|
||||
// DeleteRegistration deletes the client's user registration from the ACME server.
|
||||
func (r *Registrar) DeleteRegistration() error {
|
||||
if r == nil || r.user == nil {
|
Loading…
Add table
Add a link
Reference in a new issue