load-generator: Add revocation-by-account support (#4237)
Adds RFC 8555 revocation authorized using the issuing account.
This commit is contained in:
parent
c0246b3d97
commit
dc11681faa
|
|
@ -12,6 +12,7 @@ import (
|
|||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
|
@ -22,8 +23,8 @@ import (
|
|||
"github.com/letsencrypt/boulder/core"
|
||||
"github.com/letsencrypt/boulder/identifier"
|
||||
"github.com/letsencrypt/boulder/probs"
|
||||
"github.com/letsencrypt/boulder/revocation"
|
||||
"github.com/letsencrypt/boulder/test/load-generator/acme"
|
||||
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
)
|
||||
|
||||
|
|
@ -31,11 +32,12 @@ var (
|
|||
// stringToOperation maps a configured plan action to a function that can
|
||||
// operate on a state/context.
|
||||
stringToOperation = map[string]func(*State, *context) error{
|
||||
"newAccount": newAccount,
|
||||
"getAccount": getAccount,
|
||||
"newOrder": newOrder,
|
||||
"fulfillOrder": fulfillOrder,
|
||||
"finalizeOrder": finalizeOrder,
|
||||
"newAccount": newAccount,
|
||||
"getAccount": getAccount,
|
||||
"newOrder": newOrder,
|
||||
"fulfillOrder": fulfillOrder,
|
||||
"finalizeOrder": finalizeOrder,
|
||||
"revokeCertificate": revokeCertificate,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -582,3 +584,81 @@ func postAsGet(s *State, ctx *context, url string, latencyTag string) (*http.Res
|
|||
|
||||
return s.post(url, requestPayload, ctx.ns, latencyTag, http.StatusOK)
|
||||
}
|
||||
|
||||
func popCertificate(ctx *context) string {
|
||||
certIndex := mrand.Intn(len(ctx.certs))
|
||||
certURL := ctx.certs[certIndex]
|
||||
ctx.certs = append(ctx.certs[:certIndex], ctx.certs[certIndex+1:]...)
|
||||
return certURL
|
||||
}
|
||||
|
||||
func getCert(s *State, ctx *context, url string) ([]byte, error) {
|
||||
latencyTag := "/acme/cert/{serial}"
|
||||
resp, err := postAsGet(s, ctx, url, latencyTag)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s bad response: %s", url, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return ioutil.ReadAll(resp.Body)
|
||||
}
|
||||
|
||||
// revokeCertificate removes a certificate url from the context, retrieves it,
|
||||
// and sends a revocation request for the certificate to the ACME server.
|
||||
// The revocation request is signed with the account key rather than the certificate
|
||||
// key.
|
||||
func revokeCertificate(s *State, ctx *context) error {
|
||||
if len(ctx.certs) < 1 {
|
||||
return errors.New("No certificates in the context that can be revoked")
|
||||
}
|
||||
|
||||
if r := mrand.Float32(); r > s.revokeChance {
|
||||
return nil
|
||||
}
|
||||
|
||||
certURL := popCertificate(ctx)
|
||||
certPEM, err := getCert(s, ctx, certURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pemBlock, _ := pem.Decode(certPEM)
|
||||
revokeObj := struct {
|
||||
Certificate string
|
||||
Reason int
|
||||
}{
|
||||
Certificate: base64.URLEncoding.EncodeToString(pemBlock.Bytes),
|
||||
Reason: revocation.Unspecified,
|
||||
}
|
||||
|
||||
revokeJSON, err := json.Marshal(revokeObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
revokeURL := s.directory.EndpointURL(acme.RevokeCertEndpoint)
|
||||
// TODO(roland): randomly use the certificate key to sign the request instead of
|
||||
// the account key
|
||||
jws, err := ctx.signKeyIDV2Request(revokeJSON, revokeURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
requestPayload := []byte(jws.FullSerialize())
|
||||
|
||||
resp, err := s.post(
|
||||
revokeURL,
|
||||
requestPayload,
|
||||
ctx.ns,
|
||||
"/acme/revoke-cert",
|
||||
http.StatusOK,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
_, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
"newAccount",
|
||||
"newOrder",
|
||||
"fulfillOrder",
|
||||
"finalizeOrder"
|
||||
"finalizeOrder",
|
||||
"revokeCertificate"
|
||||
],
|
||||
"rate": 1,
|
||||
"runtime": "10s",
|
||||
|
|
@ -22,5 +23,6 @@
|
|||
"regEmail": "loadtesting@letsencrypt.org",
|
||||
"maxRegs": 20,
|
||||
"maxNamesPerCert": 20,
|
||||
"dontSaveState": true
|
||||
"dontSaveState": true,
|
||||
"revokeChance": 0.5
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ type Config struct {
|
|||
MaxRegs int // maximum number of registrations to create
|
||||
MaxNamesPerCert int // maximum number of names on one certificate/order
|
||||
ChallengeStrategy string // challenge selection strategy ("random", "http-01", "dns-01", "tls-alpn-01")
|
||||
RevokeChance float32 // chance of revoking certificate after issuance, between 0.0 and 1.0
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
|
@ -87,6 +88,7 @@ func main() {
|
|||
config.RegEmail,
|
||||
config.Plan.Actions,
|
||||
config.ChallengeStrategy,
|
||||
config.RevokeChance,
|
||||
)
|
||||
cmd.FailOnError(err, "Failed to create load generator")
|
||||
|
||||
|
|
|
|||
|
|
@ -188,6 +188,8 @@ type State struct {
|
|||
challStrat acme.ChallengeStrategy
|
||||
httpClient *http.Client
|
||||
|
||||
revokeChance float32
|
||||
|
||||
reqTotal int64
|
||||
respCodes map[int]*respCode
|
||||
cMu sync.Mutex
|
||||
|
|
@ -288,7 +290,8 @@ func New(
|
|||
latencyPath string,
|
||||
userEmail string,
|
||||
operations []string,
|
||||
challStrat string) (*State, error) {
|
||||
challStrat string,
|
||||
revokeChance float32) (*State, error) {
|
||||
certKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -301,6 +304,9 @@ func New(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if revokeChance > 1 {
|
||||
return nil, errors.New("revokeChance must be between 0.0 and 1.0")
|
||||
}
|
||||
httpClient := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
DialContext: (&net.Dialer{
|
||||
|
|
@ -333,6 +339,7 @@ func New(
|
|||
maxNamesPerCert: maxNamesPerCert,
|
||||
email: userEmail,
|
||||
respCodes: make(map[int]*respCode),
|
||||
revokeChance: revokeChance,
|
||||
}
|
||||
|
||||
// convert operations strings to methods
|
||||
|
|
|
|||
Loading…
Reference in New Issue