1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package submission
17
18 import (
19 "context"
20 "fmt"
21 "sync"
22 "time"
23
24 ct "github.com/google/certificate-transparency-go"
25 "github.com/google/certificate-transparency-go/asn1"
26 "github.com/google/certificate-transparency-go/ctpolicy"
27 "github.com/google/certificate-transparency-go/loglist3"
28 "github.com/google/certificate-transparency-go/schedule"
29 "github.com/google/certificate-transparency-go/tls"
30 "github.com/google/certificate-transparency-go/x509util"
31 "github.com/google/trillian/monitoring"
32 "k8s.io/klog/v2"
33 )
34
35
36 type CTPolicyType int
37
38
39 const (
40 ChromeCTPolicy CTPolicyType = iota
41 AppleCTPolicy
42 )
43
44 var (
45 proxyOnce sync.Once
46 logListUpdates monitoring.Counter
47 rspLatency monitoring.Histogram
48 )
49
50
51 func proxyInitMetrics(mf monitoring.MetricFactory) {
52 logListUpdates = mf.NewCounter("log_list_updates", "Number of Log-list updates")
53 rspLatency = mf.NewHistogram("http_latency", "Latency of policy-multiplexed add-responses in seconds", "ep")
54 }
55
56
57 type DistributorBuilder func(*loglist3.LogList) (*Distributor, error)
58
59
60
61 func GetDistributorBuilder(plc CTPolicyType, lcBuilder LogClientBuilder, mf monitoring.MetricFactory) DistributorBuilder {
62 if plc == AppleCTPolicy {
63 return func(ll *loglist3.LogList) (*Distributor, error) {
64 return NewDistributor(ll, ctpolicy.AppleCTPolicy{}, lcBuilder, mf)
65 }
66 }
67 return func(ll *loglist3.LogList) (*Distributor, error) {
68 return NewDistributor(ll, ctpolicy.ChromeCTPolicy{}, lcBuilder, mf)
69 }
70 }
71
72
73 func ASN1MarshalSCTs(scts []*AssignedSCT) ([]byte, error) {
74 if len(scts) == 0 {
75 return nil, fmt.Errorf("ASN1MarshalSCTs requires positive number of SCTs, 0 provided")
76 }
77 unassignedSCTs := make([]*ct.SignedCertificateTimestamp, 0, len(scts))
78 for _, sct := range scts {
79 unassignedSCTs = append(unassignedSCTs, sct.SCT)
80 }
81 sctList, err := x509util.MarshalSCTsIntoSCTList(unassignedSCTs)
82 if err != nil {
83 return nil, err
84 }
85 encdSCTList, err := tls.Marshal(*sctList)
86 if err != nil {
87 return nil, err
88 }
89 encoded, err := asn1.Marshal(encdSCTList)
90 if err != nil {
91 return nil, err
92 }
93 return encoded, nil
94 }
95
96
97 type Proxy struct {
98 Init chan bool
99
100 llRefreshInterval time.Duration
101 rootsRefreshInterval time.Duration
102
103 llWatcher *LogListManager
104 distributorBuilder DistributorBuilder
105
106 distMu sync.RWMutex
107 dist *Distributor
108 distCancel context.CancelFunc
109 }
110
111
112 func NewProxy(llm *LogListManager, db DistributorBuilder, mf monitoring.MetricFactory) *Proxy {
113 var p Proxy
114 p.llWatcher = llm
115 p.distributorBuilder = db
116 p.Init = make(chan bool, 1)
117 p.rootsRefreshInterval = 24 * time.Hour
118
119 if mf == nil {
120 mf = monitoring.InertMetricFactory{}
121 }
122 proxyOnce.Do(func() { proxyInitMetrics(mf) })
123
124 return &p
125 }
126
127
128
129
130 func (p *Proxy) Run(ctx context.Context, llRefresh time.Duration, rootsRefresh time.Duration) {
131 init := false
132 p.llRefreshInterval = llRefresh
133 p.rootsRefreshInterval = rootsRefresh
134 p.llWatcher.Run(ctx, llRefresh)
135
136 go func() {
137 for {
138 select {
139 case <-ctx.Done():
140 if !init {
141 close(p.Init)
142 }
143 return
144 case llData := <-p.llWatcher.LLUpdates:
145 logListUpdates.Inc()
146 if err := p.restartDistributor(ctx, llData.List); err != nil {
147 klog.Errorf("Unable to use Log-list:\n %v\n %v", err, llData.JSON)
148 } else if !init {
149 init = true
150 p.Init <- true
151 close(p.Init)
152 }
153 case err := <-p.llWatcher.Errors:
154 klog.Error(err)
155 }
156 }
157 }()
158 }
159
160
161
162 func (p *Proxy) restartDistributor(ctx context.Context, ll *loglist3.LogList) error {
163 d, err := p.distributorBuilder(ll)
164 if err != nil {
165
166 return err
167 }
168
169
170 refreshCtx, refreshCancel := context.WithCancel(ctx)
171 go schedule.Every(refreshCtx, p.rootsRefreshInterval, func(ectx context.Context) {
172 if errs := d.RefreshRoots(ectx); len(errs) > 0 {
173 for _, err := range errs {
174 klog.Warning(err)
175 }
176 }
177 })
178
179 p.distMu.Lock()
180 defer p.distMu.Unlock()
181 if p.distCancel != nil {
182 p.distCancel()
183 }
184 p.dist = d
185 p.distCancel = refreshCancel
186 return nil
187 }
188
189
190 func (p *Proxy) AddPreChain(ctx context.Context, rawChain [][]byte, loadPendingLogs bool) ([]*AssignedSCT, error) {
191 if p.dist == nil {
192 return []*AssignedSCT{}, fmt.Errorf("proxy distributor is not initialized. call Run()")
193 }
194
195 defer func(start time.Time) {
196 rspLatency.Observe(time.Since(start).Seconds(), "add-pre-chain")
197 }(time.Now())
198 return p.dist.AddPreChain(ctx, rawChain, loadPendingLogs)
199 }
200
201
202 func (p *Proxy) AddChain(ctx context.Context, rawChain [][]byte, loadPendingLogs bool) ([]*AssignedSCT, error) {
203 if p.dist == nil {
204 return []*AssignedSCT{}, fmt.Errorf("proxy distributor is not initialized. call Run()")
205 }
206 defer func(start time.Time) {
207 rspLatency.Observe(time.Since(start).Seconds(), "add-chain")
208 }(time.Now())
209 return p.dist.AddChain(ctx, rawChain, loadPendingLogs)
210 }
211
View as plain text