Generate and store crl files regularly; set crl URL in certs

Tweak the new crl-storer to save the crl files locally instead of in S3,
with some housekeeping to keep only the last five versions.
This commit is contained in:
Arjan H
2022-08-17 20:36:39 +02:00
parent 616da91583
commit 5c3380bf0f
8 changed files with 163 additions and 11 deletions

View File

@@ -64,9 +64,13 @@ if [ "$PKI_DOMAIN_MODE" == "lockdown" ] || [ "$PKI_DOMAIN_MODE" == "whitelist" ]
cat rate-limit-policies.yml | tr '\n' '\r' | sed -e "s|\(certificatesPerFQDNSet:.*must-staple.le.wtf: 10000\).*\(certificatesPerFQDNSetFast:.*\)|\1\n${REPLACEMENT}rateLimitsURL: http://$PKI_FQDN/rate-limits\n\2|" | tr '\r' '\n' > rate-limit-policies.yml.bak && mv rate-limit-policies.yml.bak rate-limit-policies.yml
fi
perl -i -p0e "s/(\"maxNames\": [^\n]*).*?(\s+)(\"lifespanOCSP\":)/\1\2\"lifespanCRL\": \"2h\",\2\3/igs" config/ca-a.json
perl -i -p0e "s/(\"maxNames\": [^\n]*).*?(\s+)(\"lifespanOCSP\":)/\1\2\"lifespanCRL\": \"2h\",\2\3/igs" config/ca-b.json
sed -i -e "s|\"issuerURL\": \".*\"|\"issuerURL\": \"http://$PKI_FQDN/aia/issuer/$PKI_ISSUER_NAME_ID\"|" config/ca-a.json
sed -i -e "s|\"issuerURL\": \".*\"|\"issuerURL\": \"http://$PKI_FQDN/aia/issuer/$PKI_ISSUER_NAME_ID\"|" config/ca-b.json
sed -i -e "s|\"crlURL\": \".*\"|\"crlURL\": \"http://$PKI_FQDN/crl/$PKI_ISSUER_NAME_ID.crl\"|" config/ca-a.json
sed -i -e "s|\"crlURL\": \".*\"|\"crlURL\": \"http://$PKI_FQDN/crl/$PKI_ISSUER_NAME_ID.crl\"|" config/ca-b.json
if [ "$PKI_EXTENDED_TIMEOUT" == "1" ]; then
sed -i -e "s/\"timeout\": \"15s\"/\"timeout\": \"30s\"/" config/ca-a.json

View File

@@ -663,8 +663,6 @@ config_boulder() {
sed -i -e "s|http://example.com/cps|http://$LABCA_FQDN/cps/|g" config/ca-b.json
sed -i -e "s|1.2.3.4|1.3.6.1.4.1.44947.1.1.1|g" config/ca-a.json
sed -i -e "s|1.2.3.4|1.3.6.1.4.1.44947.1.1.1|g" config/ca-b.json
perl -i -p0e "s/(\s+\"crlURL\":[^\n]*)//igs" config/ca-a.json
perl -i -p0e "s/(\s+\"crlURL\":[^\n]*)//igs" config/ca-b.json
sed -i -e "s/ocspURL.Path = encodedReq/ocspURL.Path += encodedReq/" ocsp/helper/helper.go
sed -i -e "s/\"dnsTimeout\": \".*\"/\"dnsTimeout\": \"3s\"/" config/ra.json
sed -i -e "s/\"dnsTimeout\": \".*\"/\"dnsTimeout\": \"3s\"/" config/va.json
@@ -695,7 +693,7 @@ config_boulder() {
export PKI_DOMAIN_MODE=$(grep domain_mode $adminDir/data/config.json | sed -e 's/.*:[ ]*//' | sed -e 's/\",//g' | sed -e 's/\"//g')
export PKI_LOCKDOWN_DOMAINS=$(grep lockdown $adminDir/data/config.json | grep -v domain_mode | sed -e 's/.*:[ ]*//' | sed -e 's/\",//g' | sed -e 's/\"//g')
export PKI_WHITELIST_DOMAINS=$(grep whitelist $adminDir/data/config.json | grep -v domain_mode | sed -e 's/.*:[ ]*//' | sed -e 's/\",//g' | sed -e 's/\"//g')
export PKI_ISSUER_NAME_ID=$(grep issuer_name_id $adminDir/data/config.json | sed -e 's/.*:[ ]*//' | sed -e 's/\",//g' | sed -e 's/\"//g')
export PKI_ISSUER_NAME_ID=$(grep issuer_name_id $adminDir/data/config.json | sed -e 's/.*:[ ]*//' | sed -e 's/,//g' | sed -e 's/\"//g')
if [ -z "$PKI_ISSUER_NAME_ID" ] && [ -e "$adminDir/data/issuer/ca-int.pem" ]; then
local img=$(grep "&boulder_image" $boulderDir/docker-compose.yml | sed -e "s/.*boulder_image \(.*\)/\1/")
eval img=$img

View File

@@ -31,6 +31,8 @@ $SUDO patch -p1 < $cloneDir/patches/startservers.patch
$SUDO patch -p1 < $cloneDir/patches/errors_errors.patch
$SUDO patch -p1 < $cloneDir/patches/ratelimit_rate-limits.patch
$SUDO patch -p1 < $cloneDir/patches/linter_linter.patch
$SUDO patch -p1 < $cloneDir/patches/crl-storer_main.patch
$SUDO patch -p1 < $cloneDir/patches/storer_storer.patch
sed -i -e "s/berrors.RateLimitError(/berrors.RateLimitError(ra.rlPolicies.RateLimitsURL(), /g" ra/ra.go

View File

@@ -1,8 +1,8 @@
diff --git a/test/config/crl-storer.json b/test/config/crl-storer.json
index 61f14d795..4896803c4 100644
index 61f14d79..a620896f 100644
--- a/test/config/crl-storer.json
+++ b/test/config/crl-storer.json
@@ -15,9 +15,7 @@
@@ -15,10 +15,9 @@
]
},
"issuerCerts": [
@@ -11,5 +11,7 @@ index 61f14d795..4896803c4 100644
- "/hierarchy/intermediate-cert-ecdsa-a.pem"
+ "/hierarchy/intermediate-cert-rsa-a.pem"
],
+ "localStorePath": "/wwwstatic/crl",
"s3Endpoint": "http://localhost:7890",
"s3Region": "us-west-1",
"s3Bucket": "lets-encrypt-crls",

View File

@@ -1,10 +1,15 @@
diff --git a/test/config/crl-updater.json b/test/config/crl-updater.json
index 02a57b25..a5752dec 100644
index 875fc7ab..9c0ea6ed 100644
--- a/test/config/crl-updater.json
+++ b/test/config/crl-updater.json
@@ -15,15 +15,13 @@
@@ -14,16 +14,18 @@
"serverAddress": "ca.boulder:9106",
"timeout": "15s"
},
+ "crlStorerService": {
+ "serverAddress": "crl-storer.boulder:9109",
+ "timeout": "15s"
+ },
"issuerCerts": [
- "/hierarchy/intermediate-cert-rsa-a.pem",
- "/hierarchy/intermediate-cert-rsa-b.pem",
@@ -14,10 +19,12 @@ index 02a57b25..a5752dec 100644
- "numShards": 10,
+ "numShards": 1,
"certificateLifetime": "2160h",
"updatePeriod": "6h",
"updateOffset": "9120s",
- "updatePeriod": "6h",
- "updateOffset": "9120s",
- "maxParallelism": 10
+ "updatePeriod": "1h",
+ "updateOffset": "120s",
+ "maxParallelism": 1
},
"syslog": {

View File

@@ -0,0 +1,23 @@
diff --git a/cmd/crl-storer/main.go b/cmd/crl-storer/main.go
index 74d525c4..6a91ddab 100644
--- a/cmd/crl-storer/main.go
+++ b/cmd/crl-storer/main.go
@@ -47,6 +47,9 @@ type Config struct {
// https://docs.aws.amazon.com/sdkref/latest/guide/file-format.html.
S3CredsFile string
+ // If this is set, store the files locally instead of using (fake) S3
+ LocalStorePath string
+
Features map[string]bool
}
@@ -129,7 +132,7 @@ func main() {
}
s3client := s3.NewFromConfig(awsConfig, s3opts...)
- csi, err := storer.New(issuers, s3client, c.CRLStorer.S3Bucket, scope, logger, clk)
+ csi, err := storer.New(issuers, s3client, c.CRLStorer.S3Bucket, c.CRLStorer.LocalStorePath, scope, logger, clk)
cmd.FailOnError(err, "Failed to create CRLStorer impl")
serverMetrics := bgrpc.NewServerMetrics(scope)

View File

@@ -11,11 +11,12 @@ index 0cd8c8dd..36619190 100644
GOFLAGS: -mod=vendor
# Go 1.18 turns off SHA-1 validation on CSRs (and certs, but that doesn't
# affect us). It also turns off TLS 1.0 and TLS 1.1. Temporarily go back
@@ -17,6 +17,7 @@ services:
@@ -17,6 +17,8 @@ services:
GODEBUG: x509sha1=1,tls10default=1
volumes:
- .:/boulder:cached
+ - /home/labca/boulder_labca:/boulder/labca
+ - /home/labca/nginx_data/static:/wwwstatic
- ./.gocache:/root/.cache/go-build:cached
- ./.hierarchy:/hierarchy/:cached
- ./.softhsm-tokens/:/var/lib/softhsm/tokens/:cached

115
patches/storer_storer.patch Normal file
View File

@@ -0,0 +1,115 @@
diff --git a/crl/storer/storer.go b/crl/storer/storer.go
index b2514eb4..77955b0c 100644
--- a/crl/storer/storer.go
+++ b/crl/storer/storer.go
@@ -9,6 +9,9 @@ import (
"fmt"
"io"
"math/big"
+ "os"
+ "path/filepath"
+ "sort"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/aws/aws-sdk-go-v2/service/s3/types"
@@ -32,6 +35,7 @@ type crlStorer struct {
cspb.UnimplementedCRLStorerServer
s3Client s3Putter
s3Bucket string
+ localStorePath string
issuers map[issuance.IssuerNameID]*issuance.Certificate
uploadCount *prometheus.CounterVec
sizeHistogram *prometheus.HistogramVec
@@ -44,6 +48,7 @@ func New(
issuers []*issuance.Certificate,
s3Client s3Putter,
s3Bucket string,
+ localStorePath string,
stats prometheus.Registerer,
log blog.Logger,
clk clock.Clock,
@@ -77,6 +82,7 @@ func New(
issuers: issuersByNameID,
s3Client: s3Client,
s3Bucket: s3Bucket,
+ localStorePath: localStorePath,
uploadCount: uploadCount,
sizeHistogram: sizeHistogram,
latencyHistogram: latencyHistogram,
@@ -153,15 +159,19 @@ func (cs *crlStorer) UploadCRL(stream cspb.CRLStorer_UploadCRLServer) error {
checksum := sha256.Sum256(crlBytes)
checksumb64 := base64.StdEncoding.EncodeToString(checksum[:])
crlContentType := "application/pkix-crl"
- _, err = cs.s3Client.PutObject(stream.Context(), &s3.PutObjectInput{
- Bucket: &cs.s3Bucket,
- Key: &filename,
- Body: bytes.NewReader(crlBytes),
- ChecksumAlgorithm: types.ChecksumAlgorithmSha256,
- ChecksumSHA256: &checksumb64,
- ContentType: &crlContentType,
- Metadata: map[string]string{"crlNumber": crlNumber.String()},
- })
+ if cs.localStorePath == "" {
+ _, err = cs.s3Client.PutObject(stream.Context(), &s3.PutObjectInput{
+ Bucket: &cs.s3Bucket,
+ Key: &filename,
+ Body: bytes.NewReader(crlBytes),
+ ChecksumAlgorithm: types.ChecksumAlgorithmSha256,
+ ChecksumSHA256: &checksumb64,
+ ContentType: &crlContentType,
+ Metadata: map[string]string{"crlNumber": crlNumber.String()},
+ })
+ } else {
+ err = storeLocalFile(cs.localStorePath, issuer.NameID(), crlNumber, shardIdx, bytes.NewReader(crlBytes))
+ }
if err != nil {
cs.uploadCount.WithLabelValues(issuer.Subject.CommonName, "failed")
cs.log.AuditErrf(
@@ -186,3 +196,46 @@ func (cs *crlStorer) UploadCRL(stream cspb.CRLStorer_UploadCRLServer) error {
return stream.SendAndClose(&emptypb.Empty{})
}
+
+func storeLocalFile(path string, nameID issuance.IssuerNameID, crlNumber *big.Int, shardIdx int64, crlBytes io.Reader) error {
+ // Write the file
+ fn := fmt.Sprintf("%s%c%d-%d-%d.crl", path, os.PathSeparator, nameID, crlNumber, shardIdx)
+ out, err := os.Create(fn)
+ defer out.Close()
+ if err != nil {
+ return fmt.Errorf("creating local crl file: %w", err)
+ }
+
+ _, err = io.Copy(out, crlBytes)
+ if err != nil {
+ return fmt.Errorf("storing crl locally: %w", err)
+ }
+
+ // Create/update the symlink
+ ln := fmt.Sprintf("%s%c%d.crl", path, os.PathSeparator, nameID)
+ lntmp := ln + ".new"
+ fn = fmt.Sprintf("%d-%d-%d.crl", nameID, crlNumber, shardIdx)
+
+ if err = os.Remove(lntmp); err != nil && !os.IsNotExist(err) {
+ return fmt.Errorf("removing symlink: %w", err)
+ }
+ if err = os.Symlink(fn, lntmp); err != nil {
+ return fmt.Errorf("creating symlink: %w", err)
+ }
+ if err = os.Rename(lntmp, ln); err != nil {
+ return fmt.Errorf("renaming symlink: %w", err)
+ }
+
+ // Remove old files (keep last five)
+ pt := fmt.Sprintf("%s%c%d-*-%d.crl", path, os.PathSeparator, nameID, shardIdx)
+ matches, err := filepath.Glob(pt)
+ sort.Strings(matches)
+ for i := 0; i < len(matches)-5; i++ {
+ err := os.Remove(matches[i])
+ if err != nil {
+ return fmt.Errorf("deleting file %s: %w", matches[i], err)
+ }
+ }
+
+ return nil
+}
\ No newline at end of file