|
7 | 7 | "io" |
8 | 8 | "log" |
9 | 9 | "net/http" |
| 10 | + "path/filepath" |
10 | 11 | "strings" |
11 | 12 | "sync" |
12 | 13 | "sync/atomic" |
@@ -54,6 +55,18 @@ func (w *Watcher) Start() { |
54 | 55 | w.certChan = make(chan models.Entry, 5000) |
55 | 56 | } |
56 | 57 |
|
| 58 | + if config.AppConfig.General.Recovery.Enabled { |
| 59 | + ctIndexFilePath, err := filepath.Abs(config.AppConfig.General.Recovery.CTIndexFile) |
| 60 | + if err != nil { |
| 61 | + log.Printf("Error getting absolute path for CT index file: '%s', %s\n", config.AppConfig.General.Recovery.CTIndexFile, err) |
| 62 | + return |
| 63 | + } |
| 64 | + // Load Saved CT Indexes |
| 65 | + metrics.LoadCTIndex(ctIndexFilePath) |
| 66 | + // Save CTIndexes at regular intervals |
| 67 | + go metrics.SaveCertIndexesAtInterval(time.Second*30, ctIndexFilePath) // save indexes every X seconds |
| 68 | + } |
| 69 | + |
57 | 70 | // initialize the watcher with currently available logs |
58 | 71 | w.updateLogs() |
59 | 72 |
|
@@ -126,13 +139,16 @@ func (w *Watcher) addNewlyAvailableLogs(logList loglist3.LogList) { |
126 | 139 | w.wg.Add(1) |
127 | 140 | newCTs++ |
128 | 141 |
|
| 142 | + lastCTIndex := metrics.GetCTIndex(transparencyLog.URL) |
129 | 143 | ctWorker := worker{ |
130 | 144 | name: transparencyLog.Description, |
131 | 145 | operatorName: operator.Name, |
132 | 146 | ctURL: transparencyLog.URL, |
133 | 147 | entryChan: w.certChan, |
| 148 | + ctIndex: lastCTIndex, |
134 | 149 | } |
135 | 150 | w.workers = append(w.workers, &ctWorker) |
| 151 | + metrics.Init(operator.Name, transparencyLog.URL) |
136 | 152 |
|
137 | 153 | // Start a goroutine for each worker |
138 | 154 | go func() { |
@@ -199,12 +215,55 @@ func (w *Watcher) Stop() { |
199 | 215 | w.cancelFunc() |
200 | 216 | } |
201 | 217 |
|
| 218 | +// CreateIndexFile creates a ct_index.json file based on the current STHs of all availble logs. |
| 219 | +func (w *Watcher) CreateIndexFile(filePath string) error { |
| 220 | + logs, err := getAllLogs() |
| 221 | + if err != nil { |
| 222 | + return err |
| 223 | + } |
| 224 | + |
| 225 | + w.context, w.cancelFunc = context.WithCancel(context.Background()) |
| 226 | + log.Println("Fetching current STH for all logs...") |
| 227 | + for _, operator := range logs.Operators { |
| 228 | + // Iterate over each log of the operator |
| 229 | + for _, transparencyLog := range operator.Logs { |
| 230 | + // Check if the log is already being watched |
| 231 | + metrics.Init(operator.Name, transparencyLog.URL) |
| 232 | + log.Println("Fetching STH for", transparencyLog.URL) |
| 233 | + |
| 234 | + hc := http.Client{Timeout: 5 * time.Second} |
| 235 | + jsonClient, e := client.New(transparencyLog.URL, &hc, jsonclient.Options{UserAgent: userAgent}) |
| 236 | + if e != nil { |
| 237 | + log.Printf("Error creating JSON client: %s\n", e) |
| 238 | + continue |
| 239 | + } |
| 240 | + |
| 241 | + sth, getSTHerr := jsonClient.GetSTH(w.context) |
| 242 | + if getSTHerr != nil { |
| 243 | + // TODO this can happen due to a 429 error. We should retry the request |
| 244 | + log.Printf("Could not get STH for '%s': %s\n", transparencyLog.URL, getSTHerr) |
| 245 | + continue |
| 246 | + } |
| 247 | + |
| 248 | + metrics.index[transparencyLog.URL] = int64(sth.TreeSize) |
| 249 | + } |
| 250 | + } |
| 251 | + w.cancelFunc() |
| 252 | + |
| 253 | + tempFilePath := fmt.Sprintf("%s.tmp", filePath) |
| 254 | + metrics.SaveCertIndexes(tempFilePath, filePath) |
| 255 | + log.Println("Index file saved to", filePath) |
| 256 | + |
| 257 | + return nil |
| 258 | +} |
| 259 | + |
202 | 260 | // A worker processes a single CT log. |
203 | 261 | type worker struct { |
204 | 262 | name string |
205 | 263 | operatorName string |
206 | 264 | ctURL string |
207 | 265 | entryChan chan models.Entry |
| 266 | + ctIndex int64 |
208 | 267 | mu sync.Mutex |
209 | 268 | running bool |
210 | 269 | cancel context.CancelFunc |
@@ -284,18 +343,24 @@ func (w *worker) runWorker(ctx context.Context) error { |
284 | 343 | return errCreatingClient |
285 | 344 | } |
286 | 345 |
|
287 | | - sth, getSTHerr := jsonClient.GetSTH(ctx) |
288 | | - if getSTHerr != nil { |
| 346 | + // If recovery is enabled and the CT index is set, we start at the saved index. Otherwise we start at the latest STH. |
| 347 | + validSavedCTIndexExists := config.AppConfig.General.Recovery.Enabled && w.ctIndex >= 0 |
| 348 | + if !validSavedCTIndexExists { |
| 349 | + sth, getSTHerr := jsonClient.GetSTH(ctx) |
| 350 | + if getSTHerr != nil { |
289 | 351 | // TODO this can happen due to a 429 error. We should retry the request |
290 | | - log.Printf("Could not get STH for '%s': %s\n", w.ctURL, getSTHerr) |
291 | | - return errFetchingSTHFailed |
| 352 | + log.Printf("Could not get STH for '%s': %s\n", w.ctURL, getSTHerr) |
| 353 | + return errFetchingSTHFailed |
| 354 | + } |
| 355 | + // Start at the latest STH to skip all the past certificates |
| 356 | + w.ctIndex = int64(sth.TreeSize) |
292 | 357 | } |
293 | 358 |
|
294 | 359 | certScanner := scanner.NewScanner(jsonClient, scanner.ScannerOptions{ |
295 | 360 | FetcherOptions: scanner.FetcherOptions{ |
296 | 361 | BatchSize: 100, |
297 | 362 | ParallelFetch: 1, |
298 | | - StartIndex: int64(sth.TreeSize), // Start at the latest STH to skip all the past certificates |
| 363 | + StartIndex: w.ctIndex, |
299 | 364 | Continuous: true, |
300 | 365 | }, |
301 | 366 | Matcher: scanner.MatchAll{}, |
@@ -364,8 +429,9 @@ func certHandler(entryChan chan models.Entry) { |
364 | 429 | // Update metrics |
365 | 430 | url := entry.Data.Source.NormalizedURL |
366 | 431 | operator := entry.Data.Source.Operator |
| 432 | + index := entry.Data.CertIndex |
367 | 433 |
|
368 | | - metrics.Inc(operator, url) |
| 434 | + metrics.Inc(operator, url, index) |
369 | 435 | } |
370 | 436 | } |
371 | 437 |
|
@@ -421,14 +487,6 @@ func getAllLogs() (loglist3.LogList, error) { |
421 | 487 | } |
422 | 488 | } |
423 | 489 |
|
424 | | - // Add new ct logs to metrics |
425 | | - for _, operator := range allLogs.Operators { |
426 | | - for _, ctlog := range operator.Logs { |
427 | | - url := normalizeCtlogURL(ctlog.URL) |
428 | | - metrics.Init(operator.Name, url) |
429 | | - } |
430 | | - } |
431 | | - |
432 | 490 | return *allLogs, nil |
433 | 491 | } |
434 | 492 |
|
|
0 commit comments