Skip to content

Commit

Permalink
Infra flags for fulcio / rekor / oidc values (sigstore#462)
Browse files Browse the repository at this point in the history
* Allow customer infra values

This change allows users to specify their own values for:

* Fulcio
* Rekor
* OIDC Issuer
* OIDC Client ID
* OIDC Secret

All flags are marked as experimental

Signed-off-by: Luke Hinds <lhinds@redhat.com>

* Also include copasetic

Signed-off-by: Luke Hinds <lhinds@redhat.com>

* Fix e2e tests by setting rekor env

Signed-off-by: Luke Hinds <lhinds@redhat.com>

* Fix e2e tests by setting rekor env

Signed-off-by: Luke Hinds <lhinds@redhat.com>

* Name all as rekor-url

Signed-off-by: Luke Hinds <lhinds@redhat.com>

* Fix passing args

Signed-off-by: Luke Hinds <lhinds@redhat.com>

* Resolve failing tests

Signed-off-by: Luke Hinds <lhinds@redhat.com>
  • Loading branch information
lukehinds authored Jul 22, 2021
1 parent 647606b commit 7068357
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 80 deletions.
49 changes: 29 additions & 20 deletions cmd/cosign/cli/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,22 @@ func (a *annotationsMap) String() string {

func Sign() *ffcli.Command {
var (
flagset = flag.NewFlagSet("cosign sign", flag.ExitOnError)
key = flagset.String("key", "", "path to the private key file, KMS URI or Kubernetes Secret")
cert = flagset.String("cert", "", "Path to the x509 certificate to include in the Signature")
upload = flagset.Bool("upload", true, "whether to upload the signature")
sk = flagset.Bool("sk", false, "whether to use a hardware security key")
slot = flagset.String("slot", "", "security key slot to use for generated key (default: signature) (authentication|signature|card-authentication|key-management)")
payloadPath = flagset.String("payload", "", "path to a payload file to use rather than generating one.")
force = flagset.Bool("f", false, "skip warnings and confirmations")
recursive = flagset.Bool("r", false, "if a multi-arch image is specified, additionally sign each discrete image")
idToken = flagset.String("identity-token", "", "[EXPERIMENTAL] identity token to use for certificate from fulcio")
annotations = annotationsMap{}
flagset = flag.NewFlagSet("cosign sign", flag.ExitOnError)
key = flagset.String("key", "", "path to the private key file, KMS URI or Kubernetes Secret")
cert = flagset.String("cert", "", "Path to the x509 certificate to include in the Signature")
upload = flagset.Bool("upload", true, "whether to upload the signature")
sk = flagset.Bool("sk", false, "whether to use a hardware security key")
slot = flagset.String("slot", "", "security key slot to use for generated key (default: signature) (authentication|signature|card-authentication|key-management)")
payloadPath = flagset.String("payload", "", "path to a payload file to use rather than generating one.")
force = flagset.Bool("f", false, "skip warnings and confirmations")
recursive = flagset.Bool("r", false, "if a multi-arch image is specified, additionally sign each discrete image")
fulcioURL = flagset.String("fulcio-server", "https://fulcio.sigstore.dev", "[EXPERIMENTAL] address of sigstore PKI server")
rekorURL = flagset.String("rekor-url", "https://rekor.sigstore.dev", "[EXPERIMENTAL] address of rekor STL server")
idToken = flagset.String("identity-token", "", "[EXPERIMENTAL] identity token to use for certificate from fulcio")
oidcIssuer = flagset.String("oidc-issuer", "https://oauth2.sigstore.dev/auth", "[EXPERIMENTAL] OIDC provider to be used to issue ID token")
oidcClientID = flagset.String("oidc-client-id", "sigstore", "[EXPERIMENTAL] OIDC client ID for application")
oidcClientSecret = flagset.String("oidc-client-secret", "", "[EXPERIMENTAL] OIDC client secret for application")
annotations = annotationsMap{}
)
flagset.Var(&annotations, "a", "extra key=value pairs to sign")
return &ffcli.Command{
Expand Down Expand Up @@ -133,11 +138,16 @@ EXAMPLES
}

ko := KeyOpts{
KeyRef: *key,
PassFunc: GetPass,
Sk: *sk,
Slot: *slot,
IDToken: *idToken,
KeyRef: *key,
PassFunc: GetPass,
Sk: *sk,
Slot: *slot,
FulcioURL: *fulcioURL,
RekorURL: *rekorURL,
IDToken: *idToken,
OIDCIssuer: *oidcIssuer,
OIDCClientID: *oidcClientID,
OIDCClientSecret: *oidcClientSecret,
}
for _, img := range args {
if err := SignCmd(ctx, ko, annotations.annotations, img, *cert, *upload, *payloadPath, *force, *recursive); err != nil {
Expand Down Expand Up @@ -186,7 +196,6 @@ func getTransitiveImages(rootIndex *remote.Descriptor, repo name.Repository, opt

func SignCmd(ctx context.Context, ko KeyOpts, annotations map[string]interface{},
imageRef string, certPath string, upload bool, payloadPath string, force bool, recursive bool) error {

// A key file or token is required unless we're in experimental mode!
if EnableExperimental() {
if nOf(ko.KeyRef, ko.Sk) > 1 {
Expand Down Expand Up @@ -233,7 +242,7 @@ func SignCmd(ctx context.Context, ko KeyOpts, annotations map[string]interface{}
uploadTLog := EnableExperimental()
if uploadTLog && !force {
if _, err := remote.Get(ref); err != nil {
fmt.Printf("warning: uploading to the transparency log at %s for a private image, please confirm [Y/N]: ", TlogServer())
fmt.Printf("warning: uploading to the transparency log at %s for a private image, please confirm [Y/N]: ", ko.RekorURL)

var tlogConfirmResponse string
if _, err := fmt.Scanln(&tlogConfirmResponse); err != nil {
Expand Down Expand Up @@ -316,7 +325,7 @@ func SignCmd(ctx context.Context, ko KeyOpts, annotations map[string]interface{}
}

if uploadTLog {
rekorClient, err := rekorClient.GetRekorClient(TlogServer())
rekorClient, err := rekorClient.GetRekorClient(ko.RekorURL)
if err != nil {
return err
}
Expand Down Expand Up @@ -450,7 +459,7 @@ func signerFromKeyOpts(ctx context.Context, certPath string, ko KeyOpts) (*certS
}
// Default Keyless!
fmt.Fprintln(os.Stderr, "Generating ephemeral keys...")
k, err := fulcio.NewSigner(ctx, ko.IDToken)
k, err := fulcio.NewSigner(ctx, ko.IDToken, ko.OIDCIssuer, ko.OIDCClientID, ko.FulcioURL)
if err != nil {
return nil, errors.Wrap(err, "getting key from Fulcio")
}
Expand Down
52 changes: 34 additions & 18 deletions cmd/cosign/cli/sign_blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,18 @@ import (

func SignBlob() *ffcli.Command {
var (
flagset = flag.NewFlagSet("cosign sign-blob", flag.ExitOnError)
key = flagset.String("key", "", "path to the private key file or a KMS URI")
b64 = flagset.Bool("b64", true, "whether to base64 encode the output")
sk = flagset.Bool("sk", false, "whether to use a hardware security key")
slot = flagset.String("slot", "", "security key slot to use for generated key (default: signature) (authentication|signature|card-authentication|key-management)")
idToken = flagset.String("identity-token", "", "[EXPERIMENTAL] identity token to use for certificate from fulcio")
output = flagset.String("output", "", "write the signature to FILE")
flagset = flag.NewFlagSet("cosign sign-blob", flag.ExitOnError)
key = flagset.String("key", "", "path to the private key file or a KMS URI")
b64 = flagset.Bool("b64", true, "whether to base64 encode the output")
sk = flagset.Bool("sk", false, "whether to use a hardware security key")
slot = flagset.String("slot", "", "security key slot to use for generated key (default: signature) (authentication|signature|card-authentication|key-management)")
fulcioURL = flagset.String("fulcio-server", "https://fulcio.sigstore.dev", "[EXPERIMENTAL] address of sigstore PKI server")
rekorURL = flagset.String("rekor-url", "https://rekor.sigstore.dev", "[EXPERIMENTAL] address of rekor STL server")
idToken = flagset.String("identity-token", "", "[EXPERIMENTAL] identity token to use for certificate from fulcio")
oidcIssuer = flagset.String("oidc-issuer", "https://oauth2.sigstore.dev/auth", "[EXPERIMENTAL] OIDC provider to be used to issue ID token")
oidcClientID = flagset.String("oidc-client-id", "sigstore", "[EXPERIMENTAL] OIDC client ID for application")
oidcClientSecret = flagset.String("oidc-client-secret", "", "[EXPERIMENTAL] OIDC client secret for application")
output = flagset.String("output", "", "write the signature to FILE")
)
return &ffcli.Command{
Name: "sign-blob",
Expand Down Expand Up @@ -81,11 +86,16 @@ EXAMPLES
return flag.ErrHelp
}
ko := KeyOpts{
KeyRef: *key,
Sk: *sk,
Slot: *slot,
PassFunc: GetPass,
IDToken: *idToken,
KeyRef: *key,
Sk: *sk,
Slot: *slot,
PassFunc: GetPass,
FulcioURL: *fulcioURL,
RekorURL: *rekorURL,
IDToken: *idToken,
OIDCIssuer: *oidcIssuer,
OIDCClientID: *oidcClientID,
OIDCClientSecret: *oidcClientSecret,
}
for _, blob := range args {
if _, err := SignBlobCmd(ctx, ko, blob, *b64, *output); err != nil {
Expand All @@ -98,16 +108,22 @@ EXAMPLES
}

type KeyOpts struct {
Sk bool
Slot string
KeyRef string
IDToken string
PassFunc cosign.PassFunc
Sk bool
Slot string
KeyRef string
FulcioURL string
RekorURL string
IDToken string
PassFunc cosign.PassFunc
OIDCIssuer string
OIDCClientID string
OIDCClientSecret string
}

func SignBlobCmd(ctx context.Context, ko KeyOpts, payloadPath string, b64 bool, output string) ([]byte, error) {
var payload []byte
var err error

if payloadPath == "-" {
payload, err = ioutil.ReadAll(os.Stdin)
} else {
Expand Down Expand Up @@ -140,7 +156,7 @@ func SignBlobCmd(ctx context.Context, ko KeyOpts, payloadPath string, b64 bool,
}
rekorBytes = pemBytes
}
rekorClient, err := rekorClient.GetRekorClient(TlogServer())
rekorClient, err := rekorClient.GetRekorClient(ko.RekorURL)
if err != nil {
return nil, err
}
Expand Down
10 changes: 0 additions & 10 deletions cmd/cosign/cli/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ import (

const (
ExperimentalEnv = "COSIGN_EXPERIMENTAL"
ServerEnv = "REKOR_SERVER"
rekorServer = "https://rekor.sigstore.dev"
repoEnv = "COSIGN_REPOSITORY"
)

Expand All @@ -45,14 +43,6 @@ func EnableExperimental() bool {
return false
}

// TlogServer returns the name of the tlog server, can be overwritten via env var
func TlogServer() string {
if s := os.Getenv(ServerEnv); s != "" {
return s
}
return rekorServer
}

func TargetRepositoryForImage(img name.Reference) (name.Repository, error) {
wantRepo := os.Getenv(repoEnv)
if wantRepo == "" {
Expand Down
5 changes: 4 additions & 1 deletion cmd/cosign/cli/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type VerifyCommand struct {
Sk bool
Slot string
Output string
RekorURL string
Annotations *map[string]interface{}
}

Expand All @@ -50,6 +51,7 @@ func applyVerifyFlags(cmd *VerifyCommand, flagset *flag.FlagSet) {
flagset.StringVar(&cmd.KeyRef, "key", "", "path to the public key file, URL, KMS URI or Kubernetes Secret")
flagset.BoolVar(&cmd.Sk, "sk", false, "whether to use a hardware security key")
flagset.StringVar(&cmd.Slot, "slot", "", "security key slot to use for generated key (default: signature) (authentication|signature|card-authentication|key-management)")
flagset.StringVar(&cmd.RekorURL, "rekor-url", "https://rekor.sigstore.dev", "address of rekor STL server")
flagset.BoolVar(&cmd.CheckClaims, "check-claims", true, "whether to check the claims found")
flagset.StringVar(&cmd.Output, "output", "json", "output the signing image information. Default JSON.")

Expand Down Expand Up @@ -124,7 +126,8 @@ func (c *VerifyCommand) Exec(ctx context.Context, args []string) (err error) {
co.ClaimVerifier = cosign.SimpleClaimVerifier
}
if EnableExperimental() {
co.RekorURL = TlogServer()
co.RekorURL = c.RekorURL

}
keyRef := c.KeyRef

Expand Down
10 changes: 6 additions & 4 deletions cmd/cosign/cli/verify_blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ func VerifyBlob() *ffcli.Command {
key = flagset.String("key", "", "path to the public key file, URL, or KMS URI")
sk = flagset.Bool("sk", false, "whether to use a hardware security key")
slot = flagset.String("slot", "", "security key slot to use for generated key (default: signature) (authentication|signature|card-authentication|key-management)")
rekorURL = flagset.String("rekor-url", "https://rekor.sigstore.dev", "[EXPERIMENTAL] address of rekor STL server")
cert = flagset.String("cert", "", "path to the public certificate")
signature = flagset.String("signature", "", "path to the signature")
)
Expand Down Expand Up @@ -89,9 +90,10 @@ EXAMPLES
return flag.ErrHelp
}
ko := KeyOpts{
KeyRef: *key,
Sk: *sk,
Slot: *slot,
KeyRef: *key,
Sk: *sk,
RekorURL: *rekorURL,
Slot: *slot,
}
if err := VerifyBlobCmd(ctx, ko, *cert, *signature, args[0]); err != nil {
return errors.Wrapf(err, "verifying blob %s", args)
Expand Down Expand Up @@ -202,7 +204,7 @@ func VerifyBlobCmd(ctx context.Context, ko KeyOpts, certRef, sigRef, blobRef str
fmt.Fprintln(os.Stderr, "Verified OK")

if EnableExperimental() {
rekorClient, err := rekorClient.GetRekorClient(TlogServer())
rekorClient, err := rekorClient.GetRekorClient(ko.RekorURL)
if err != nil {
return err
}
Expand Down
5 changes: 4 additions & 1 deletion copasetic/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"bytes"
"context"
"encoding/json"
"flag"
"fmt"
"io"
"io/ioutil"
Expand All @@ -45,6 +46,8 @@ import (
)

func main() {
rekorURL := flag.String("rekor-url", "https://rekor.sigstore.dev", "[EXPERIMENTAL] address of rekor STL server")
flag.Parse()

rego.RegisterBuiltin2(
&rego.Function{
Expand Down Expand Up @@ -196,7 +199,7 @@ func main() {
remote.WithAuthFromKeychain(authn.DefaultKeychain),
remote.WithContext(bctx.Context),
},
RekorURL: cli.TlogServer(),
RekorURL: *rekorURL,
SignatureRepo: sigRepo,
}
sps, err := cosign.Verify(bctx.Context, ref, co)
Expand Down
28 changes: 8 additions & 20 deletions pkg/cosign/fulcio/fulcio.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,6 @@ import (
)

const (
defaultFulcioAddress = "https://fulcio.sigstore.dev"
oauthAddress = "https://oauth2.sigstore.dev/auth"
clientID = "sigstore"

FlowNormal = "normal"
FlowDevice = "device"
FlowToken = "token"
Expand All @@ -56,14 +52,6 @@ const (
//go:embed fulcio.pem
var rootPem string

func fulcioServer() string {
addr := os.Getenv("FULCIO_ADDRESS")
if addr != "" {
return addr
}
return defaultFulcioAddress
}

type oidcConnector interface {
OIDConnect(string, string, string) (*oauthflow.OIDCIDToken, error)
}
Expand All @@ -80,13 +68,13 @@ type signingCertProvider interface {
SigningCert(params *operations.SigningCertParams, authInfo runtime.ClientAuthInfoWriter, opts ...operations.ClientOption) (*operations.SigningCertCreated, error)
}

func getCertForOauthID(priv *ecdsa.PrivateKey, scp signingCertProvider, connector oidcConnector) (string, string, error) {
func getCertForOauthID(priv *ecdsa.PrivateKey, scp signingCertProvider, connector oidcConnector, oidcIssuer string, oidcClientID string) (string, string, error) {
pubBytes, err := x509.MarshalPKIXPublicKey(&priv.PublicKey)
if err != nil {
return "", "", err
}

tok, err := connector.OIDConnect(oauthAddress, clientID, "")
tok, err := connector.OIDConnect(oidcIssuer, oidcClientID, "")
if err != nil {
return "", "", err
}
Expand Down Expand Up @@ -136,8 +124,8 @@ func getFulcioClient(addr string) (*fulcioClient.Fulcio, error) {
}

// GetCert returns the PEM-encoded signature of the OIDC identity returned as part of an interactive oauth2 flow plus the PEM-encoded cert chain.
func GetCert(ctx context.Context, priv *ecdsa.PrivateKey, idToken, flow string) (string, string, error) {
fcli, err := getFulcioClient(fulcioServer())
func GetCert(ctx context.Context, priv *ecdsa.PrivateKey, idToken, flow string, oidcIssuer string, oidcClientID string, fulcioClient string) (string, string, error) {
fcli, err := getFulcioClient(fulcioClient)
if err != nil {
return "", "", err
}
Expand All @@ -146,7 +134,7 @@ func GetCert(ctx context.Context, priv *ecdsa.PrivateKey, idToken, flow string)
switch flow {
case FlowDevice:
c.flow = oauthflow.NewDeviceFlowTokenGetter(
oauthAddress, oauthflow.SigstoreDeviceURL, oauthflow.SigstoreTokenURL)
oidcIssuer, oauthflow.SigstoreDeviceURL, oauthflow.SigstoreTokenURL)
case FlowNormal:
c.flow = oauthflow.DefaultIDTokenGetter
case FlowToken:
Expand All @@ -155,7 +143,7 @@ func GetCert(ctx context.Context, priv *ecdsa.PrivateKey, idToken, flow string)
return "", "", fmt.Errorf("unsupported oauth flow: %s", flow)
}

return getCertForOauthID(priv, fcli.Operations, c)
return getCertForOauthID(priv, fcli.Operations, c, oidcIssuer, oidcClientID)
}

type Signer struct {
Expand All @@ -165,7 +153,7 @@ type Signer struct {
*signature.ECDSASignerVerifier
}

func NewSigner(ctx context.Context, idToken string) (*Signer, error) {
func NewSigner(ctx context.Context, idToken, oidcIssuer, oidcClientID, fulcioClient string) (*Signer, error) {
priv, err := cosign.GeneratePrivateKey()
if err != nil {
return nil, errors.Wrap(err, "generating cert")
Expand All @@ -186,7 +174,7 @@ func NewSigner(ctx context.Context, idToken string) (*Signer, error) {
default:
flow = FlowNormal
}
cert, chain, err := GetCert(ctx, priv, idToken, flow) // TODO, use the chain.
cert, chain, err := GetCert(ctx, priv, idToken, flow, oidcIssuer, oidcClientID, fulcioClient) // TODO, use the chain.
if err != nil {
return nil, errors.Wrap(err, "retrieving cert")
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/cosign/fulcio/fulcio_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ func TestGetCertForOauthID(t *testing.T) {
err: tc.tokenGetterErr,
}

cert, chain, err := getCertForOauthID(testKey, tscp, &tf)
cert, chain, err := getCertForOauthID(testKey, tscp, &tf, "", "")

if err != nil {
if !tc.expectErr {
Expand Down
Loading

0 comments on commit 7068357

Please sign in to comment.