diff --git a/.github/workflows/reusable_monitoring.yml b/.github/workflows/reusable_monitoring.yml index d4dbcf3d..09309afb 100644 --- a/.github/workflows/reusable_monitoring.yml +++ b/.github/workflows/reusable_monitoring.yml @@ -76,7 +76,7 @@ jobs: run: cat ${{ env.LOG_FILE }} # Skip on first run continue-on-error: true - - run: go run ./cmd/verifier --file ${{ env.LOG_FILE }} --once --monitored-values "${{ inputs.identities }}" --user-agent "${{ format('{0}/{1}/{2}', needs.detect-workflow.outputs.repository, needs.detect-workflow.outputs.ref, github.run_id) }}" + - run: go run ./cmd/rekor_consistency --file ${{ env.LOG_FILE }} --once --monitored-values "${{ inputs.identities }}" --user-agent "${{ format('{0}/{1}/{2}', needs.detect-workflow.outputs.repository, needs.detect-workflow.outputs.ref, github.run_id) }}" - name: Upload checkpoint uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: diff --git a/cmd/verifier/main.go b/cmd/rekor_consistency/main.go similarity index 87% rename from cmd/verifier/main.go rename to cmd/rekor_consistency/main.go index 5ce979d4..c0e9fcd2 100644 --- a/cmd/verifier/main.go +++ b/cmd/rekor_consistency/main.go @@ -20,6 +20,7 @@ import ( "flag" "fmt" "log" + "os" "runtime" "strings" "time" @@ -55,6 +56,16 @@ func main() { userAgentString := flag.String("user-agent", "", "details to include in the user agent string") flag.Parse() + rekorClient, err := client.GetRekorClient(*serverURL, client.WithUserAgent(strings.TrimSpace(fmt.Sprintf("rekor-monitor/%s (%s; %s) %s", version.GetVersionInfo().GitVersion, runtime.GOOS, runtime.GOARCH, *userAgentString)))) + if err != nil { + log.Fatalf("getting Rekor client: %v", err) + } + + verifier, err := rekor.GetLogVerifier(context.Background(), rekorClient) + if err != nil { + log.Fatal(err) + } + var monitoredVals identity.MonitoredValues if err := yaml.Unmarshal([]byte(*monitoredValsInput), &monitoredVals); err != nil { log.Fatalf("error parsing identities: %v", err) @@ -73,23 +84,29 @@ func main() { fmt.Printf("Monitoring subject %s\n", sub) } - rekorClient, err := client.GetRekorClient(*serverURL, client.WithUserAgent(strings.TrimSpace(fmt.Sprintf("rekor-monitor/%s (%s; %s) %s", version.GetVersionInfo().GitVersion, runtime.GOOS, runtime.GOARCH, *userAgentString)))) - if err != nil { - log.Fatalf("getting Rekor client: %v", err) - } - - verifier, err := rekor.GetLogVerifier(context.Background(), rekorClient) - if err != nil { - log.Fatal(err) - } - err = rekor.VerifyConsistencyCheckInputs(interval, logInfoFile, outputIdentitiesFile, once) if err != nil { log.Fatal(err) } - err = rekor.RunConsistencyCheck(*interval, rekorClient, verifier, *logInfoFile, monitoredVals, *outputIdentitiesFile, *once) - if err != nil { - log.Fatalf("%v", err) + ticker := time.NewTicker(*interval) + defer ticker.Stop() + + // Loop will: + // 1. Fetch latest checkpoint and verify + // 2. If old checkpoint is present, verify consistency proof + // 3. Write latest checkpoint to file + + // To get an immediate first tick + for ; ; <-ticker.C { + err = rekor.RunConsistencyCheck(rekorClient, verifier, *logInfoFile, monitoredVals, *outputIdentitiesFile) + if err != nil { + fmt.Fprintf(os.Stderr, "error running consistency check: %v", err) + return + } + + if *once { + return + } } } diff --git a/go.mod b/go.mod index f6cd5f8a..18e2631f 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/sigstore/sigstore v1.8.10 github.com/wneessen/go-mail v0.5.1 golang.org/x/mod v0.21.0 + gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 sigs.k8s.io/release-utils v0.8.5 ) @@ -108,6 +109,5 @@ require ( golang.org/x/time v0.5.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/pkg/rekor/verifier.go b/pkg/rekor/verifier.go index 4864465f..fbb54114 100644 --- a/pkg/rekor/verifier.go +++ b/pkg/rekor/verifier.go @@ -111,62 +111,49 @@ func VerifyConsistencyCheckInputs(interval *time.Duration, logInfoFile *string, } // RunConsistencyCheck periodically verifies the root hash consistency of a Rekor log. -func RunConsistencyCheck(interval time.Duration, rekorClient *client.Rekor, verifier signature.Verifier, logInfoFile string, mvs identity.MonitoredValues, outputIdentitiesFile string, once bool) error { - ticker := time.NewTicker(interval) - defer ticker.Stop() - - // Loop will: - // 1. Fetch latest checkpoint and verify - // 2. If old checkpoint is present, verify consistency proof - // 3. Write latest checkpoint to file +func RunConsistencyCheck(rekorClient *client.Rekor, verifier signature.Verifier, logInfoFile string, mvs identity.MonitoredValues, outputIdentitiesFile string) error { + logInfo, err := GetLogInfo(context.Background(), rekorClient) + if err != nil { + return fmt.Errorf("failed to get log info: %v", err) + } + checkpoint, err := verifyLatestCheckpointSignature(logInfo, verifier) + if err != nil { + return fmt.Errorf("failed to verify signature of latest checkpoint: %v", err) + } - // To get an immediate first tick - for ; ; <-ticker.C { - logInfo, err := GetLogInfo(context.Background(), rekorClient) - if err != nil { - return fmt.Errorf("failed to get log info: %v", err) - } - checkpoint, err := verifyLatestCheckpointSignature(logInfo, verifier) + fi, err := os.Stat(logInfoFile) + // File containing previous checkpoints exists + var prevCheckpoint *util.SignedCheckpoint + if err == nil && fi.Size() != 0 { + prevCheckpoint, err = verifyCheckpointConsistency(logInfoFile, checkpoint, *logInfo.TreeID, rekorClient, verifier) if err != nil { - return fmt.Errorf("failed to verify signature of latest checkpoint: %v", err) + return fmt.Errorf("failed to verify previous checkpoint: %v", err) } - fi, err := os.Stat(logInfoFile) - // File containing previous checkpoints exists - var prevCheckpoint *util.SignedCheckpoint - if err == nil && fi.Size() != 0 { - prevCheckpoint, err = verifyCheckpointConsistency(logInfoFile, checkpoint, *logInfo.TreeID, rekorClient, verifier) - if err != nil { - return fmt.Errorf("failed to verify previous checkpoint: %v", err) - } - - } - - // Write if there was no stored checkpoint or the sizes differ - if prevCheckpoint == nil || prevCheckpoint.Size != checkpoint.Size { - if err := file.WriteCheckpoint(checkpoint, logInfoFile); err != nil { - // TODO: Once the consistency check and identity search are split into separate tasks, this should hard fail. - // Temporarily skipping this to allow this job to succeed, remediating the issue noted here: https://github.com/sigstore/rekor-monitor/issues/271 - fmt.Fprintf(os.Stderr, "failed to write checkpoint: %v", err) - } - } + } - if prevCheckpoint != nil && prevCheckpoint.Size != checkpoint.Size { - err = writeIdentitiesBetweenCheckpoints(logInfo, prevCheckpoint, checkpoint, mvs, rekorClient, outputIdentitiesFile) - if err != nil { - return fmt.Errorf("failed to monitor identities: %v", err) - } + // Write if there was no stored checkpoint or the sizes differ + if prevCheckpoint == nil || prevCheckpoint.Size != checkpoint.Size { + if err := file.WriteCheckpoint(checkpoint, logInfoFile); err != nil { + // TODO: Once the consistency check and identity search are split into separate tasks, this should hard fail. + // Temporarily skipping this to allow this job to succeed, remediating the issue noted here: https://github.com/sigstore/rekor-monitor/issues/271 + fmt.Fprintf(os.Stderr, "failed to write checkpoint: %v", err) } + } - // TODO: Switch to writing checkpoints to GitHub so that the history is preserved. Then we only need - // to persist the last checkpoint. - // Delete old checkpoints to avoid the log growing indefinitely - if err := file.DeleteOldCheckpoints(logInfoFile); err != nil { - return fmt.Errorf("failed to delete old checkpoints: %v", err) + if prevCheckpoint != nil && prevCheckpoint.Size != checkpoint.Size { + err = writeIdentitiesBetweenCheckpoints(logInfo, prevCheckpoint, checkpoint, mvs, rekorClient, outputIdentitiesFile) + if err != nil { + return fmt.Errorf("failed to monitor identities: %v", err) } + } - if once { - return nil - } + // TODO: Switch to writing checkpoints to GitHub so that the history is preserved. Then we only need + // to persist the last checkpoint. + // Delete old checkpoints to avoid the log growing indefinitely + if err := file.DeleteOldCheckpoints(logInfoFile); err != nil { + return fmt.Errorf("failed to delete old checkpoints: %v", err) } + + return nil } diff --git a/pkg/test/e2e/e2e_test.go b/pkg/test/e2e/e2e_test.go index a8c5a940..33b5ce0a 100755 --- a/pkg/test/e2e/e2e_test.go +++ b/pkg/test/e2e/e2e_test.go @@ -31,7 +31,6 @@ import ( "runtime" "strings" "testing" - "time" "github.com/sigstore/rekor-monitor/pkg/fulcio/extensions" "github.com/sigstore/rekor-monitor/pkg/identity" @@ -148,8 +147,6 @@ func TestRunConsistencyCheck(t *testing.T) { tempOutputIdentitiesFileName := tempOutputIdentitiesFile.Name() defer os.Remove(tempOutputIdentitiesFileName) - interval := time.Minute - monitoredVals := identity.MonitoredValues{ Subjects: []string{subject}, CertificateIdentities: []identity.CertificateIdentity{ @@ -168,9 +165,8 @@ func TestRunConsistencyCheck(t *testing.T) { certFingerprint, }, } - once := true - err = rekor.RunConsistencyCheck(interval, rekorClient, verifier, tempLogInfoFileName, monitoredVals, tempOutputIdentitiesFileName, once) + err = rekor.RunConsistencyCheck(rekorClient, verifier, tempLogInfoFileName, monitoredVals, tempOutputIdentitiesFileName) if err != nil { t.Errorf("first consistency check failed: %v", err) } @@ -210,7 +206,7 @@ func TestRunConsistencyCheck(t *testing.T) { t.Errorf("expected checkpoint size of 2, received size %d", checkpoint.Size) } - err = rekor.RunConsistencyCheck(interval, rekorClient, verifier, tempLogInfoFileName, monitoredVals, tempOutputIdentitiesFileName, once) + err = rekor.RunConsistencyCheck(rekorClient, verifier, tempLogInfoFileName, monitoredVals, tempOutputIdentitiesFileName) if err != nil { t.Errorf("second consistency check failed: %v", err) }