157 lines
3.9 KiB
Go
157 lines
3.9 KiB
Go
// +build integration
|
|
|
|
package integration
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
|
|
"github.com/eggsampler/acme/v2"
|
|
)
|
|
|
|
func random_domain() string {
|
|
var bytes [3]byte
|
|
rand.Read(bytes[:])
|
|
return hex.EncodeToString(bytes[:]) + ".com"
|
|
}
|
|
|
|
type client struct {
|
|
acme.Account
|
|
acme.Client
|
|
}
|
|
|
|
func makeClient() (*client, error) {
|
|
c, err := acme.NewClient(os.Getenv("DIRECTORY"))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Error connecting to acme directory: %v", err)
|
|
}
|
|
privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error creating private key: %v", err)
|
|
}
|
|
account, err := c.NewAccount(privKey, false, true, "mailto:example@letsencrypt.org")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error creating new account: %v", err)
|
|
}
|
|
return &client{account, c}, nil
|
|
}
|
|
|
|
func addHTTP01Response(token, keyAuthorization string) error {
|
|
resp, err := http.Post("http://boulder:8055/add-http01", "",
|
|
bytes.NewBufferString(fmt.Sprintf(`{
|
|
"token": "%s",
|
|
"content": "%s"
|
|
}`, token, keyAuthorization)))
|
|
if err != nil {
|
|
return fmt.Errorf("adding http-01 response: %s", err)
|
|
}
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("adding http-01 response: status %d", resp.StatusCode)
|
|
}
|
|
resp.Body.Close()
|
|
return nil
|
|
}
|
|
|
|
func delHTTP01Response(token string) error {
|
|
resp, err := http.Post("http://boulder:8055/del-http01", "",
|
|
bytes.NewBufferString(fmt.Sprintf(`{
|
|
"token": "%s"
|
|
}`, token)))
|
|
if err != nil {
|
|
return fmt.Errorf("deleting http-01 response: %s", err)
|
|
}
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("deleting http-01 response: status %d", resp.StatusCode)
|
|
}
|
|
resp.Body.Close()
|
|
return nil
|
|
}
|
|
|
|
type issuanceResult struct {
|
|
acme.Order
|
|
certs []*x509.Certificate
|
|
}
|
|
|
|
func authAndIssue(domains []string) (*issuanceResult, error) {
|
|
c, err := makeClient()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var ids []acme.Identifier
|
|
for _, domain := range domains {
|
|
ids = append(ids, acme.Identifier{Type: "dns", Value: domain})
|
|
}
|
|
order, err := c.Client.NewOrder(c.Account, ids)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("making order: %s", err)
|
|
}
|
|
|
|
for _, authUrl := range order.Authorizations {
|
|
auth, err := c.Client.FetchAuthorization(c.Account, authUrl)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("fetching authorization at %s: %s", authUrl, err)
|
|
}
|
|
|
|
chal, ok := auth.ChallengeMap[acme.ChallengeTypeHTTP01]
|
|
if !ok {
|
|
return nil, fmt.Errorf("no HTTP challenge at %s", authUrl)
|
|
}
|
|
|
|
err = addHTTP01Response(chal.Token, chal.KeyAuthorization)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("adding HTTP-01 response: %s", err)
|
|
}
|
|
defer delHTTP01Response(chal.Token)
|
|
chal, err = c.Client.UpdateChallenge(c.Account, chal)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("updating challenge: %s", err)
|
|
}
|
|
}
|
|
|
|
csr, err := makeCSR(domains)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
order, err = c.Client.FinalizeOrder(c.Account, order, csr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("finalizing order: %s", err)
|
|
}
|
|
certs, err := c.Client.FetchCertificates(c.Account, order.Certificate)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("fetching certificates: %s", err)
|
|
}
|
|
return &issuanceResult{order, certs}, nil
|
|
}
|
|
|
|
func makeCSR(domains []string) (*x509.CertificateRequest, error) {
|
|
certKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("generating certificate key: %s", err)
|
|
}
|
|
|
|
csrDer, err := x509.CreateCertificateRequest(rand.Reader, &x509.CertificateRequest{
|
|
SignatureAlgorithm: x509.ECDSAWithSHA256,
|
|
PublicKeyAlgorithm: x509.ECDSA,
|
|
PublicKey: certKey.Public(),
|
|
Subject: pkix.Name{CommonName: domains[0]},
|
|
DNSNames: domains,
|
|
}, certKey)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("making csr: %s", err)
|
|
}
|
|
csr, err := x509.ParseCertificateRequest(csrDer)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parsing csr: %s", err)
|
|
}
|
|
return csr, nil
|
|
}
|