Skip to content

Commit

Permalink
add support for pre-existing secrets for CTLog. (#429)
Browse files Browse the repository at this point in the history
* add support for pre-existing secrets for CTLog.

Signed-off-by: Ville Aikas <[email protected]>

* use version 1.7.x

Signed-off-by: Ville Aikas <[email protected]>

* install knative 1.7.x not latest.

Signed-off-by: Ville Aikas <[email protected]>

Signed-off-by: Ville Aikas <[email protected]>
  • Loading branch information
vaikas authored Oct 18, 2022
1 parent 43e3597 commit 38c958a
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 18 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/fulcio-rekor-kind.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ jobs:
- name: Setup Knative
uses: chainguard-dev/actions/setup-knative@main
with:
version: "latest"
version: "1.7.x"
serving-features: >
{
"kubernetes.podspec-fieldref": "enabled"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ jobs:
- name: Setup Knative
uses: chainguard-dev/actions/setup-knative@main
with:
version: "latest"
version: "1.7.x"
serving-features: >
{
"kubernetes.podspec-fieldref": "enabled"
Expand Down
34 changes: 33 additions & 1 deletion cmd/ctlog/createctconfig/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"errors"
"flag"
"fmt"
"log"
Expand All @@ -37,6 +38,7 @@ import (
"google.golang.org/protobuf/types/known/anypb"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
v1 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest"
"knative.dev/pkg/logging"
"knative.dev/pkg/signals"
Expand All @@ -55,6 +57,7 @@ const (
var (
cmname = flag.String("configmap", "ctlog-config", "Name of the configmap where the treeID lives")
configInSecret = flag.Bool("config-in-secret", false, "If set to true, create the ctlog configuration proto into a secret specified in ctlog-secrets under key 'config'")
privateKeySecret = flag.String("private-secret", "", "If there's an existing private key that should be used, read it from this secret, decrypt with the key-password and use it instead of creating a new one.")
secretName = flag.String("secret", "ctlog-secrets", "Name of the secret to create for the keyfiles")
pubKeySecretName = flag.String("pubkeysecret", "ctlog-public-key", "Name of the secret to create containing only the public key")
ctlogPrefix = flag.String("log-prefix", "sigstorescaffolding", "Prefix to append to the url. This is basically the name of the log.")
Expand Down Expand Up @@ -150,7 +153,16 @@ func main() {
if existingSecret.Data[privateKey] == nil ||
existingSecret.Data[publicKey] == nil ||
(existingSecret.Data[configKey] == nil && existingCMConfig == nil) {
ctlogConfig, err := createConfigWithKeys(ctx, *keyType)
var ctlogConfig *ctlog.CTLogConfig
var err error
if *privateKeySecret != "" {
// We have an existing private key, use it instead of creating
// a new one.
ctlogConfig, err = createConfigFromExistingSecret(ctx, nsSecret, *privateKeySecret)
} else {
// Create a fresh private key.
ctlogConfig, err = createConfigWithKeys(ctx, *keyType)
}
if err != nil {
logging.FromContext(ctx).Fatalf("Failed to generate keys: %v", err)
}
Expand Down Expand Up @@ -248,3 +260,23 @@ func createConfigWithKeys(ctx context.Context, keytype string) (*ctlog.CTLogConf
PubKey: signer.Public(),
}, nil
}

// create
func createConfigFromExistingSecret(ctx context.Context, nsSecret v1.SecretInterface, secretName string) (*ctlog.CTLogConfig, error) {
existingSecret, err := nsSecret.Get(ctx, secretName, metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("getting an existing private key secret: %w", err)
}
private := existingSecret.Data[privateKey]
if private == nil || len(private) == 0 {
return nil, errors.New("secret missing private key entry")
}
priv, pub, err := ctlog.DecryptExistingPrivateKey(private, *keyPassword)
if err != nil {
return nil, fmt.Errorf("decrypting existing private key secret: %w", err)
}
return &ctlog.CTLogConfig{
PrivKey: priv,
PubKey: pub,
}, nil
}
47 changes: 33 additions & 14 deletions pkg/ctlog/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"crypto/rand"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"strings"

Expand Down Expand Up @@ -216,24 +217,12 @@ func Unmarshal(ctx context.Context, in map[string][]byte) (*CTLogConfig, error)
return nil, fmt.Errorf("Not a valid PEMKeyFile in proto")
}

privPEM, _ := pem.Decode(private)
if privPEM == nil {
return nil, fmt.Errorf("did not find valid private PEM data")
}
ret.PrivKeyPassword = pb.Password

privatePEMBlock, err := x509.DecryptPEMBlock(privPEM, []byte(pb.Password))
ret.PrivKey, _, err = DecryptExistingPrivateKey(private, ret.PrivKeyPassword)
if err != nil {
return nil, fmt.Errorf("failed to decrypt private PEMKeyFile: %w", err)
}

if ret.PrivKey, err = x509.ParsePKCS8PrivateKey(privatePEMBlock); err != nil {
// Try it as RSA
if ret.PrivKey, err = x509.ParsePKCS1PrivateKey(privatePEMBlock); err != nil {
return nil, fmt.Errorf("failed to parse private key PEM: %w", err)
}
return nil, fmt.Errorf("decrypting existing private key: %w", err)
}

// If there's legacy rootCA entry, check it first.
if legacyRoot, ok := in[LegacyRootCAKey]; ok && len(legacyRoot) > 0 {
ret.FulcioCerts = append(ret.FulcioCerts, legacyRoot)
Expand Down Expand Up @@ -375,3 +364,33 @@ func mustMarshalAny(pb proto.Message) *anypb.Any {
}
return ret
}

// DecryptExistingPrivateKey reads in an encrypted private key, decrypts with
// the given password, and returns private, public keys for it.
func DecryptExistingPrivateKey(privateKey []byte, password string) (crypto.PrivateKey, crypto.PublicKey, error) {
privPEM, _ := pem.Decode(privateKey)
if privPEM == nil {
return nil, nil, fmt.Errorf("did not find valid private PEM data")
}
privatePEMBlock, err := x509.DecryptPEMBlock(privPEM, []byte(password))
if err != nil {
return nil, nil, fmt.Errorf("failed to decrypt private PEMKeyFile: %w", err)
}

var priv crypto.PrivateKey
if priv, err = x509.ParsePKCS8PrivateKey(privatePEMBlock); err != nil {
// Try it as RSA
if priv, err = x509.ParsePKCS1PrivateKey(privatePEMBlock); err != nil {
if priv, err = x509.ParseECPrivateKey(privatePEMBlock); err != nil {
return nil, nil, fmt.Errorf("failed to parse private key PEM: %w", err)
}
}
}
var ok bool
var signer crypto.Signer
if signer, ok = priv.(crypto.Signer); !ok {
return nil, nil, errors.New("failed to convert private key to Signer")
}

return priv, signer.Public(), nil
}
36 changes: 35 additions & 1 deletion pkg/ctlog/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,23 @@ KTkomoSY/OxE/5doBCACehThH+96joWfgC0rXi9qAwZ6hwIMJAKy
privateKeyEncodedRSA = "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpQcm9jLVR5cGU6IDQsRU5DUllQVEVECkRFSy1JbmZvOiBBRVMtMjU2LUNCQyw3NWUxNTkxNzQ0NTc4MjMwNGUzYjY1NGQ5NjhjY2M4MAoKV1pPQ1QrQXlaUmlaaFpDdXMveGxuR2dFbzNwTk1GRSsra0YvWVdBZUxMQjhmclNuL2NlL3VjbURuOURGQ01VZApORlNhSks1YzNvWEJCckt0Uk1sQ0I2S2RGblJucHNpVHUzbU1sVzVPdzRNTVh0L3JJaEFXbDFDaUFYUkdqL0NWClg4clRvQldpOFN4dXh3aWgrOHlrY0VpaVg3Ti9aWkNYOUppbjFQeTc0QUczWHBPT28rbFhwKzRTN1BwQmlZbzAKU0pzaUZ4Mlk0LzF4RXBWMEVWdmZobmN1R0k1R0ROcm0wUnBBNnNraGRSbU5iMW1HYkR5ZXdnMndPTTJTRHRGQwpSWEE5aFAxV1czUWx0VGhXRml2VTU0SngrYktMc3Fnem9JMzNZRmRFdnRPNmNxWCtoOVprN1pORmxaMDNaREk4Ck5RdzEyT3Z3VnpEeE5XdmFYVFhIMEpJc2tUSTE5cjFCTnB6aW1xdWg4ZWRYSTFuT2ppbUM5VjlRQTF0TVNmWmkKVmM2RW9VSG55N0xNVXkydG1yN3R2M2pLRWJHT09nclNRcXhJejAxcjFtV0dpREU2YkNDeFFueUhOUHExQmlIRQp1WTR3K25iU2V5UDhVc3h6YjlVNkRSd2IxVzZkMjlmbGNsdFp1TFlqdEhRL1JwRUdxbWRNc1RmRU1wRUVTNU9jClJPVmtsQlpQM0NHN3I4NGN0aVBMUGpvZnk0aG4rai9SeTBtT2tzcFcyVjNlQ2FvdGQwU0lQZFhxT3h6K2p3U1kKaDRBelg1VHdMSlg2UDlSaVdVZ2xQUWZKNjhCclpOT1Ywc3IwaEIwc1NXY25mSWorWWxSSzMvUXJTZGdhellRRQo0ZHBrK0hDUUE4bkdwN1M1Uks4ZGdxek1QYS96Z1AvR1dnN0t5K0dVWFB3cXRhalBFd1ZVWFJPNGViWUJCQ1RwClFHYnRSSmdRRjFzSmtqN1F0d0J4NzVoM25ZSjlWdEhiMWR2d2FKL09mWklhSklKQkRROVlyRGtqMjVmdDdtWlEKZVlGN1c5NlhCU0xHc2ZhdzlDMXhNRXZVY081UGtkS3ArR3pvMFhUaXhNb1U1Q0h6Yk0rQnFqMFZycGpNV29XbQphbHZpYVc4RlNYQkZQZUNoNFIrOXhwN1Q3ZWl6OU9uRFpKRVdnR1B1YXZyN29XL0t6blE1RS9SVlJtRllaZVY2CkluRXlmUVlRVE5QMnVBWjdibFRCeEc0VlhWdjA4ZUhWVHJ4YkVBcmE4VXJrZkQ1Nm02U3M3YWsrYU1mdG0vSnkKZHBxbTJ5YWlpSDd1SmRiZ1hyNTBnNEFDUThtZlE1QjNpbk1Ea0NFZ2RyQTRTQXg1YXNaQjJ0V2l1VC9SZFVSLworMUpXbjNKdXBEL2dhWU5CTVBTRzhjL0hKa0xmeE5UdzZVaHBBTlg5TkErTlE1UVdCUTVaaWNhbUNLQWJUczEvCjhUUlJlbnBLdUdhZXVsazhneVNOTm5xa0plZUNlZ1c5RGR1d3BZcUpjVkJ3L3lrY3BDc0hleVVZSTFOZkd0dCsKcTJ0Z2h0WGhaSGpFV1ZhcWVIb0JOTHlxZ0NET0l6U1QyTnFSeC9yYXhXckl1K0JwMTJTazNpQm5pc1Y0cE02NQprMnFaTDVhY2FDb3lIWTlSWStKSThYdHBzcHVjclViZnp6K0F3ZVZpdkcwN0hkOWRnV0dMRHRwMDJ4VGFMb09pCnp1NnV1dU9heUtZaUI4N2RBYlJlZUY0RVNrTlZOM3k4c1hIS3lnRlFvN2pqRExWVnBwRVVYWC8vN081VU9aZ0wKMWtWcVJ4K2hLeTQvTnVqQUVReWJubnMzRlpIMHBDMDQvcnAwS2xBeHlmRzBRNWJvTWdBeUR0VGlyUFBzK2lwTQpveDh1aWdlQlFaTmZyWW41TVA2UWVUSWY0QWx4NWNzSktxb1Nzb2dZclljbWhoSkhkc1Q4QUpidlpXSUo4L01JCjRFKzJ6UEZSNUlOYzNGbjVoVFpnRzNMQjh4N0ErTHlCbEdNR2owdW9melVzdnZMNnpxeEtqZ3F1Qm5DbTNmTHYKSjFnaDFYbkUyeENVekZhSlpQOVVNU1N2bmVmci92TzBFMjFxL0NlSGRUNWZsaUl4UjBZQ0t5MENvd3ZIeUdyYwpmc2JWWS92dGhIcUxLYmx0Vkh0bndPOExFTmhWZmVweGhFUy9sQUZrWmgrbmZFYjVsUnRZb2hZSW9RUkFOR1A0CmhCS1BhWldua0kwbFl5TmJNU1h3d3U0R2lScFdUUjhUYW84WDlXSWlJdmgyc3hHd0NleTBPSGZCVGtoYnR3Y2sKQzlaT0pERW9SNXBlOGZXSitzWXBia1laYjd3TzhSVEMyNlBGZTRQdEtKRFNGWXlOMzM2T1ZVdzM2RkZmVzR0QQpvcGtBdkRVbDdXVEZ1TlB4RVZ3SXZQSnN2ZDdnaG9Kdm1MYm4xQldQTS8wY0lobkQ0YkdrbjBsVURTTUFjUXIvCkV2R0h4Z2xpeU4wdktnOWU5SE9VNkVOYVdMaTRzemhwdE05RzF6UnBic01CV05zRW5TTEEwL3BaS01TOXdGdk8KL1N2VEVFc3dlM2xKWjV3WFc2R3lUdURFMzQ4Z011UFk4RmpCajZjQVo3RUJLTmYrWG1TY3VQTHYxVzd3Nm52cApKTGtQRS8wQmswdEZWRndlZUlERHJOTEg4Z0dseTY3MHk5cUxQSi8rMUhwdXpwR2tqc3RwWEs2QkRqWXUzeEFlCkhsd3E2RDNmRTMrZ0VkcW5RUmhZeHRacWxqaGIydFIxYUErZndhcWVBT2dNOG43RkNaY0gvK0ZBakdhRis3YjEKQ0RIdjA0cktKdVFGZjZTKzNzQktaVW9aVllJakxidE9VWko4c2QvZEZaQ01mNGhnN3RiaXNQeVFxMjQ2MUI3Wgp1SnFidlozdHhiT0lpd3k0cklCT2VtTnJaR3ArYmMzT3FuOHZQaEtpM3c2aDd4M2lvUzBxS250bStMbG11MXBqCnZOZnQwNmFZYklGcUhkY3ZqQ1AxajZNemY1Rm9TMGhmVnlpRmltOVFUOVpGeDl4bDNGeHBkK2VsYkxYY09pM0YKU2dISWE5SUdYQXNsSmo5dE5zdC9GaHBxeFdQbmt5c3dNTjRCQkJ2SDJNZU5odWpVUGdWblp5bEVodU1jQTBrcgpzdWMrNmliMEdRYUhRSW1pOHpmQ1FyQUVXMzZ2WWRxK1M0ZjBOeEZVNFZkclFzd2tpYlJhSytBTkZGY1ZKUzFJClcxWFdoU0FKV2VPUjJONmxJVFNqZVNDbXc5bnlXb1prZXBvSEkrcTlDZ0cvV09qRy9ZUkdjZUJNSFZQbk1zNDYKanA1NitvQkdXSUVpK3dvRU51UFV0aDNlZnZNT2dGTlBGZWh1QUFUUHBOeGtaMlBheHpRVmp6NXJGR09tNmJtZgoyWExIQVZxcTFjYVhEY1RidGxoSWh0Q3A5cmlGcXIzc2R6YlFxWThCWUsyQjdyQ0JHbXFjZld0akgvWUZadkNrCnFWNWpoOHQ2MFp2Z2F1bU15Y2h2NGNVaFRWMFJzZ1BteE9GMzdUenY1T0d3OVBKeS9sdFphNncveFFZQWVMaHQKRnVWN0I0WFJvdERyYklvZkNNM1ZObXdXTnN4R29LNWY1LzV6bVBEQ1JQNjZDNkkwbWVLNjZXb3prY0N2NTRMcQpJZDJaZTN5aUY2bjE0K05xZUZMWGVsdnRvay9RSWdiTEd3ME9XVEQyaFJtZGVYMjhUMEVMMW5kZ0ZUYU0xV3NlCkVJdXQxWXNLWXk1Vml2bDg1V0JiZEsvKzZuMjVIa2l3SGV5bHRsOWZ1cFEwSlcyM01yc1I2RWwybU1qQ0FFTEwKQ0l4TjdrOGFRTk92SndmV25LWjQ0U3BIalFPUXdtTTJySlVpZzBhZURUMWNMck9sVDNSVndUeG5DK00rN2V6SwpTZElza0ZZR0ZXdW12NlBZSVZBMy9MOE16T3dWeGs1WWwzcnpJaVh4UGlrdU1FeEtqNlRsNU8rQjBXQ2c0UVVUCjFGdk1zZksxNUwrRjdaeExuVi96WTVmQ2VBUEY2dXZDYjJ4VFBBeGZwN0VxK0tsSEdybzBWb1UwSGRSNFJLR2YKZlg0TytkZ3NNUHB1K1lQWTBWVGZTVjdVN2dWdklPcHhzc2lQbXQwdmRLSjJLK04xWUV5TmdKVlBCNUtyVXZveQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo="

publicKeyEncodedRSA = "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUNDZ0tDQWdFQXVhTkpwYmdWT2VSQ2g4TmFGaEVBYytTZGplKzhPV1NLdGxPZnFFd1ExWVlGQytVTVRjNHkKdHZDQWxRRjNvRURTdDBhR0tvRkVyamY5MldzeXZITis0WG5ZRnZZSkVnM0ttd29OR3BmbXN1ckErcnhscFhDSQpsNG8zLy9IT2RoemZDc2l3TGR6VWUvbCtFQ1NXdkRnZzRoN0MwdlBIYVE0dGpyYUVMZ1VYSmVTaEQ4ek9qVlZXCk9SaEVKYWVEVHlKNnArZGtkS0ZFVEt6bmVuSnNFTmRBbmwzd1pORlNYbDg1L2FjL05DRDNFMXpQNmNkc0F5bnAKWTlzQk9ER1pmZTh5ajBWVFNQOW4rTXRKdWJtRG9xYlFBV09IUDNNRCs0ZnBTTnA1QS9Oa1hicDdneHkzWEtZRwpQUlBrNjlQZUVhZFdYMnRRaThDTi9QcG51TUJtUzdRdFljMVQ3RzZLZ1V4ZEJBQ3Y0Vm5VbHc5aUlRTzZPWFlsCkRJUHhHYXc2QzNUOGhsR3l6UEM3TWU2cjRFUURuQzNkYXFTSU1sbFFWSkFTakVSNWRNMXZjRFVmZWYxMnh4YXcKQkRFTmRFTVpOeVFRMHVFZGtFVVZLZTZwaTh6dE9uV2c1QkRVWWxuOEJNT3pKczZCVWQxR1VFWHhhenBhdHB0dAphaFhYVjhYZTUwSHloOXNWNEVDUENZUFpsb0JGU1IybHJKTExabnFDaFROM2s0SHV4R3Uydmxsc2xCbzZZT1R5CkRQQ2dJMys2TDFyUU9uM2pBZ3RWVy9ZdDAxS2RDK0tSaE5tU2w1eFVmL0ZZc091c3J4bU1oL2JnR2sxZldtZmMKbC9KNFBhRlNiS3VkWXZIeFp6MVdqMm5zZitQNEg5c2JZdUJya2RmSFhnQVVOTk5YNjYxVjFkOENBd0VBQVE9PQotLS0tLUVORCBSU0EgUFVCTElDIEtFWS0tLS0tCg=="

// Testing importing an existing key that's been added to TUF already.
// Generated with:
// openssl ecparam -name prime256v1 -genkey -noout -out privkey.pem
// openssl ec -in privkey.pem -pubout -out pubkey.pem
// openssl ec -in privkey.pem -out privatekey_encrypted.pem -aes256
// And encrypted with this supersecretpassword
existingEncryptedPrivateKeyPassword = "supersecretpassword"
existingEncryptedPrivateKey = `
-----BEGIN EC PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,3C33CA88DF439D434ABDB2DD03491BEC
A9UPVwTxy82/vDcG9q/e5SDKYokAGYvMyS5KD9rfyS5RGGQDdpkQPK0q6v9AFJbn
VCphFSJvnjFAR90XgF2EK+fVpX2GQjFEPhODVzAmqjawZHfTeGeMU5cJ+nNW+O6A
71ay3pGMAEQAvrzEErTLzCsBf2HZV1ioeFZVwHysvAA=
-----END EC PRIVATE KEY-----`
)

// testConfig wraps the private,public, and config into a single struct
Expand Down Expand Up @@ -138,7 +155,11 @@ func TestRoundTrip(t *testing.T) {
if err != nil {
t.Fatalf("Failed to generate Private Key: %v", err)
}
for k, v := range map[string]crypto.PrivateKey{"rsa": privateKeyRSA, "ecdsa": privateKeyECDSA} {
privateKeyEC, _, err := DecryptExistingPrivateKey([]byte(existingEncryptedPrivateKey), existingEncryptedPrivateKeyPassword)
if err != nil {
t.Fatalf("Failed to parse encrypted Private Key: %v", err)
}
for k, v := range map[string]crypto.PrivateKey{"rsa": privateKeyRSA, "ecdsa": privateKeyECDSA, "ec": privateKeyEC} {
t.Logf("testing with %s", k)
var ok bool
var signer crypto.Signer
Expand Down Expand Up @@ -318,3 +339,16 @@ func validateFulcioEntries(ctx context.Context, config map[string][]byte, fulcio
}
}
}

func TestDecrypteExistingPrivateKey(t *testing.T) {
priv, pub, err := DecryptExistingPrivateKey([]byte(existingEncryptedPrivateKey), existingEncryptedPrivateKeyPassword)
if err != nil {
t.Fatalf("Failed to decrypt existing private key %v", err)
}
if priv == nil {
t.Fatalf("got back a nil private key")
}
if pub == nil {
t.Fatalf("got back a nil public key")
}
}

0 comments on commit 38c958a

Please sign in to comment.