forked from letsencrypt/boulder
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
149 lines (125 loc) · 3.72 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package notmain
import (
"crypto/x509"
"encoding/json"
"flag"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"time"
"github.com/letsencrypt/boulder/cmd"
"github.com/letsencrypt/boulder/core"
"github.com/letsencrypt/boulder/crl/checker"
)
func downloadShard(url string) (*x509.RevocationList, error) {
resp, err := http.Get(url)
if err != nil {
return nil, fmt.Errorf("downloading crl: %w", err)
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("downloading crl: http status %d", resp.StatusCode)
}
crlBytes, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("reading CRL bytes: %w", err)
}
crl, err := x509.ParseRevocationList(crlBytes)
if err != nil {
return nil, fmt.Errorf("parsing CRL: %w", err)
}
return crl, nil
}
func main() {
urlFile := flag.String("crls", "", "path to a file containing a JSON Array of CRL URLs")
issuerFile := flag.String("issuer", "", "path to an issuer certificate on disk, required, '-' to disable validation")
ageLimitStr := flag.String("ageLimit", "168h", "maximum allowable age of a CRL shard")
emitRevoked := flag.Bool("emitRevoked", false, "emit revoked serial numbers on stdout, one per line, hex-encoded")
save := flag.Bool("save", false, "save CRLs to files named after the URL")
flag.Parse()
logger := cmd.NewLogger(cmd.SyslogConfig{StdoutLevel: 6, SyslogLevel: -1})
logger.Info(cmd.VersionString())
urlFileContents, err := os.ReadFile(*urlFile)
cmd.FailOnError(err, "Reading CRL URLs file")
var urls []string
err = json.Unmarshal(urlFileContents, &urls)
cmd.FailOnError(err, "Parsing JSON Array of CRL URLs")
if *issuerFile == "" {
cmd.Fail("-issuer is required, but may be '-' to disable validation")
}
var issuer *x509.Certificate
if *issuerFile != "-" {
issuer, err = core.LoadCert(*issuerFile)
cmd.FailOnError(err, "Loading issuer certificate")
} else {
logger.Warning("CRL signature validation disabled")
}
ageLimit, err := time.ParseDuration(*ageLimitStr)
cmd.FailOnError(err, "Parsing age limit")
errCount := 0
seenSerials := make(map[string]struct{})
totalBytes := 0
oldestTimestamp := time.Time{}
for _, u := range urls {
crl, err := downloadShard(u)
if err != nil {
errCount += 1
logger.Errf("fetching CRL %q failed: %s", u, err)
continue
}
if *save {
parsedURL, err := url.Parse(u)
if err != nil {
logger.Errf("parsing url: %s", err)
continue
}
filename := fmt.Sprintf("%s%s", parsedURL.Host, strings.ReplaceAll(parsedURL.Path, "/", "_"))
err = os.WriteFile(filename, crl.Raw, 0660)
if err != nil {
logger.Errf("writing file: %s", err)
continue
}
}
totalBytes += len(crl.Raw)
zcrl, err := x509.ParseRevocationList(crl.Raw)
if err != nil {
errCount += 1
logger.Errf("parsing CRL %q failed: %s", u, err)
continue
}
err = checker.Validate(zcrl, issuer, ageLimit)
if err != nil {
errCount += 1
logger.Errf("checking CRL %q failed: %s", u, err)
continue
}
if oldestTimestamp.IsZero() || crl.ThisUpdate.Before(oldestTimestamp) {
oldestTimestamp = crl.ThisUpdate
}
for _, c := range crl.RevokedCertificateEntries {
serial := core.SerialToString(c.SerialNumber)
if _, seen := seenSerials[serial]; seen {
errCount += 1
logger.Errf("serial seen in multiple shards: %s", serial)
continue
}
seenSerials[serial] = struct{}{}
}
}
if *emitRevoked {
for serial := range seenSerials {
fmt.Println(serial)
}
}
if errCount != 0 {
cmd.Fail(fmt.Sprintf("Encountered %d errors", errCount))
}
logger.AuditInfof(
"Validated %d CRLs, %d serials, %d bytes. Oldest CRL: %s",
len(urls), len(seenSerials), totalBytes, oldestTimestamp.Format(time.RFC3339))
}
func init() {
cmd.RegisterCommand("crl-checker", main, nil)
}