Skip to content

Commit

Permalink
Monitor all Rekor verifiers and entry types
Browse files Browse the repository at this point in the history
Using the new Verifiers and Identities APIs, we can now easily monitor
all entry types with any type of verifier, including certs, keys, pgp
keys, ssh keys, etc. We can now monitor a certificate subject, email, or
key fingerprint. The subjects can also be regular expressions.

Also refactors the input to be yaml rather than a string.

Signed-off-by: Hayden Blauzvern <hblauzvern@google.com>
  • Loading branch information
haydentherapper committed Aug 31, 2023
1 parent b61561d commit 11019a2
Show file tree
Hide file tree
Showing 9 changed files with 609 additions and 813 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/reusable_monitoring.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ on:
type: number
default: 14
identities:
description: 'New-line separated list of identities, where each line can either be a subject or a subject followed by OIDC providers, whitespace-separated'
description: 'multiline yaml of certificate subjects and issuers, key subjects, and fingerprints. For certificates, if no issuers are specified, match any OIDC provider'
required: false
type: string

Expand Down Expand Up @@ -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 --identities "${{ inputs.identities }}"
- run: go run ./cmd/verifier --file ${{ env.LOG_FILE }} --once --monitored-values "${{ inputs.identities }}"
- name: Upload checkpoint
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
with:
Expand Down
35 changes: 25 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ Caveats:

## Identity monitoring

// TODO: Document fingerprint format

You can also specify a list of identities to monitor. Currently, only identities from the certificate's
Subject Alternative Name (SAN) field will be matched, and only for the hashedrekord Rekor entry type.

Expand Down Expand Up @@ -64,24 +66,37 @@ jobs:
file_issue: true # Strongly recommended: Files an issue on monitoring failure
artifact_retention_days: 14 # Optional, default is 14: Must be longer than the cron job frequency
identities: |
user@domain.com
otheruser@domain.com https://accounts.google.com https://github.com/login
certIdentities:
- certSubject: user@domain.com
- certSubject: otheruser@domain.com
issuers:
- https://accounts.google.com
- https://github.com/login
subjects:
- subject@domain.com
fingerprints:
- A0B1C2D3E4F5
```

In this example, the monitor will log any entries that contain a certificate whose SAN
is `user@domain.com`. It will also log any entries whose SAN is `otheruser@domain.com`
and the OIDC provider specified in a
[custom extension](https://github.com/sigstore/fulcio/blob/main/docs/oid-info.md#1361415726411--issuer)
matches one of the specified issuers (Google or GitHub in this example).
In this example, the monitor will log:

* Entries that contain a certificate whose SAN is `user@domain.com`
* Entries whose SAN is `otheruser@domain.com` and the OIDC provider specified in a [custom extension](https://github.com/sigstore/fulcio/blob/main/docs/oid-info.md#1361415726418--issuer-v2) matches one of the specified issuers (Google or GitHub in this example)
* Non-certificate entries, such as PGP or SSH keys, whose subject matches `subject@domain.com`
* Entries whose key or certificate fingerprint matches `A0B1C2D3E4F5`

Fingerprint values are as follows:

* For keys, certificates, and minisign, hex-encoded SHA-256 digest of the DER-encoded PKIX public key or certificate
* For SSH and PGP, the standard for each ecosystem:
* For SSH, unpadded base-64 encoded SHA-256 digest of the key
* For PGP, hex-encoded SHA-1 digest of a key, which can be either a primary key or subkey

Upcoming features:

* Creating issues when identities are found
* Support for all Rekor types
* Support for other identities
* Key/certificate fingerprints
* CI identity values in Fulcio certificates
* Regular expressions

## Security

Expand Down
57 changes: 21 additions & 36 deletions cmd/verifier/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package main

import (
"bufio"
"context"
"encoding/hex"
"flag"
Expand All @@ -33,6 +32,7 @@ import (
"github.com/sigstore/rekor/pkg/util"
"github.com/sigstore/rekor/pkg/verify"
"github.com/sigstore/sigstore/pkg/signature"
"gopkg.in/yaml.v3"
)

// Default values for monitoring job parameters
Expand All @@ -42,28 +42,8 @@ const (
outputIdentitiesFileName = "identities.txt"
)

func parseIdentities(identitiesInput string) (rekor.Identities, error) {
ids := rekor.Identities{}
scanner := bufio.NewScanner(strings.NewReader(identitiesInput))
for scanner.Scan() {
l := strings.Fields(scanner.Text())
switch len(l) {
case 0:
continue
case 1:
ids.Identities = append(ids.Identities, rekor.Identity{Subject: l[0]})
default:
ids.Identities = append(ids.Identities, rekor.Identity{Subject: l[0], Issuers: l[1:]})
}
}
if err := scanner.Err(); err != nil {
return ids, err
}
return ids, nil
}

// runConsistencyCheck periodically verifies the root hash consistency of a Rekor log.
func runConsistencyCheck(interval *time.Duration, rekorClient *gclient.Rekor, verifier signature.Verifier, logInfoFile *string, ids rekor.Identities, outputIdentitiesFile *string, once *bool) error {
func runConsistencyCheck(interval *time.Duration, rekorClient *gclient.Rekor, verifier signature.Verifier, logInfoFile *string, mvs rekor.MonitoredValues, outputIdentitiesFile *string, once *bool) error {
ticker := time.NewTicker(*interval)
defer ticker.Stop()

Expand Down Expand Up @@ -131,23 +111,22 @@ func runConsistencyCheck(interval *time.Duration, rekorClient *gclient.Rekor, ve
endIndex := int(checkpoint.Size) + totalSize - 1

// Search for identities in the log range
if len(ids.Identities) > 0 {
if len(mvs.CertificateIdentities) > 0 || len(mvs.Fingerprints) > 0 || len(mvs.Subjects) > 0 {
entries, err := rekor.GetEntriesByIndexRange(context.Background(), rekorClient, startIndex, endIndex)
if err != nil {
return fmt.Errorf("error getting entries by index range: %v", err)
}
idEntries, err := rekor.MatchedIndices(entries, ids)
idEntries, err := rekor.MatchedIndices(entries, mvs)
if err != nil {
return fmt.Errorf("error finding log indices: %v", err)
}

if len(idEntries) > 0 {
for _, idEntry := range idEntries {
fmt.Fprintf(os.Stderr, "Found subject %s, issuer %s at log index %d, uuid %s\n",
idEntry.Subject, idEntry.Issuer, idEntry.Index, idEntry.UUID)
fmt.Fprintf(os.Stderr, "Found %s\n", idEntry.String())

if err := file.WriteIdentity(*outputIdentitiesFile, idEntry); err != nil {
return fmt.Errorf("failed to write identity: %v", err)
return fmt.Errorf("failed to write entry: %v", err)
}
}
}
Expand All @@ -169,23 +148,29 @@ func main() {
interval := flag.Duration("interval", 5*time.Minute, "Length of interval between each periodical consistency check")
logInfoFile := flag.String("file", logInfoFileName, "Name of the file containing initial merkle tree information")
once := flag.Bool("once", false, "Perform consistency check once and exit")
identitiesInput := flag.String("identities", "", "newline-separated list of identities and issuers in the format "+
"subject [issuer...]. If no issuers are specified, match any OIDC providers.")
monitoredValsInput := flag.String("monitored-values", "", "yaml of certificate subjects and issuers, key subjects, "+
"and fingerprints. For certificates, if no issuers are specified, match any OIDC provider.")
outputIdentitiesFile := flag.String("output-identities", outputIdentitiesFileName,
"Name of the file containing indices and identities found in the log. Format is \"subject issuer index uuid\"")
flag.Parse()

ids, err := parseIdentities(*identitiesInput)
if err != nil {
var monitoredVals rekor.MonitoredValues
if err := yaml.Unmarshal([]byte(*monitoredValsInput), &monitoredVals); err != nil {
log.Fatalf("error parsing identities: %v", err)
}
for _, id := range ids.Identities {
if len(id.Issuers) == 0 {
fmt.Printf("Monitoring subject %s\n", id.Subject)
for _, certId := range monitoredVals.CertificateIdentities {

Check warning on line 161 in cmd/verifier/main.go

View workflow job for this annotation

GitHub Actions / lint

var-naming: range var certId should be certID (revive)
if len(certId.Issuers) == 0 {
fmt.Printf("Monitoring subject %s\n", certId.CertSubject)
} else {
fmt.Printf("Monitoring subject %s for issuer(s) %s\n", id.Subject, strings.Join(id.Issuers, ","))
fmt.Printf("Monitoring subject %s for issuer(s) %s\n", certId.CertSubject, strings.Join(certId.Issuers, ","))
}
}
for _, fp := range monitoredVals.Fingerprints {
fmt.Printf("Monitoring fingerprint %s\n", fp)
}
for _, sub := range monitoredVals.Subjects {
fmt.Printf("Monitoring subject %s\n", sub)
}

rekorClient, err := client.GetRekorClient(*serverURL)
if err != nil {
Expand All @@ -197,7 +182,7 @@ func main() {
log.Fatal(err)
}

err = runConsistencyCheck(interval, rekorClient, verifier, logInfoFile, ids, outputIdentitiesFile, once)
err = runConsistencyCheck(interval, rekorClient, verifier, logInfoFile, monitoredVals, outputIdentitiesFile, once)
if err != nil {
log.Fatalf("%v", err)
}
Expand Down
60 changes: 15 additions & 45 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,17 @@ go 1.20
require (
github.com/go-openapi/runtime v0.26.0
github.com/go-openapi/swag v0.22.4
github.com/sigstore/cosign/v2 v2.1.1
github.com/sigstore/rekor v1.2.2
github.com/sigstore/rekor v1.2.3-0.20230815151852-3e1715a2b7b7
github.com/sigstore/sigstore v1.7.2
golang.org/x/mod v0.12.0
gopkg.in/yaml.v3 v3.0.1
)

require (
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7 // indirect
github.com/digitorus/pkcs7 v0.0.0-20221212123742-001c36b64ec3 // indirect
github.com/digitorus/timestamp v0.0.0-20221019182153-ef3b63b79b31 // indirect
github.com/docker/cli v23.0.5+incompatible // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/docker/docker v23.0.5+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/go-chi/chi v4.1.2+incompatible // indirect
github.com/go-logr/logr v1.2.4 // indirect
Expand All @@ -37,72 +30,49 @@ require (
github.com/go-openapi/validate v0.22.1 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.1 // indirect
github.com/go-playground/validator/v10 v10.15.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/certificate-transparency-go v1.1.6 // indirect
github.com/google/go-containerregistry v0.15.2 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-hclog v1.2.0 // indirect
github.com/hashicorp/go-retryablehttp v0.7.4 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/in-toto/in-toto-golang v0.9.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.16.5 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/letsencrypt/boulder v0.0.0-20221109233200-85aa52084eaf // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc3 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/sassoftware/relic v7.2.1+incompatible // indirect
github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
github.com/sigstore/timestamp-authority v1.1.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/afero v1.9.5 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/cobra v1.7.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.16.0 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
github.com/theupdateframework/go-tuf v0.5.2 // indirect
github.com/theupdateframework/go-tuf v0.6.1 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/transparency-dev/merkle v0.0.2 // indirect
github.com/vbatts/tar-split v0.11.3 // indirect
go.mongodb.org/mongo-driver v1.11.3 // indirect
go.opentelemetry.io/otel v1.16.0 // indirect
go.opentelemetry.io/otel/metric v1.16.0 // indirect
go.opentelemetry.io/otel/trace v1.16.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/crypto v0.11.0 // indirect
go.uber.org/zap v1.25.0 // indirect
golang.org/x/crypto v0.12.0 // indirect
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
golang.org/x/net v0.12.0 // indirect
golang.org/x/net v0.14.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.10.0 // indirect
golang.org/x/term v0.10.0 // indirect
golang.org/x/text v0.11.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
google.golang.org/grpc v1.56.1 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/term v0.11.0 // indirect
golang.org/x/text v0.12.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230731193218-e0aa005b6bdf // indirect
google.golang.org/grpc v1.57.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gotest.tools/v3 v3.1.0 // indirect
k8s.io/klog/v2 v2.100.1 // indirect
)
Loading

0 comments on commit 11019a2

Please sign in to comment.