ACME TLS ALPN
This commit is contained in:
parent
17ad5153b8
commit
139f280f35
258 changed files with 25528 additions and 1516 deletions
2
vendor/github.com/xenolf/lego/acme/challenges.go
generated
vendored
2
vendor/github.com/xenolf/lego/acme/challenges.go
generated
vendored
|
@ -10,4 +10,6 @@ const (
|
|||
// DNS01 is the "dns-01" ACME challenge https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#dns
|
||||
// Note: DNS01Record returns a DNS record which will fulfill this challenge
|
||||
DNS01 = Challenge("dns-01")
|
||||
// TLSALPN01 is the "tls-alpn-01" ACME challenge https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-01
|
||||
TLSALPN01 = Challenge("tls-alpn-01")
|
||||
)
|
||||
|
|
145
vendor/github.com/xenolf/lego/acme/client.go
generated
vendored
145
vendor/github.com/xenolf/lego/acme/client.go
generated
vendored
|
@ -5,6 +5,7 @@ import (
|
|||
"crypto"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
@ -81,8 +82,10 @@ func NewClient(caDirURL string, user User, keyType KeyType) (*Client, error) {
|
|||
// REVIEW: best possibility?
|
||||
// Add all available solvers with the right index as per ACME
|
||||
// spec to this map. Otherwise they won`t be found.
|
||||
solvers := make(map[Challenge]solver)
|
||||
solvers[HTTP01] = &httpChallenge{jws: jws, validate: validate, provider: &HTTPProviderServer{}}
|
||||
solvers := map[Challenge]solver{
|
||||
HTTP01: &httpChallenge{jws: jws, validate: validate, provider: &HTTPProviderServer{}},
|
||||
TLSALPN01: &tlsALPNChallenge{jws: jws, validate: validate, provider: &TLSALPNProviderServer{}},
|
||||
}
|
||||
|
||||
return &Client{directory: dir, user: user, jws: jws, keyType: keyType, solvers: solvers}, nil
|
||||
}
|
||||
|
@ -94,8 +97,10 @@ func (c *Client) SetChallengeProvider(challenge Challenge, p ChallengeProvider)
|
|||
c.solvers[challenge] = &httpChallenge{jws: c.jws, validate: validate, provider: p}
|
||||
case DNS01:
|
||||
c.solvers[challenge] = &dnsChallenge{jws: c.jws, validate: validate, provider: p}
|
||||
case TLSALPN01:
|
||||
c.solvers[challenge] = &tlsALPNChallenge{jws: c.jws, validate: validate, provider: p}
|
||||
default:
|
||||
return fmt.Errorf("Unknown challenge %v", challenge)
|
||||
return fmt.Errorf("unknown challenge %v", challenge)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -119,6 +124,24 @@ func (c *Client) SetHTTPAddress(iface string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// SetTLSAddress specifies a custom interface:port to be used for TLS based challenges.
|
||||
// If this option is not used, the default port 443 and all interfaces will be used.
|
||||
// To only specify a port and no interface use the ":port" notation.
|
||||
//
|
||||
// NOTE: This REPLACES any custom TLS-ALPN provider previously set by calling
|
||||
// c.SetChallengeProvider with the default TLS-ALPN challenge provider.
|
||||
func (c *Client) SetTLSAddress(iface string) error {
|
||||
host, port, err := net.SplitHostPort(iface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if chlng, ok := c.solvers[TLSALPN01]; ok {
|
||||
chlng.(*tlsALPNChallenge).provider = NewTLSALPNProviderServer(host, port)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExcludeChallenges explicitly removes challenges from the pool for solving.
|
||||
func (c *Client) ExcludeChallenges(challenges []Challenge) {
|
||||
// Loop through all challenges and delete the requested one if found.
|
||||
|
@ -142,7 +165,7 @@ func (c *Client) Register(tosAgreed bool) (*RegistrationResource, error) {
|
|||
if c == nil || c.user == nil {
|
||||
return nil, errors.New("acme: cannot register a nil client or user")
|
||||
}
|
||||
log.Printf("[INFO] acme: Registering account for %s", c.user.GetEmail())
|
||||
log.Infof("acme: Registering account for %s", c.user.GetEmail())
|
||||
|
||||
accMsg := accountMessage{}
|
||||
if c.user.GetEmail() != "" {
|
||||
|
@ -176,7 +199,7 @@ func (c *Client) RegisterWithExternalAccountBinding(tosAgreed bool, kid string,
|
|||
if c == nil || c.user == nil {
|
||||
return nil, errors.New("acme: cannot register a nil client or user")
|
||||
}
|
||||
log.Printf("[INFO] acme: Registering account (EAB) for %s", c.user.GetEmail())
|
||||
log.Infof("acme: Registering account (EAB) for %s", c.user.GetEmail())
|
||||
|
||||
accMsg := accountMessage{}
|
||||
if c.user.GetEmail() != "" {
|
||||
|
@ -222,7 +245,7 @@ func (c *Client) RegisterWithExternalAccountBinding(tosAgreed bool, kid string,
|
|||
// ResolveAccountByKey will attempt to look up an account using the given account key
|
||||
// and return its registration resource.
|
||||
func (c *Client) ResolveAccountByKey() (*RegistrationResource, error) {
|
||||
log.Printf("[INFO] acme: Trying to resolve account by key")
|
||||
log.Infof("acme: Trying to resolve account by key")
|
||||
|
||||
acc := accountMessage{OnlyReturnExisting: true}
|
||||
hdr, err := postJSON(c.jws, c.directory.NewAccountURL, acc, nil)
|
||||
|
@ -251,7 +274,7 @@ func (c *Client) DeleteRegistration() error {
|
|||
if c == nil || c.user == nil {
|
||||
return errors.New("acme: cannot unregister a nil client or user")
|
||||
}
|
||||
log.Printf("[INFO] acme: Deleting account for %s", c.user.GetEmail())
|
||||
log.Infof("acme: Deleting account for %s", c.user.GetEmail())
|
||||
|
||||
accMsg := accountMessage{
|
||||
Status: "deactivated",
|
||||
|
@ -271,7 +294,7 @@ func (c *Client) QueryRegistration() (*RegistrationResource, error) {
|
|||
return nil, errors.New("acme: cannot query the registration of a nil client or user")
|
||||
}
|
||||
// Log the URL here instead of the email as the email may not be set
|
||||
log.Printf("[INFO] acme: Querying account for %s", c.user.GetRegistration().URI)
|
||||
log.Infof("acme: Querying account for %s", c.user.GetRegistration().URI)
|
||||
|
||||
accMsg := accountMessage{}
|
||||
|
||||
|
@ -317,9 +340,9 @@ DNSNames:
|
|||
}
|
||||
|
||||
if bundle {
|
||||
log.Printf("[INFO][%s] acme: Obtaining bundled SAN certificate given a CSR", strings.Join(domains, ", "))
|
||||
log.Infof("[%s] acme: Obtaining bundled SAN certificate given a CSR", strings.Join(domains, ", "))
|
||||
} else {
|
||||
log.Printf("[INFO][%s] acme: Obtaining SAN certificate given a CSR", strings.Join(domains, ", "))
|
||||
log.Infof("[%s] acme: Obtaining SAN certificate given a CSR", strings.Join(domains, ", "))
|
||||
}
|
||||
|
||||
order, err := c.createOrderForIdentifiers(domains)
|
||||
|
@ -341,7 +364,7 @@ DNSNames:
|
|||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
|
||||
log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
|
||||
|
||||
failures := make(ObtainError)
|
||||
cert, err := c.requestCertificateForCsr(order, bundle, csr.Raw, nil)
|
||||
|
@ -377,9 +400,9 @@ func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto
|
|||
}
|
||||
|
||||
if bundle {
|
||||
log.Printf("[INFO][%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", "))
|
||||
log.Infof("[%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", "))
|
||||
} else {
|
||||
log.Printf("[INFO][%s] acme: Obtaining SAN certificate", strings.Join(domains, ", "))
|
||||
log.Infof("[%s] acme: Obtaining SAN certificate", strings.Join(domains, ", "))
|
||||
}
|
||||
|
||||
order, err := c.createOrderForIdentifiers(domains)
|
||||
|
@ -401,7 +424,7 @@ func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto
|
|||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
|
||||
log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
|
||||
|
||||
failures := make(ObtainError)
|
||||
cert, err := c.requestCertificateForOrder(order, bundle, privKey, mustStaple)
|
||||
|
@ -460,7 +483,7 @@ func (c *Client) RenewCertificate(cert CertificateResource, bundle, mustStaple b
|
|||
|
||||
// This is just meant to be informal for the user.
|
||||
timeLeft := x509Cert.NotAfter.Sub(time.Now().UTC())
|
||||
log.Printf("[INFO][%s] acme: Trying renewal with %d hours remaining", cert.Domain, int(timeLeft.Hours()))
|
||||
log.Infof("[%s] acme: Trying renewal with %d hours remaining", cert.Domain, int(timeLeft.Hours()))
|
||||
|
||||
// We always need to request a new certificate to renew.
|
||||
// Start by checking to see if the certificate was based off a CSR, and
|
||||
|
@ -534,7 +557,7 @@ func (c *Client) solveChallengeForAuthz(authorizations []authorization) error {
|
|||
for _, authz := range authorizations {
|
||||
if authz.Status == "valid" {
|
||||
// Boulder might recycle recent validated authz (see issue #267)
|
||||
log.Printf("[INFO][%s] acme: Authorization already valid; skipping challenge", authz.Identifier.Value)
|
||||
log.Infof("[%s] acme: Authorization already valid; skipping challenge", authz.Identifier.Value)
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -565,7 +588,7 @@ func (c *Client) chooseSolver(auth authorization, domain string) (int, solver) {
|
|||
if solver, ok := c.solvers[Challenge(challenge.Type)]; ok {
|
||||
return i, solver
|
||||
}
|
||||
log.Printf("[INFO][%s] acme: Could not find solver for: %s", domain, challenge.Type)
|
||||
log.Infof("[%s] acme: Could not find solver for: %s", domain, challenge.Type)
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
@ -617,7 +640,7 @@ func (c *Client) getAuthzForOrder(order orderResource) ([]authorization, error)
|
|||
|
||||
func logAuthz(order orderResource) {
|
||||
for i, auth := range order.Authorizations {
|
||||
log.Printf("[INFO][%s] AuthURL: %s", order.Identifiers[i].Value, auth)
|
||||
log.Infof("[%s] AuthURL: %s", order.Identifiers[i].Value, auth)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -640,9 +663,18 @@ func (c *Client) requestCertificateForOrder(order orderResource, bundle bool, pr
|
|||
|
||||
// determine certificate name(s) based on the authorization resources
|
||||
commonName := order.Domains[0]
|
||||
var san []string
|
||||
|
||||
// ACME draft Section 7.4 "Applying for Certificate Issuance"
|
||||
// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.4
|
||||
// says:
|
||||
// Clients SHOULD NOT make any assumptions about the sort order of
|
||||
// "identifiers" or "authorizations" elements in the returned order
|
||||
// object.
|
||||
san := []string{commonName}
|
||||
for _, auth := range order.Identifiers {
|
||||
san = append(san, auth.Value)
|
||||
if auth.Value != commonName {
|
||||
san = append(san, auth.Value)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: should the CSR be customizable?
|
||||
|
@ -659,13 +691,13 @@ func (c *Client) requestCertificateForCsr(order orderResource, bundle bool, csr
|
|||
|
||||
csrString := base64.RawURLEncoding.EncodeToString(csr)
|
||||
var retOrder orderMessage
|
||||
_, error := postJSON(c.jws, order.Finalize, csrMessage{Csr: csrString}, &retOrder)
|
||||
if error != nil {
|
||||
return nil, error
|
||||
_, err := postJSON(c.jws, order.Finalize, csrMessage{Csr: csrString}, &retOrder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if retOrder.Status == "invalid" {
|
||||
return nil, error
|
||||
return nil, err
|
||||
}
|
||||
|
||||
certRes := CertificateResource{
|
||||
|
@ -686,25 +718,30 @@ func (c *Client) requestCertificateForCsr(order orderResource, bundle bool, csr
|
|||
}
|
||||
}
|
||||
|
||||
maxChecks := 1000
|
||||
for i := 0; i < maxChecks; i++ {
|
||||
_, err := getJSON(order.URL, &retOrder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
done, err := c.checkCertResponse(retOrder, &certRes, bundle)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if done {
|
||||
break
|
||||
}
|
||||
if i == maxChecks-1 {
|
||||
return nil, fmt.Errorf("polled for certificate %d times; giving up", i)
|
||||
stopTimer := time.NewTimer(30 * time.Second)
|
||||
defer stopTimer.Stop()
|
||||
retryTick := time.NewTicker(500 * time.Millisecond)
|
||||
defer retryTick.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-stopTimer.C:
|
||||
return nil, errors.New("certificate polling timed out")
|
||||
case <-retryTick.C:
|
||||
_, err := getJSON(order.URL, &retOrder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
done, err := c.checkCertResponse(retOrder, &certRes, bundle)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if done {
|
||||
return &certRes, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &certRes, nil
|
||||
}
|
||||
|
||||
// checkCertResponse checks to see if the certificate is ready and a link is contained in the
|
||||
|
@ -726,15 +763,16 @@ func (c *Client) checkCertResponse(order orderMessage, certRes *CertificateResou
|
|||
return false, err
|
||||
}
|
||||
|
||||
// The issuer certificate link is always supplied via an "up" link
|
||||
// in the response headers of a new certificate.
|
||||
// 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
|
||||
links := parseLinks(resp.Header["Link"])
|
||||
if link, ok := links["up"]; ok {
|
||||
issuerCert, err := c.getIssuerCertificate(link)
|
||||
|
||||
if err != nil {
|
||||
// If we fail to acquire the issuer cert, return the issued certificate - do not fail.
|
||||
log.Printf("[WARNING][%s] acme: Could not bundle issuer certificate: %v", certRes.Domain, err)
|
||||
log.Warnf("[%s] acme: Could not bundle issuer certificate: %v", certRes.Domain, err)
|
||||
} else {
|
||||
issuerCert = pemEncode(derCertificateBytes(issuerCert))
|
||||
|
||||
|
@ -746,26 +784,33 @@ func (c *Client) checkCertResponse(order orderMessage, certRes *CertificateResou
|
|||
|
||||
certRes.IssuerCertificate = issuerCert
|
||||
}
|
||||
} else {
|
||||
// Get issuerCert from bundled response from Let's Encrypt
|
||||
// See https://community.letsencrypt.org/t/acme-v2-no-up-link-in-response/64962
|
||||
_, rest := pem.Decode(cert)
|
||||
if rest != nil {
|
||||
certRes.IssuerCertificate = rest
|
||||
}
|
||||
}
|
||||
|
||||
certRes.Certificate = cert
|
||||
certRes.CertURL = order.Certificate
|
||||
certRes.CertStableURL = order.Certificate
|
||||
log.Printf("[INFO][%s] Server responded with a certificate.", certRes.Domain)
|
||||
log.Infof("[%s] Server responded with a certificate.", certRes.Domain)
|
||||
return true, nil
|
||||
|
||||
case "processing":
|
||||
return false, nil
|
||||
case "invalid":
|
||||
return false, errors.New("Order has invalid state: invalid")
|
||||
return false, errors.New("order has invalid state: invalid")
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// getIssuerCertificate requests the issuer certificate
|
||||
func (c *Client) getIssuerCertificate(url string) ([]byte, error) {
|
||||
log.Printf("[INFO] acme: Requesting issuer cert from %s", url)
|
||||
log.Infof("acme: Requesting issuer cert from %s", url)
|
||||
resp, err := httpGet(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -819,7 +864,7 @@ func validate(j *jws, domain, uri string, c challenge) error {
|
|||
for {
|
||||
switch chlng.Status {
|
||||
case "valid":
|
||||
log.Printf("[INFO][%s] The server validated our request", domain)
|
||||
log.Infof("[%s] The server validated our request", domain)
|
||||
return nil
|
||||
case "pending":
|
||||
case "processing":
|
||||
|
|
11
vendor/github.com/xenolf/lego/acme/crypto.go
generated
vendored
11
vendor/github.com/xenolf/lego/acme/crypto.go
generated
vendored
|
@ -215,9 +215,7 @@ func generatePrivateKey(keyType KeyType) (crypto.PrivateKey, error) {
|
|||
|
||||
func generateCsr(privateKey crypto.PrivateKey, domain string, san []string, mustStaple bool) ([]byte, error) {
|
||||
template := x509.CertificateRequest{
|
||||
Subject: pkix.Name{
|
||||
CommonName: domain,
|
||||
},
|
||||
Subject: pkix.Name{CommonName: domain},
|
||||
}
|
||||
|
||||
if len(san) > 0 {
|
||||
|
@ -303,8 +301,8 @@ func getCertExpiration(cert []byte) (time.Time, error) {
|
|||
return pCert.NotAfter, nil
|
||||
}
|
||||
|
||||
func generatePemCert(privKey *rsa.PrivateKey, domain string) ([]byte, error) {
|
||||
derBytes, err := generateDerCert(privKey, time.Time{}, domain)
|
||||
func generatePemCert(privKey *rsa.PrivateKey, domain string, extensions []pkix.Extension) ([]byte, error) {
|
||||
derBytes, err := generateDerCert(privKey, time.Time{}, domain, extensions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -312,7 +310,7 @@ func generatePemCert(privKey *rsa.PrivateKey, domain string) ([]byte, error) {
|
|||
return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}), nil
|
||||
}
|
||||
|
||||
func generateDerCert(privKey *rsa.PrivateKey, expiration time.Time, domain string) ([]byte, error) {
|
||||
func generateDerCert(privKey *rsa.PrivateKey, expiration time.Time, domain string, extensions []pkix.Extension) ([]byte, error) {
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
|
@ -334,6 +332,7 @@ func generateDerCert(privKey *rsa.PrivateKey, expiration time.Time, domain strin
|
|||
KeyUsage: x509.KeyUsageKeyEncipherment,
|
||||
BasicConstraintsValid: true,
|
||||
DNSNames: []string{domain},
|
||||
ExtraExtensions: extensions,
|
||||
}
|
||||
|
||||
return x509.CreateCertificate(rand.Reader, &template, &template, &privKey.PublicKey, privKey)
|
||||
|
|
10
vendor/github.com/xenolf/lego/acme/dns_challenge.go
generated
vendored
10
vendor/github.com/xenolf/lego/acme/dns_challenge.go
generated
vendored
|
@ -72,10 +72,10 @@ type dnsChallenge struct {
|
|||
}
|
||||
|
||||
func (s *dnsChallenge) Solve(chlng challenge, domain string) error {
|
||||
log.Printf("[INFO][%s] acme: Trying to solve DNS-01", domain)
|
||||
log.Infof("[%s] acme: Trying to solve DNS-01", domain)
|
||||
|
||||
if s.provider == nil {
|
||||
return errors.New("No DNS Provider configured")
|
||||
return errors.New("no DNS Provider configured")
|
||||
}
|
||||
|
||||
// Generate the Key Authorization for the challenge
|
||||
|
@ -86,18 +86,18 @@ func (s *dnsChallenge) Solve(chlng challenge, domain string) error {
|
|||
|
||||
err = s.provider.Present(domain, chlng.Token, keyAuth)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error presenting token: %s", err)
|
||||
return fmt.Errorf("error presenting token: %s", err)
|
||||
}
|
||||
defer func() {
|
||||
err := s.provider.CleanUp(domain, chlng.Token, keyAuth)
|
||||
if err != nil {
|
||||
log.Printf("Error cleaning up %s: %v ", domain, err)
|
||||
log.Warnf("Error cleaning up %s: %v ", domain, err)
|
||||
}
|
||||
}()
|
||||
|
||||
fqdn, value, _ := DNS01Record(domain, keyAuth)
|
||||
|
||||
log.Printf("[INFO][%s] Checking DNS record propagation using %+v", domain, RecursiveNameservers)
|
||||
log.Infof("[%s] Checking DNS record propagation using %+v", domain, RecursiveNameservers)
|
||||
|
||||
var timeout, interval time.Duration
|
||||
switch provider := s.provider.(type) {
|
||||
|
|
10
vendor/github.com/xenolf/lego/acme/dns_challenge_manual.go
generated
vendored
10
vendor/github.com/xenolf/lego/acme/dns_challenge_manual.go
generated
vendored
|
@ -30,9 +30,9 @@ func (*DNSProviderManual) Present(domain, token, keyAuth string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
log.Printf("[INFO] acme: Please create the following TXT record in your %s zone:", authZone)
|
||||
log.Printf("[INFO] acme: %s", dnsRecord)
|
||||
log.Printf("[INFO] acme: Press 'Enter' when you are done")
|
||||
log.Infof("acme: Please create the following TXT record in your %s zone:", authZone)
|
||||
log.Infof("acme: %s", dnsRecord)
|
||||
log.Infof("acme: Press 'Enter' when you are done")
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
_, _ = reader.ReadString('\n')
|
||||
|
@ -49,7 +49,7 @@ func (*DNSProviderManual) CleanUp(domain, token, keyAuth string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
log.Printf("[INFO] acme: You can now remove this TXT record from your %s zone:", authZone)
|
||||
log.Printf("[INFO] acme: %s", dnsRecord)
|
||||
log.Infof("acme: You can now remove this TXT record from your %s zone:", authZone)
|
||||
log.Infof("acme: %s", dnsRecord)
|
||||
return nil
|
||||
}
|
||||
|
|
82
vendor/github.com/xenolf/lego/acme/http.go
generated
vendored
82
vendor/github.com/xenolf/lego/acme/http.go
generated
vendored
|
@ -1,33 +1,45 @@
|
|||
package acme
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// UserAgent (if non-empty) will be tacked onto the User-Agent string in requests.
|
||||
var UserAgent string
|
||||
var (
|
||||
// UserAgent (if non-empty) will be tacked onto the User-Agent string in requests.
|
||||
UserAgent string
|
||||
|
||||
// HTTPClient is an HTTP client with a reasonable timeout value.
|
||||
var HTTPClient = http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 15 * time.Second,
|
||||
ResponseHeaderTimeout: 15 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
},
|
||||
}
|
||||
// HTTPClient is an HTTP client with a reasonable timeout value and
|
||||
// potentially a custom *x509.CertPool based on the caCertificatesEnvVar
|
||||
// environment variable (see the `initCertPool` function)
|
||||
HTTPClient = http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).DialContext,
|
||||
TLSHandshakeTimeout: 15 * time.Second,
|
||||
ResponseHeaderTimeout: 15 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
TLSClientConfig: &tls.Config{
|
||||
ServerName: os.Getenv(caServerNameEnvVar),
|
||||
RootCAs: initCertPool(),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
// defaultGoUserAgent is the Go HTTP package user agent string. Too
|
||||
|
@ -36,12 +48,46 @@ const (
|
|||
|
||||
// ourUserAgent is the User-Agent of this underlying library package.
|
||||
ourUserAgent = "xenolf-acme"
|
||||
|
||||
// caCertificatesEnvVar is the environment variable name that can be used to
|
||||
// specify the path to PEM encoded CA Certificates that can be used to
|
||||
// authenticate an ACME server with a HTTPS certificate not issued by a CA in
|
||||
// the system-wide trusted root list.
|
||||
caCertificatesEnvVar = "LEGO_CA_CERTIFICATES"
|
||||
|
||||
// caServerNameEnvVar is the environment variable name that can be used to
|
||||
// specify the CA server name that can be used to
|
||||
// authenticate an ACME server with a HTTPS certificate not issued by a CA in
|
||||
// the system-wide trusted root list.
|
||||
caServerNameEnvVar = "LEGO_CA_SERVER_NAME"
|
||||
)
|
||||
|
||||
// initCertPool creates a *x509.CertPool populated with the PEM certificates
|
||||
// found in the filepath specified in the caCertificatesEnvVar OS environment
|
||||
// variable. If the caCertificatesEnvVar is not set then initCertPool will
|
||||
// return nil. If there is an error creating a *x509.CertPool from the provided
|
||||
// caCertificatesEnvVar value then initCertPool will panic.
|
||||
func initCertPool() *x509.CertPool {
|
||||
if customCACertsPath := os.Getenv(caCertificatesEnvVar); customCACertsPath != "" {
|
||||
customCAs, err := ioutil.ReadFile(customCACertsPath)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("error reading %s=%q: %v",
|
||||
caCertificatesEnvVar, customCACertsPath, err))
|
||||
}
|
||||
certPool := x509.NewCertPool()
|
||||
if ok := certPool.AppendCertsFromPEM(customCAs); !ok {
|
||||
panic(fmt.Sprintf("error creating x509 cert pool from %s=%q: %v",
|
||||
caCertificatesEnvVar, customCACertsPath, err))
|
||||
}
|
||||
return certPool
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// httpHead performs a HEAD request with a proper User-Agent string.
|
||||
// The response body (resp.Body) is already closed when this function returns.
|
||||
func httpHead(url string) (resp *http.Response, err error) {
|
||||
req, err := http.NewRequest("HEAD", url, nil)
|
||||
req, err := http.NewRequest(http.MethodHead, url, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to head %q: %v", url, err)
|
||||
}
|
||||
|
@ -59,7 +105,7 @@ func httpHead(url string) (resp *http.Response, err error) {
|
|||
// httpPost performs a POST request with a proper User-Agent string.
|
||||
// Callers should close resp.Body when done reading from it.
|
||||
func httpPost(url string, bodyType string, body io.Reader) (resp *http.Response, err error) {
|
||||
req, err := http.NewRequest("POST", url, body)
|
||||
req, err := http.NewRequest(http.MethodPost, url, body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to post %q: %v", url, err)
|
||||
}
|
||||
|
@ -72,7 +118,7 @@ func httpPost(url string, bodyType string, body io.Reader) (resp *http.Response,
|
|||
// httpGet performs a GET request with a proper User-Agent string.
|
||||
// Callers should close resp.Body when done reading from it.
|
||||
func httpGet(url string) (resp *http.Response, err error) {
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get %q: %v", url, err)
|
||||
}
|
||||
|
|
4
vendor/github.com/xenolf/lego/acme/http_challenge.go
generated
vendored
4
vendor/github.com/xenolf/lego/acme/http_challenge.go
generated
vendored
|
@ -19,7 +19,7 @@ func HTTP01ChallengePath(token string) string {
|
|||
|
||||
func (s *httpChallenge) Solve(chlng challenge, domain string) error {
|
||||
|
||||
log.Printf("[INFO][%s] acme: Trying to solve HTTP-01", domain)
|
||||
log.Infof("[%s] acme: Trying to solve HTTP-01", domain)
|
||||
|
||||
// Generate the Key Authorization for the challenge
|
||||
keyAuth, err := getKeyAuthorization(chlng.Token, s.jws.privKey)
|
||||
|
@ -34,7 +34,7 @@ func (s *httpChallenge) Solve(chlng challenge, domain string) error {
|
|||
defer func() {
|
||||
err := s.provider.CleanUp(domain, chlng.Token, keyAuth)
|
||||
if err != nil {
|
||||
log.Printf("[%s] error cleaning up: %v", domain, err)
|
||||
log.Warnf("[%s] error cleaning up: %v", domain, err)
|
||||
}
|
||||
}()
|
||||
|
||||
|
|
6
vendor/github.com/xenolf/lego/acme/http_challenge_server.go
generated
vendored
6
vendor/github.com/xenolf/lego/acme/http_challenge_server.go
generated
vendored
|
@ -60,12 +60,12 @@ func (s *HTTPProviderServer) serve(domain, token, keyAuth string) {
|
|||
// For validation it then writes the token the server returned with the challenge
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
|
||||
if strings.HasPrefix(r.Host, domain) && r.Method == "GET" {
|
||||
if strings.HasPrefix(r.Host, domain) && r.Method == http.MethodGet {
|
||||
w.Header().Add("Content-Type", "text/plain")
|
||||
w.Write([]byte(keyAuth))
|
||||
log.Printf("[INFO][%s] Served key authentication", domain)
|
||||
log.Infof("[%s] Served key authentication", domain)
|
||||
} else {
|
||||
log.Printf("[WARN] 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 HOST header properly.", r.Host, r.Method)
|
||||
w.Write([]byte("TEST"))
|
||||
}
|
||||
})
|
||||
|
|
104
vendor/github.com/xenolf/lego/acme/tls_alpn_challenge.go
generated
vendored
Normal file
104
vendor/github.com/xenolf/lego/acme/tls_alpn_challenge.go
generated
vendored
Normal file
|
@ -0,0 +1,104 @@
|
|||
package acme
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"fmt"
|
||||
|
||||
"github.com/xenolf/lego/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-01#section-5.1
|
||||
var idPeAcmeIdentifierV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 30, 1}
|
||||
|
||||
type tlsALPNChallenge struct {
|
||||
jws *jws
|
||||
validate validateFunc
|
||||
provider ChallengeProvider
|
||||
}
|
||||
|
||||
// Solve manages the provider to validate and solve the challenge.
|
||||
func (t *tlsALPNChallenge) Solve(chlng challenge, domain string) error {
|
||||
log.Infof("[%s] acme: Trying to solve TLS-ALPN-01", domain)
|
||||
|
||||
// Generate the Key Authorization for the challenge
|
||||
keyAuth, err := getKeyAuthorization(chlng.Token, t.jws.privKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = t.provider.Present(domain, chlng.Token, keyAuth)
|
||||
if err != nil {
|
||||
return fmt.Errorf("[%s] error presenting token: %v", domain, err)
|
||||
}
|
||||
defer func() {
|
||||
err := t.provider.CleanUp(domain, chlng.Token, keyAuth)
|
||||
if err != nil {
|
||||
log.Warnf("[%s] error cleaning up: %v", domain, err)
|
||||
}
|
||||
}()
|
||||
|
||||
return t.validate(t.jws, domain, chlng.URL, challenge{Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth})
|
||||
}
|
||||
|
||||
// TLSALPNChallengeBlocks returns PEM blocks (certPEMBlock, keyPEMBlock) with the acmeValidation-v1 extension
|
||||
// and domain name for the `tls-alpn-01` challenge.
|
||||
func TLSALPNChallengeBlocks(domain, keyAuth string) ([]byte, []byte, error) {
|
||||
// Compute the SHA-256 digest of the key authorization.
|
||||
zBytes := sha256.Sum256([]byte(keyAuth))
|
||||
|
||||
value, err := asn1.Marshal(zBytes[:sha256.Size])
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// 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-01#section-3
|
||||
extensions := []pkix.Extension{
|
||||
{
|
||||
Id: idPeAcmeIdentifierV1,
|
||||
Critical: true,
|
||||
Value: value,
|
||||
},
|
||||
}
|
||||
|
||||
// Generate a new RSA key for the certificates.
|
||||
tempPrivKey, err := generatePrivateKey(RSA2048)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
rsaPrivKey := tempPrivKey.(*rsa.PrivateKey)
|
||||
|
||||
// Generate the PEM certificate using the provided private key, domain, and extra extensions.
|
||||
tempCertPEM, err := generatePemCert(rsaPrivKey, domain, extensions)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Encode the private key into a PEM format. We'll need to use it to generate the x509 keypair.
|
||||
rsaPrivPEM := pemEncode(rsaPrivKey)
|
||||
|
||||
return tempCertPEM, rsaPrivPEM, nil
|
||||
}
|
||||
|
||||
// TLSALPNChallengeCert returns a certificate with the acmeValidation-v1 extension
|
||||
// and domain name for the `tls-alpn-01` challenge.
|
||||
func TLSALPNChallengeCert(domain, keyAuth string) (*tls.Certificate, error) {
|
||||
tempCertPEM, rsaPrivPEM, err := TLSALPNChallengeBlocks(domain, keyAuth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
certificate, err := tls.X509KeyPair(tempCertPEM, rsaPrivPEM)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &certificate, nil
|
||||
}
|
86
vendor/github.com/xenolf/lego/acme/tls_alpn_challenge_server.go
generated
vendored
Normal file
86
vendor/github.com/xenolf/lego/acme/tls_alpn_challenge_server.go
generated
vendored
Normal file
|
@ -0,0 +1,86 @@
|
|||
package acme
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
// ACMETLS1Protocol is the ALPN Protocol ID for the ACME-TLS/1 Protocol.
|
||||
ACMETLS1Protocol = "acme-tls/1"
|
||||
|
||||
// defaultTLSPort is the port that the TLSALPNProviderServer will default to
|
||||
// when no other port is provided.
|
||||
defaultTLSPort = "443"
|
||||
)
|
||||
|
||||
// TLSALPNProviderServer implements ChallengeProvider for `TLS-ALPN-01`
|
||||
// challenge. It may be instantiated without using the NewTLSALPNProviderServer
|
||||
// if you want only to use the default values.
|
||||
type TLSALPNProviderServer struct {
|
||||
iface string
|
||||
port string
|
||||
listener net.Listener
|
||||
}
|
||||
|
||||
// NewTLSALPNProviderServer creates a new TLSALPNProviderServer on the selected
|
||||
// interface and port. Setting iface and / or port to an empty string will make
|
||||
// the server fall back to the "any" interface and port 443 respectively.
|
||||
func NewTLSALPNProviderServer(iface, port string) *TLSALPNProviderServer {
|
||||
return &TLSALPNProviderServer{iface: iface, port: port}
|
||||
}
|
||||
|
||||
// Present generates a certificate with a SHA-256 digest of the keyAuth provided
|
||||
// as the acmeValidation-v1 extension value to conform to the ACME-TLS-ALPN
|
||||
// spec.
|
||||
func (t *TLSALPNProviderServer) Present(domain, token, keyAuth string) error {
|
||||
if t.port == "" {
|
||||
// Fallback to port 443 if the port was not provided.
|
||||
t.port = defaultTLSPort
|
||||
}
|
||||
|
||||
// Generate the challenge certificate using the provided keyAuth and domain.
|
||||
cert, err := TLSALPNChallengeCert(domain, keyAuth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Place the generated certificate with the extension into the TLS config
|
||||
// so that it can serve the correct details.
|
||||
tlsConf := new(tls.Config)
|
||||
tlsConf.Certificates = []tls.Certificate{*cert}
|
||||
|
||||
// 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
|
||||
tlsConf.NextProtos = []string{ACMETLS1Protocol}
|
||||
|
||||
// Create the listener with the created tls.Config.
|
||||
t.listener, err = tls.Listen("tcp", net.JoinHostPort(t.iface, t.port), tlsConf)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not start HTTPS server for challenge -> %v", err)
|
||||
}
|
||||
|
||||
// Shut the server down when we're finished.
|
||||
go func() {
|
||||
http.Serve(t.listener, nil)
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp closes the HTTPS server.
|
||||
func (t *TLSALPNProviderServer) CleanUp(domain, token, keyAuth string) error {
|
||||
if t.listener == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Server was created, close it.
|
||||
if err := t.listener.Close(); err != nil && err != http.ErrServerClosed {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue