mirror of
https://github.com/outbackdingo/labca.git
synced 2026-01-27 10:19:34 +00:00
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:
@@ -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
|
||||
|
||||
4
install
4
install
@@ -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
|
||||
|
||||
2
patch.sh
2
patch.sh
@@ -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
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": {
|
||||
|
||||
23
patches/crl-storer_main.patch
Normal file
23
patches/crl-storer_main.patch
Normal 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)
|
||||
@@ -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
115
patches/storer_storer.patch
Normal 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
|
||||
Reference in New Issue
Block a user