forked from TrueCloudLab/lego
Clean some stuff up and refactor getCerts for some concurrency.
This commit is contained in:
parent
62b4ebe72b
commit
caa6e78289
3 changed files with 104 additions and 42 deletions
|
@ -11,7 +11,9 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Logger is used to log errors; if nil, the default log.Logger is used.
|
// Logger is used to log errors; if nil, the default log.Logger is used.
|
||||||
|
@ -163,6 +165,7 @@ func (c *Client) ObtainCertificates(domains []string) ([]CertificateResource, er
|
||||||
return c.requestCertificates(challenges)
|
return c.requestCertificates(challenges)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RevokeCertificate takes a DER encoded certificate and tries to revoke it at the CA.
|
||||||
func (c *Client) RevokeCertificate(certificate []byte) error {
|
func (c *Client) RevokeCertificate(certificate []byte) error {
|
||||||
encodedCert := base64.URLEncoding.EncodeToString(certificate)
|
encodedCert := base64.URLEncoding.EncodeToString(certificate)
|
||||||
|
|
||||||
|
@ -287,42 +290,100 @@ func (c *Client) getChallenges(domains []string) []*authorizationResource {
|
||||||
// It then uses these to request a certificate from the CA and returns the list of successfully
|
// It then uses these to request a certificate from the CA and returns the list of successfully
|
||||||
// granted certificates.
|
// granted certificates.
|
||||||
func (c *Client) requestCertificates(challenges []*authorizationResource) ([]CertificateResource, error) {
|
func (c *Client) requestCertificates(challenges []*authorizationResource) ([]CertificateResource, error) {
|
||||||
var certs []CertificateResource
|
resc, errc := make(chan CertificateResource), make(chan error)
|
||||||
for _, authz := range challenges {
|
for _, authz := range challenges {
|
||||||
|
go c.requestCertificate(authz, resc, errc)
|
||||||
|
}
|
||||||
|
|
||||||
|
var certs []CertificateResource
|
||||||
|
for i := 0; i < len(challenges); i++ {
|
||||||
|
select {
|
||||||
|
case res := <-resc:
|
||||||
|
certs = append(certs, res)
|
||||||
|
case err := <-errc:
|
||||||
|
logger().Printf("%v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return certs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) requestCertificate(authz *authorizationResource, result chan CertificateResource, errc chan error) {
|
||||||
privKey, err := generatePrivateKey(c.keyBits)
|
privKey, err := generatePrivateKey(c.keyBits)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
errc <- err
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
csr, err := generateCsr(privKey, authz.Domain)
|
csr, err := generateCsr(privKey, authz.Domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
errc <- err
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
csrString := base64.URLEncoding.EncodeToString(csr)
|
csrString := base64.URLEncoding.EncodeToString(csr)
|
||||||
jsonBytes, err := json.Marshal(csrMessage{Resource: "new-cert", Csr: csrString, Authorizations: []string{authz.AuthURL}})
|
jsonBytes, err := json.Marshal(csrMessage{Resource: "new-cert", Csr: csrString, Authorizations: []string{authz.AuthURL}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
errc <- err
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := c.jws.post(authz.NewCertURL, jsonBytes)
|
resp, err := c.jws.post(authz.NewCertURL, jsonBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
errc <- err
|
||||||
}
|
return
|
||||||
|
|
||||||
if resp.Header.Get("Content-Type") != "application/pkix-cert" {
|
|
||||||
return nil, fmt.Errorf("The server returned an unexpected content-type header: %s - expected %s", resp.Header.Get("Content-Type"), "application/pkix-cert")
|
|
||||||
}
|
|
||||||
|
|
||||||
cert, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
privateKeyPem := pemEncode(privKey)
|
privateKeyPem := pemEncode(privKey)
|
||||||
|
cerRes := CertificateResource{
|
||||||
|
Domain: authz.Domain,
|
||||||
|
CertURL: resp.Header.Get("Location"),
|
||||||
|
PrivateKey: privateKeyPem}
|
||||||
|
|
||||||
certs = append(certs, CertificateResource{Domain: authz.Domain, CertURL: resp.Header.Get("Location"), PrivateKey: privateKeyPem, Certificate: cert})
|
for {
|
||||||
|
|
||||||
|
switch resp.StatusCode {
|
||||||
|
case 202:
|
||||||
|
case 201:
|
||||||
|
|
||||||
|
cert, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
errc <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// The server returns a body with a length of zero if the
|
||||||
|
// certificate was not ready at the time this request completed.
|
||||||
|
// Otherwise the body is the certificate.
|
||||||
|
if len(cert) > 0 {
|
||||||
|
cerRes.CertStableURL = resp.Header.Get("Content-Location")
|
||||||
|
cerRes.Certificate = cert
|
||||||
|
result <- cerRes
|
||||||
|
} else {
|
||||||
|
// The certificate was granted but is not yet issued.
|
||||||
|
// Check retry-after and loop.
|
||||||
|
ra := resp.Header.Get("Retry-After")
|
||||||
|
retryAfter, err := strconv.Atoi(ra)
|
||||||
|
if err != nil {
|
||||||
|
errc <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger().Printf("[%s] Server responded with status 202. Respecting retry-after of: %d", authz.Domain, retryAfter)
|
||||||
|
time.Sleep(time.Duration(retryAfter) * time.Millisecond)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
logger().Fatalf("[%s] The server returned an unexpected status code %d.", authz.Domain, resp.StatusCode)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = http.Get(cerRes.CertURL)
|
||||||
|
if err != nil {
|
||||||
|
errc <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return certs, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func logResponseHeaders(resp *http.Response) {
|
func logResponseHeaders(resp *http.Response) {
|
||||||
|
|
|
@ -66,7 +66,7 @@ type challenge struct {
|
||||||
Status string `json:"status,omitempty"`
|
Status string `json:"status,omitempty"`
|
||||||
URI string `json:"uri,omitempty"`
|
URI string `json:"uri,omitempty"`
|
||||||
Token string `json:"token,omitempty"`
|
Token string `json:"token,omitempty"`
|
||||||
Tls bool `json:"tls,omitempty"`
|
TLS bool `json:"tls,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type csrMessage struct {
|
type csrMessage struct {
|
||||||
|
@ -86,6 +86,7 @@ type revokeCertMessage struct {
|
||||||
type CertificateResource struct {
|
type CertificateResource struct {
|
||||||
Domain string
|
Domain string
|
||||||
CertURL string
|
CertURL string
|
||||||
|
CertStableURL string
|
||||||
PrivateKey []byte
|
PrivateKey []byte
|
||||||
Certificate []byte
|
Certificate []byte
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,17 +147,17 @@ func (s *simpleHTTPChallenge) startHTTPSServer(domain string, token string) (net
|
||||||
return nil, fmt.Errorf("Could not start HTTP listener! -> %v", err)
|
return nil, fmt.Errorf("Could not start HTTP listener! -> %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonBytes, err := json.Marshal(challenge{Type: "simpleHttp", Token: token, Tls: true})
|
jsonBytes, err := json.Marshal(challenge{Type: "simpleHttp", Token: token, TLS: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("startHTTPSServer: Failed to marshal network message...")
|
return nil, errors.New("startHTTPSServer: Failed to marshal network message")
|
||||||
}
|
}
|
||||||
signed, err := s.jws.signContent(jsonBytes)
|
signed, err := s.jws.signContent(jsonBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("startHTTPSServer: Failed to sign message...")
|
return nil, errors.New("startHTTPSServer: Failed to sign message")
|
||||||
}
|
}
|
||||||
signedCompact := signed.FullSerialize()
|
signedCompact := signed.FullSerialize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("startHTTPSServer: Failed to serialize message...")
|
return nil, errors.New("startHTTPSServer: Failed to serialize message")
|
||||||
}
|
}
|
||||||
|
|
||||||
// The handler validates the HOST header and request type.
|
// The handler validates the HOST header and request type.
|
||||||
|
|
Loading…
Reference in a new issue