SigStore WebPKI

fulcio - A New Kind of Root CA For Code Signing

fulcio is a free Root-CA for code signing certs - issuing certificates based on an OIDC email address.

fulcio only signs short-lived certificates that are valid for under 20 minutes.

Status

Fulcio is a work in progress. There's working code and a running instance and a plan, but you should not attempt to try to actually use it for anything.

The fulcio root cert is currently:

-----BEGIN CERTIFICATE-----
  MIIB+DCCAX6gAwIBAgITNVkDZoCiofPDsy7dfm6geLbuhzAKBggqhkjOPQQDAzAq
  MRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIx
  MDMwNzAzMjAyOVoXDTMxMDIyMzAzMjAyOVowKjEVMBMGA1UEChMMc2lnc3RvcmUu
  ZGV2MREwDwYDVQQDEwhzaWdzdG9yZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABLSy
  A7Ii5k+pNO8ZEWY0ylemWDowOkNa3kL+GZE5Z5GWehL9/A9bRNA3RbrsZ5i0Jcas
  taRL7Sp5fp/jD5dxqc/UdTVnlvS16an+2Yfswe/QuLolRUCrcOE2+2iA5+tzd6Nm
  MGQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYE
  FMjFHQBBmiQpMlEk6w2uSu1KBtPsMB8GA1UdIwQYMBaAFMjFHQBBmiQpMlEk6w2u
  Su1KBtPsMAoGCCqGSM49BAMDA2gAMGUCMH8liWJfMui6vXXBhjDgY4MwslmN/TJx
  Ve/83WrFomwmNf056y1X48F9c4m3a3ozXAIxAKjRay5/aj/jsKKGIkmQatjI8uup
  Hr/+CxFvaJWmpYqNkLDGRU+9orzh5hI2RrcuaQ==
  -----END CERTIFICATE-----

We WILL change this and add intermediaries in the future.

API

The API is defined via OpenAPI, defined here.

Transparency

Fulcio will publish issued certificates to a unique CT-log. That log will be hosted by the sigstore project.

We encourage auditors to monitor this log, and aim to help people access the data.

A simple example would be a service that emails users (on a different address) when ceritficates have been issued on their behalf. This can then be used to detect bad behavior or possible compromise.

Parameters

The fulcio root CA is currently running on GCP Private CA with the EC_P384_SHA384 algorithm.

Security Model

  • Fulcio assumes that a valid OIDC token is a sufficient "proof of ownership" of an email address.

  • To mitigate against this, Fulcio uses a Transparency log to help protect against OIDC compromise. This means:

    • Fulcio MUST publish all certificates to the log.
    • Clients MUST NOT trust certificates that are not in the log.

    As a result users can detect any mis-issued certificates.

  • Combined with rekor's signature transparency, artifacts signed with compromised accounts can be identified.

Revocation, Rotation and Expiry

There are two main approaches to code signing:

  1. Long-term certs
  2. Trusted time-stamping

Long-term certs

These certificates are typically valid for years. All old code must be re-signed with a new cert before an old cert expires. Typically this works with long deprecation periods, dual-signing and planned rotations.

There are a couple problems with this approach:

  1. It assumes users can keep acess to private keys and keep them secret over log periods of time
  2. Revocation is hard and doesn't work well

Fulcio's Model

Fulcio is designed to avoid revocation, by issuing short-lived certificates. What really matters for CodeSigning is to know that an artifact was signed while the certificate was valid.

This can be done a few ways:

  • Third-party Timestamp Authorities (RFC3161)
  • Transparency Logs
  • Both (Fulcio's Model)

RFC3161 Timestamp Servers

RFC3161 defines a protocol for Trusted Timestamps. Parties can send a payload to an RFC3161 service and the service digitally signs that payload with its own timestamp. This is the equivalent of posting a hash to Twitter - you are getting a third-party attestation that you had a particular piece of data at a particular time, as observed by that same third-party.

The downside is that users need to interact with another service. They must timestamp all signatures and check the timestamps of all signatures - adding another dependency and set of keys they must trust (the timestamp servers). We could provide one for free, but if people don't trust the clock in our transparency ledger they might not trust another service we run.

Transparency Logs

The rekor service provides a transparency log of software signatures. As entries are appended into this log, rekor periodically signs the full tree along with a timestamp.

An entry in Rekor provides a single-party attestation that a piece of data existed prior to a certain time. These timestamps cannot be tampered with later, providing long-term trust. This long-term trust also requires that the log is monitored.

Transparency Logs make it hard to forge timestamps long-term, but in short time-windows it would be much easier for the Rekor operator to fake or forge timestamps. To mitigate this, Rekor's timestamps and STHs are signed - a valid signed tree hash contains a non-repudiadable timestamp. These signed timestamp tokens are saved as evidence in case Rekor's clock changes in the future. So, more skeptical parties don't have to trust Rekor at all!

Why Not Both!?!?!?

Like usual, we can combine timestamp servers and transparency logs to do a bit better.

Third-party timestamp authorities provide signatures for pieces of data, which includes a timestamp. Rekor can interact with these third-party TSAs automatically, allowing users to skip this step. Rekor can get its own STH (including the timestamp) signed by one or many third-party TSAs regularly.

Each timestamp attestation in the Rekor log provides a fixed "fencepost" in time. Rekor, the client and a third-party can all provide evidence of the state of the world at a point in time. Fenceposts every ten minutes protect all data in between. Auditors can monitor Rekor's log to ensure these are added, shifting the complexity burden from users to auditors.

Info

Fulcio is developed as part of the sigstore project.

We also use a slack channel! Click here for the invite link.

Owner
sigstore
Software supply chain transparency
sigstore
Comments
  • Add file backed certificate authority

    Add file backed certificate authority

    Summary

    Adds a simple file-based certificate authority to Fulcio. Expects PEM encoded key-pair without password protection.

    Usage

    fulcio serve --ca fileca --fileca-key /path/to/key.pem --fileca-cert /path/to/cert.pem
    

    By default the file passed are watched for updates and reloaded on change. This behaviour can be disabled with ---fileca-watch false.

    Fixes #276

    Remaining Work

    • [x] Add unit tests
    • [x] Run an integration test (docker-compose maybe?)

    Release Note

    - Added a simple file based certificate authority `fileca`
    
  • Support simple file-backed CA

    Support simple file-backed CA

    Description

    Some operators may wish to use a simple file based CA to issue certificates. Lets support loading CA signing key and certificate chain from a local files.

    Design aspects:

    • Will require private key to be encrypted with password
    • Cert and private key will be in seperate files
    • Cert and private key must be PEM encoded

    Work items This work will be broken up into a couple small deliverables below

    • [x] Support a root CA certificate (no certificates chains / intermediates)
    • [ ] Add support for intermediate CA / certificate chains
    • [ ] Replace emphemeralca in the e2e tests for cosign
    • [ ] Add documentation of usage (explain the CLI flags / basic example of generating a key-pair with openssl)
  • Enforce that identities are of an expected format

    Enforce that identities are of an expected format

    Description

    Dex - Look like email, K8S - Look like a service account, Username - Doesn't look like an email, etc

    Context: https://docs.google.com/document/d/1o8_bXIygufgiohJGlmBzqF4_BnXCTfgh4ILgJFJxYRs/edit?resourcekey=0-YEar3v67uoT31kj83dCVvA#heading=h.oiw6nn1ucgaq

  • Embed SCTs in issued certificates

    Embed SCTs in issued certificates

    Summary

    This adds support for embedding SCTs in certificates instead of returning a header with a detached SCTs. This is done by implementing an SCT interface for a signer. For example, GCP CA Service will not support embedded SCTs, but KMS will.

    This heavily leverages the Go CT library. I've removed the custom client in favor of the CT library client, which includes more verification and retry logic. Note that there's a TODO to include the public key of the CT log in Fulcio so that the SCT is checked before returning a response.

    A certificate is signed twice, which adds an extra remote call to KMS. The first certificate is added to the CT log via AddPreChain instead of AddChain.

    The Cosign client will need to be updated to support embedded SCTs.

    Signed-off-by: Hayden Blauzvern [email protected]

    Ticket Link

    Fixes #42, #310

    Release Note

    Added support for embedded SCTs for intermediate CA implementation. The CA will not return the SCT detached in a header. The client must verify the SCT using the SCT embedded in the certificate.
    
  • Upgrade fulcios to use of the google privateca api at v1

    Upgrade fulcios to use of the google privateca api at v1

    Signed-off-by: Scott Nichols [email protected]

    Summary

    We were still on a v1beta1 api for the googleca private ca. This adds a flag to use the v1 of the api. We are still needing to update the cert to v1.

    The new flag --gcp_private_ca_version selects v1 by default to allow us to drop the flag when we move to v1 certs and then delete the flag and the v1beta1 codepaths.

    Ticket Link

    Relates to a checkbox in https://github.com/sigstore/fulcio/issues/191

    Release Note

    Fulcio now can use the v1 api for google privateca.
    
  • Changing the detached SCT to the correct format

    Changing the detached SCT to the correct format

    Description

    See https://github.com/trailofbits/sigstore-python/pull/24 - There was an issue around unmarshalling because the detached SCT is in an unexpected format.

    Given that Fulcio 0.4 will break the Cosign client already, I'd like to just go ahead and fix the header. We'll need to hold off on Fulcio 0.4 and release Cosign 1.8 (with a change to expect a different struct for the detached SCT from Fulcio) first.

    @dlorenc @cpanato FYI - Does this seem reasonable?

  • examples: This adds example code on how to fetch a fulcio certificate

    examples: This adds example code on how to fetch a fulcio certificate

    Summary

    The API isn't super clearly documented and the workflow how to fetch certificates is not well explained. There are no simple code examples nor examples around that makes fulcio easy to pick up. This is a start at providing examples for how to utilize fulcio and something we should look at adopting for the other sigstore projects as well.

    Signed-off-by: Morten Linderud [email protected]

  • Usage outside of sigstore

    Usage outside of sigstore

    The fulcio client code recently moved from this repo to the sigstore cli in the sigstore repo. I think fulcio is useful on its own as well, independently of rekor. The sigstore cli (correct me if I'm wrong) currently doesn't allow to just retrieve a fulcio cert without signing and sending to rekor.

    Is fulcio intended to stay as an independent service? If so, does it make sense to have a stand-alone client here (or elsewhere) for it? Or alternatively extend the sigstore cli to retrieve a cert for custom usage?

  • Ambiguous CSR behavior: Fulcio accepts CSRs with email subjects that aren't emails

    Ambiguous CSR behavior: Fulcio accepts CSRs with email subjects that aren't emails

    I noticed this while doing a read-through of sigstore-python's signing code: we currently construct a CSR like so:

            oidc_identity = Identity(identity_token)
    
            # Build an X.509 Certificiate Signing Request
            builder = (
                x509.CertificateSigningRequestBuilder()
                .subject_name(
                    x509.Name(
                        [
                            x509.NameAttribute(NameOID.EMAIL_ADDRESS, oidc_identity.proof),
                        ]
                    )
                )
                .add_extension(
                    x509.BasicConstraints(ca=False, path_length=None),
                    critical=True,
                )
            )
            certificate_request = builder.sign(private_key, hashes.SHA256())
    

    ...where oidc_identity.proof is the value of either the email or sub claim, depending on the kind of OIDC JWT being used.

    For OIDC JWTs that attest to email identities, this makes sense: we're sending a CSR with a SubjectName that's an email address. It doesn't make sense for OIDC JWTs that attest to URLs, since those aren't email addresses.

    However, it currently works for both: sigstore-python currently uses NameOID.EMAIL_ADDRESS unconditionally, and Fulcio has been happily minting certs even when the SubjectName is really a URL (like a GitHub Actions workflow reference).

    This probably isn't exploitable or dangerous in any way, since the CSR is otherwise well-formed and accompanied by a valid OIDC JWT and proof of possession for the signing key. But it's a little bit surprising, and suggests that Fulcio's CSR validation is a little looser than it should be.

    IMO, there are (at least) two acceptable alternative behaviors here:

    • Ignore the SubjectName in the CSR entirely, and (after a deprecation period) have users set it to something well-known, like sigstore-csr instead of an email address with an email OID. Then, explicitly document the behavior of re-deriving the subject from the adjacent JWT.
    • Continue to handle SubjectName, but enforce the correct OID type for the content of the subject. In other words: SubjectNames that are tagged as emails should look like emails, URLs should be UNSTRUCTURED_NAME or something similar, or maybe something custom.
  • Change username format, enforce identity format

    Change username format, enforce identity format

    Summary

    This updates the username type to avoid the username subject format looking like an email. Fulcio will now specify the subject in the OtherName SAN, and the format will use a ! instead of @.

    This required some custom ASN.1 marshalling and unmarshalling, since crypto/x509 does not support the OtherName SAN.

    This also adds enforcement that email subjects match a basic email regex format, and that other types do not look like emails.

    Fixes https://github.com/sigstore/fulcio/issues/716

    Release Note

    Changed username identity format to username!Domain, username now specified in the OtherName SAN. If you have deployed your own instance of Fulcio and are using username issuers, you must update to the latest Cosign release.

    Documentation

  • Add note about the status of the legacy HTTP API.

    Add note about the status of the legacy HTTP API.

    Summary

    Small doc clarification about the legacy HTTP API.

    Ticket Link

    No ticket, but this came up in the 2022-04-19 community meeting.

    Release Note

    NONE
    
  • Standardizing Fulcio Certificate Extensions

    Standardizing Fulcio Certificate Extensions

    Summary

    Proposal for standardizing Fulcio's Certificate Extensions to align with the discussion on standardizing OIDC token claims across CI/CD systems (today GitHub Actions, in future Circle, GitLab, Buildkite etc).

    The aim here is to find new platform agnostic extensions (as the current ones are GitHub specific) that make sense across the different CI/CD providers that we'd like to see supported in Fulcio.

    I've preemptively moved the existing oid info doc to a deprecated version with little thought into how this transition should happen in practice. This should probably be scoped in a lot more detail.

    Resolves https://github.com/sigstore/fulcio/issues/754

    Signed-off-by: Philip Harrison [email protected]

  • Add in --duplex flag to run HTTP and GRPC servers on the same port

    Add in --duplex flag to run HTTP and GRPC servers on the same port

    also adds in a unit test for this. we can optionally start trying this out in staging, and if it works well we can consider deprecating the previous two-port solution.

    Signed-off-by: Priya Wadhwa [email protected]

    Release Note

    Add in --duplex flag to run HTTP and GRPC servers on the same port

    cc @vaikas

  • Every failed request is logged as an error

    Every failed request is logged as an error

    See https://github.com/sigstore/fulcio/blob/main/pkg/server/error.go#L42, all request errors are logged as errors regardless of if it's due to the user (4xx) or server (429, 5xx). This leads to a lot of errors in the logs that are due to malformed JWTs.

    We should differentiate between user errors and server errors. User errors should be at most WARN, but for JWT errors, I would remove logging entirely since there's nothing actionable to do.

  • [docs] OID information: provide more detail about the structure of each X.509 extension?

    [docs] OID information: provide more detail about the structure of each X.509 extension?

    I was perusing the oid-info doc for the nth time, and I noticed that we don't currently explicitly specify how each OID-identified X.509v3 extension is laid out.

    For example:

    1.3.6.1.4.1.57264.1.2 | GitHub Workflow Trigger This contains the event_name claim from the GitHub OIDC Identity token that contains the name of the event that triggered the workflow run. (docs)

    In particular, it might make sense to clarify that each of our extensions is (currently) laid out "inline", rather than being a nested ASN.1 structure containing the string or other data.

  • Create workflow to sync OIDC config to infrastructure repo

    Create workflow to sync OIDC config to infrastructure repo

    Description

    Right now, we have a second copy of the OIDC config in PGI. Whenever it's updated in Fulcio, a workflow should ideally create a PR in PGI.

  • Make it hard to accidentally not have a CT log

    Make it hard to accidentally not have a CT log

    Right now, if --ct-log-url is not passed, we don't set up the CT log:

    https://github.com/sigstore/fulcio/blob/d43e0d948c1f26d9a6d910857c7ecb86b8980564/cmd/app/serve.go#L225

    That feels easy to do accidentally. Should we require a --no-ct-log flag for testing or other environments without a CT log?

    H/T @mnm678 and her student (whose GH I don't know)

Related tags
Plugin for Helm to integrate the sigstore ecosystem

helm-sigstore Plugin for Helm to integrate the sigstore ecosystem. Search, upload and verify signed Helm Charts in the Rekor Transparency Log. Info he

Dec 21, 2022
Stuff to make standing up sigstore (esp. for testing) easier for e2e/integration testing.
Stuff to make standing up sigstore (esp. for testing) easier for e2e/integration testing.

sigstore-scaffolding This repository contains scaffolding to make standing up a full sigstore stack easier and automatable. Our focus is on running on

Dec 27, 2022