Skip to content
This repository was archived by the owner on Jun 9, 2024. It is now read-only.

Commit 98a5d28

Browse files
taktOliver Geiselhardt-Herms
andauthored
Implement certificate transparency (#134)
Co-authored-by: Oliver Geiselhardt-Herms <[email protected]>
1 parent 181aa8e commit 98a5d28

File tree

2 files changed

+182
-2
lines changed

2 files changed

+182
-2
lines changed

cmd/octorpki/ct.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"flag"
7+
"fmt"
8+
"io"
9+
"net/http"
10+
"time"
11+
12+
"github.com/cloudflare/cfrpki/validator/pki"
13+
"github.com/opentracing/opentracing-go"
14+
15+
librpki "github.com/cloudflare/cfrpki/validator/lib"
16+
ct "github.com/google/certificate-transparency-go"
17+
log "github.com/sirupsen/logrus"
18+
)
19+
20+
var (
21+
// Certificate Transparency
22+
CertTransparency = flag.Bool("ct", false, "Enable Certificate Transparency")
23+
CertTransparencyAddr = flag.String("ct.addr", "https://ct.cloudflare.com/logs/cirrus", "Path of CT")
24+
CertTransparencyThreads = flag.Int("ct.threads", 50, "Threads to send to CT")
25+
CertTransparencyTimeout = flag.Int("ct.timeout", 50, "CT timeout in seconds")
26+
)
27+
28+
func SingleSendCertificateTransparency(httpclient *http.Client, path string, msg *ct.AddChainRequest) error {
29+
buf := bytes.NewBuffer([]byte{})
30+
enc := json.NewEncoder(buf)
31+
enc.Encode(msg)
32+
33+
resp, err := httpclient.Post(fmt.Sprintf("%v/ct/v1/add-chain", path), "application/json", buf)
34+
if err == nil {
35+
respStr, _ := io.ReadAll(resp.Body)
36+
log.Debugf("Sent %v certs %v %v %v", len(msg.Chain), path, string(respStr), err)
37+
}
38+
39+
return err
40+
}
41+
42+
func BatchCertificateTransparency(httpclient *http.Client, path string, d chan *ct.AddChainRequest) {
43+
log.Debugf("Starting BatchCertificateTransparency")
44+
45+
for msg := range d {
46+
err := SingleSendCertificateTransparency(httpclient, path, msg)
47+
if err != nil {
48+
log.Error(err)
49+
}
50+
}
51+
}
52+
53+
func (s *OctoRPKI) SendCertificateTransparency(pSpan opentracing.Span, ctData [][]*pki.PKIFile, threads int, timeout int) {
54+
tracer := opentracing.GlobalTracer()
55+
span := tracer.StartSpan(
56+
"ct",
57+
opentracing.ChildOf(pSpan.Context()),
58+
)
59+
defer span.Finish()
60+
61+
log.Infof("Sending Certificate Transparency (threads=%v)", threads)
62+
63+
httpclient := &http.Client{
64+
Timeout: time.Duration(timeout) * time.Second,
65+
}
66+
67+
dataChan := make(chan *ct.AddChainRequest)
68+
defer close(dataChan)
69+
70+
for i := 0; i < threads; i++ {
71+
go BatchCertificateTransparency(httpclient, s.CTPath, dataChan)
72+
}
73+
74+
var iterations int
75+
for _, certs := range ctData {
76+
chain := make([][]byte, 0)
77+
78+
for _, cert := range certs {
79+
var dataBytes []byte
80+
data, err := s.Fetcher.GetFile(cert)
81+
if cert.Type == pki.TYPE_ROA || cert.Type == pki.TYPE_MFT {
82+
cms, err := librpki.DecodeCMS(data.Data)
83+
if err != nil {
84+
log.Error(err)
85+
continue
86+
}
87+
dataBytes = cms.SignedData.Certificates.Bytes
88+
} else {
89+
dataBytes = data.Data
90+
}
91+
92+
if err != nil {
93+
log.Error(err)
94+
continue
95+
}
96+
97+
chain = append(chain, dataBytes)
98+
}
99+
100+
dataChan <- &ct.AddChainRequest{
101+
Chain: chain,
102+
}
103+
104+
iterations++
105+
if len(ctData) > 0 && len(ctData) >= 20 && iterations%(len(ctData)/20) == 0 {
106+
log.Infof("Sent %v/%v (%v percent) certificates chains to CT %v", iterations, len(ctData), iterations*100/len(ctData), s.CTPath)
107+
}
108+
}
109+
110+
log.Infof("Sent %v chains to Certificate Transparency %v", len(ctData), s.CTPath)
111+
}

cmd/octorpki/octorpki.go

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,9 @@ type OctoRPKI struct {
236236

237237
Resources *schemas.ResourcesJSON
238238
ResourcesMu sync.RWMutex
239+
240+
DoCT bool
241+
CTPath string
239242
}
240243

241244
func (s *OctoRPKI) getRRDPFetch() map[string]string {
@@ -1025,7 +1028,7 @@ func (s *OctoRPKI) signROAList(roaList *prefixfile.ROAList, span opentracing.Spa
10251028
roaList.Metadata.SignatureDate = signdate
10261029
}
10271030

1028-
func (s *OctoRPKI) mainValidation(pSpan opentracing.Span) {
1031+
func (s *OctoRPKI) mainValidation(pSpan opentracing.Span) [][]*pki.PKIFile {
10291032
t1 := time.Now()
10301033
ia := make([][]SIA, len(s.Tals))
10311034
for i := 0; i < len(ia); i++ {
@@ -1036,6 +1039,8 @@ func (s *OctoRPKI) mainValidation(pSpan opentracing.Span) {
10361039
span := s.tracer.StartSpan("validation", opentracing.ChildOf(pSpan.Context()))
10371040
defer span.Finish()
10381041

1042+
ctData := make([][]*pki.PKIFile, 0)
1043+
10391044
pkiManagers := make([]*pki.SimpleManager, len(s.Tals))
10401045
for i, tal := range s.Tals {
10411046
tSpan := s.tracer.StartSpan("explore", opentracing.ChildOf(span.Context()))
@@ -1113,6 +1118,10 @@ func (s *OctoRPKI) mainValidation(pSpan opentracing.Span) {
11131118
sm.Close()
11141119
tSpan.LogKV("count-valid", count, "count-total", countExplore)
11151120
tSpan.Finish()
1121+
1122+
if s.DoCT {
1123+
ctData = append(ctData, s.ct(pkiManagers, i)...)
1124+
}
11161125
}
11171126

11181127
s.setInfoAuthorities(ia)
@@ -1122,6 +1131,46 @@ func (s *OctoRPKI) mainValidation(pSpan opentracing.Span) {
11221131
s.stats.ValidationDuration = t2.Sub(t1)
11231132
MetricOperationTime.With(prometheus.Labels{"type": "validation"}).Observe(float64(s.stats.ValidationDuration.Seconds()))
11241133
MetricLastValidation.Set(float64(s.LastComputed.Unix()))
1134+
1135+
return ctData
1136+
}
1137+
1138+
func (s *OctoRPKI) ct(pkiManagers []*pki.SimpleManager, i int) [][]*pki.PKIFile {
1139+
skiToAki := make(map[string]string)
1140+
skiToPath := make(map[string]*pki.PKIFile)
1141+
for _, obj := range pkiManagers[i].Validator.ValidObjects {
1142+
res := obj.Resource.(*librpki.RPKICertificate)
1143+
ski := hex.EncodeToString(res.Certificate.SubjectKeyId)
1144+
aki := hex.EncodeToString(res.Certificate.AuthorityKeyId)
1145+
skiToAki[ski] = aki
1146+
skiToPath[ski] = obj.File
1147+
}
1148+
1149+
pathCT := make([][]*pki.PKIFile, 0)
1150+
for ski, aki := range skiToAki {
1151+
skiDone := make(map[string]bool)
1152+
skiDone[ski] = true
1153+
1154+
curAki := aki
1155+
curPath := skiToPath[ski]
1156+
curPathCT := make([]*pki.PKIFile, 1)
1157+
curPathCT[0] = curPath
1158+
1159+
var ok bool
1160+
for curAki != "" && !ok {
1161+
ok = skiDone[curAki]
1162+
skiDone[curAki] = true
1163+
1164+
curPath = skiToPath[curAki]
1165+
if curAki != "" {
1166+
curPathCT = append(curPathCT, curPath)
1167+
}
1168+
curAki = skiToAki[curAki]
1169+
}
1170+
pathCT = append(pathCT, curPathCT)
1171+
}
1172+
1173+
return pathCT
11251174
}
11261175

11271176
func (s *OctoRPKI) setInfoAuthorities(ia [][]SIA) {
@@ -1405,6 +1454,10 @@ func main() {
14051454
log.Fatalf("Mode %v is not specified. Choose either server or oneoff", *Mode)
14061455
}
14071456

1457+
if *CertTransparencyThreads < 1 {
1458+
*CertTransparencyThreads = 1
1459+
}
1460+
14081461
s.validationLoop()
14091462
}
14101463

@@ -1425,6 +1478,8 @@ func NewOctoRPKI(tals []*pki.PKIFile, talNames []string) *OctoRPKI {
14251478
stats: newOctoRPKIStats(),
14261479
InfoAuthorities: make([][]SIA, 0),
14271480
tracer: opentracing.GlobalTracer(),
1481+
DoCT: *CertTransparency,
1482+
CTPath: *CertTransparencyAddr,
14281483
}
14291484
}
14301485

@@ -1498,7 +1553,7 @@ func (s *OctoRPKI) validationLoop() {
14981553

14991554
s.mainRsync(span)
15001555

1501-
s.mainValidation(span)
1556+
ctData := s.mainValidation(span)
15021557

15031558
// Reduce
15041559
changed := s.MainReduce()
@@ -1524,6 +1579,20 @@ func (s *OctoRPKI) validationLoop() {
15241579
break
15251580
}
15261581

1582+
// Certificate Transparency
1583+
if s.DoCT && (s.Stable.Load() || !*WaitStable) {
1584+
t1 := time.Now().UTC()
1585+
1586+
s.SendCertificateTransparency(span, ctData, *CertTransparencyThreads, *CertTransparencyTimeout)
1587+
1588+
t2 := time.Now().UTC()
1589+
MetricOperationTime.With(
1590+
prometheus.Labels{
1591+
"type": "ct",
1592+
}).
1593+
Observe(float64(t2.Sub(t1).Seconds()))
1594+
}
1595+
15271596
if s.Stable.Load() {
15281597
MetricLastStableValidation.Set(float64(s.LastComputed.Unix()))
15291598
MetricState.Set(float64(1))

0 commit comments

Comments
 (0)