Remove file reading from bootstrap package

Signed-off-by: Philip Laine <philip.laine@gmail.com>
pull/3249/head
Philip Laine 2 years ago
parent 2c267c95e5
commit a4734d7e30

@ -212,19 +212,18 @@ func bootstrapBServerCmdRun(cmd *cobra.Command, args []string) error {
secretOpts.Username = bServerArgs.username secretOpts.Username = bServerArgs.username
} }
secretOpts.Password = bitbucketToken secretOpts.Password = bitbucketToken
secretOpts.CAFile = caBundle
if bootstrapArgs.caFile != "" {
secretOpts.CAFilePath = bootstrapArgs.caFile
}
} else { } else {
keypair, err := sourcesecret.LoadKeyPairFromPath(bootstrapArgs.privateKeyFile, gitArgs.password)
if err != nil {
return err
}
secretOpts.Keypair = keypair
secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(bootstrapArgs.keyAlgorithm) secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(bootstrapArgs.keyAlgorithm)
secretOpts.RSAKeyBits = int(bootstrapArgs.keyRSABits) secretOpts.RSAKeyBits = int(bootstrapArgs.keyRSABits)
secretOpts.ECDSACurve = bootstrapArgs.keyECDSACurve.Curve secretOpts.ECDSACurve = bootstrapArgs.keyECDSACurve.Curve
secretOpts.SSHHostname = bServerArgs.hostname
if bootstrapArgs.privateKeyFile != "" { secretOpts.SSHHostname = bServerArgs.hostname
secretOpts.PrivateKeyPath = bootstrapArgs.privateKeyFile
}
if bootstrapArgs.sshHostname != "" { if bootstrapArgs.sshHostname != "" {
secretOpts.SSHHostname = bootstrapArgs.sshHostname secretOpts.SSHHostname = bootstrapArgs.sshHostname
} }
@ -243,7 +242,13 @@ func bootstrapBServerCmdRun(cmd *cobra.Command, args []string) error {
RecurseSubmodules: bootstrapArgs.recurseSubmodules, RecurseSubmodules: bootstrapArgs.recurseSubmodules,
} }
entityList, err := bootstrap.LoadEntityListFromPath(bootstrapArgs.gpgKeyRingPath)
if err != nil {
return err
}
// Bootstrap config // Bootstrap config
bootstrapOpts := []bootstrap.GitProviderOption{ bootstrapOpts := []bootstrap.GitProviderOption{
bootstrap.WithProviderRepository(bServerArgs.owner, bServerArgs.repository, bServerArgs.personal), bootstrap.WithProviderRepository(bServerArgs.owner, bServerArgs.repository, bServerArgs.personal),
bootstrap.WithBranch(bootstrapArgs.branch), bootstrap.WithBranch(bootstrapArgs.branch),
@ -255,7 +260,7 @@ func bootstrapBServerCmdRun(cmd *cobra.Command, args []string) error {
bootstrap.WithKubeconfig(kubeconfigArgs, kubeclientOptions), bootstrap.WithKubeconfig(kubeconfigArgs, kubeclientOptions),
bootstrap.WithLogger(logger), bootstrap.WithLogger(logger),
bootstrap.WithCABundle(caBundle), bootstrap.WithCABundle(caBundle),
bootstrap.WithGitCommitSigning(bootstrapArgs.gpgKeyRingPath, bootstrapArgs.gpgPassphrase, bootstrapArgs.gpgKeyID), bootstrap.WithGitCommitSigning(entityList, bootstrapArgs.gpgPassphrase, bootstrapArgs.gpgKeyID),
} }
if bootstrapArgs.sshHostname != "" { if bootstrapArgs.sshHostname != "" {
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname)) bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname))

@ -169,6 +169,15 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
installOptions.BaseURL = customBaseURL installOptions.BaseURL = customBaseURL
} }
var caBundle []byte
if bootstrapArgs.caFile != "" {
var err error
caBundle, err = os.ReadFile(bootstrapArgs.caFile)
if err != nil {
return fmt.Errorf("unable to read TLS CA file: %w", err)
}
}
// Source generation and secret config // Source generation and secret config
secretOpts := sourcesecret.Options{ secretOpts := sourcesecret.Options{
Name: bootstrapArgs.secretName, Name: bootstrapArgs.secretName,
@ -179,10 +188,7 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
if bootstrapArgs.tokenAuth { if bootstrapArgs.tokenAuth {
secretOpts.Username = gitArgs.username secretOpts.Username = gitArgs.username
secretOpts.Password = gitArgs.password secretOpts.Password = gitArgs.password
secretOpts.CAFile = caBundle
if bootstrapArgs.caFile != "" {
secretOpts.CAFilePath = bootstrapArgs.caFile
}
// Remove port of the given host when not syncing over HTTP/S to not assume port for protocol // Remove port of the given host when not syncing over HTTP/S to not assume port for protocol
// This _might_ be overwritten later on by e.g. --ssh-hostname // This _might_ be overwritten later on by e.g. --ssh-hostname
@ -213,9 +219,12 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
if bootstrapArgs.sshHostname != "" { if bootstrapArgs.sshHostname != "" {
repositoryURL.Host = bootstrapArgs.sshHostname repositoryURL.Host = bootstrapArgs.sshHostname
} }
if bootstrapArgs.privateKeyFile != "" {
secretOpts.PrivateKeyPath = bootstrapArgs.privateKeyFile keypair, err := sourcesecret.LoadKeyPairFromPath(bootstrapArgs.privateKeyFile, gitArgs.password)
if err != nil {
return err
} }
secretOpts.Keypair = keypair
// Configure last as it depends on the config above. // Configure last as it depends on the config above.
secretOpts.SSHHostname = repositoryURL.Host secretOpts.SSHHostname = repositoryURL.Host
@ -235,13 +244,9 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
RecurseSubmodules: bootstrapArgs.recurseSubmodules, RecurseSubmodules: bootstrapArgs.recurseSubmodules,
} }
var caBundle []byte entityList, err := bootstrap.LoadEntityListFromPath(bootstrapArgs.gpgKeyRingPath)
if bootstrapArgs.caFile != "" { if err != nil {
var err error return err
caBundle, err = os.ReadFile(bootstrapArgs.caFile)
if err != nil {
return fmt.Errorf("unable to read TLS CA file: %w", err)
}
} }
// Bootstrap config // Bootstrap config
@ -254,7 +259,7 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
bootstrap.WithPostGenerateSecretFunc(promptPublicKey), bootstrap.WithPostGenerateSecretFunc(promptPublicKey),
bootstrap.WithLogger(logger), bootstrap.WithLogger(logger),
bootstrap.WithCABundle(caBundle), bootstrap.WithCABundle(caBundle),
bootstrap.WithGitCommitSigning(bootstrapArgs.gpgKeyRingPath, bootstrapArgs.gpgPassphrase, bootstrapArgs.gpgKeyID), bootstrap.WithGitCommitSigning(entityList, bootstrapArgs.gpgPassphrase, bootstrapArgs.gpgKeyID),
} }
// Setup bootstrapper with constructed configs // Setup bootstrapper with constructed configs

@ -204,16 +204,13 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
if bootstrapArgs.tokenAuth { if bootstrapArgs.tokenAuth {
secretOpts.Username = "git" secretOpts.Username = "git"
secretOpts.Password = ghToken secretOpts.Password = ghToken
secretOpts.CAFile = caBundle
if bootstrapArgs.caFile != "" {
secretOpts.CAFilePath = bootstrapArgs.caFile
}
} else { } else {
secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(bootstrapArgs.keyAlgorithm) secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(bootstrapArgs.keyAlgorithm)
secretOpts.RSAKeyBits = int(bootstrapArgs.keyRSABits) secretOpts.RSAKeyBits = int(bootstrapArgs.keyRSABits)
secretOpts.ECDSACurve = bootstrapArgs.keyECDSACurve.Curve secretOpts.ECDSACurve = bootstrapArgs.keyECDSACurve.Curve
secretOpts.SSHHostname = githubArgs.hostname
secretOpts.SSHHostname = githubArgs.hostname
if bootstrapArgs.sshHostname != "" { if bootstrapArgs.sshHostname != "" {
secretOpts.SSHHostname = bootstrapArgs.sshHostname secretOpts.SSHHostname = bootstrapArgs.sshHostname
} }
@ -232,6 +229,11 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
RecurseSubmodules: bootstrapArgs.recurseSubmodules, RecurseSubmodules: bootstrapArgs.recurseSubmodules,
} }
entityList, err := bootstrap.LoadEntityListFromPath(bootstrapArgs.gpgKeyRingPath)
if err != nil {
return err
}
// Bootstrap config // Bootstrap config
bootstrapOpts := []bootstrap.GitProviderOption{ bootstrapOpts := []bootstrap.GitProviderOption{
bootstrap.WithProviderRepository(githubArgs.owner, githubArgs.repository, githubArgs.personal), bootstrap.WithProviderRepository(githubArgs.owner, githubArgs.repository, githubArgs.personal),
@ -244,7 +246,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
bootstrap.WithKubeconfig(kubeconfigArgs, kubeclientOptions), bootstrap.WithKubeconfig(kubeconfigArgs, kubeclientOptions),
bootstrap.WithLogger(logger), bootstrap.WithLogger(logger),
bootstrap.WithCABundle(caBundle), bootstrap.WithCABundle(caBundle),
bootstrap.WithGitCommitSigning(bootstrapArgs.gpgKeyRingPath, bootstrapArgs.gpgPassphrase, bootstrapArgs.gpgKeyID), bootstrap.WithGitCommitSigning(entityList, bootstrapArgs.gpgPassphrase, bootstrapArgs.gpgKeyID),
} }
if bootstrapArgs.sshHostname != "" { if bootstrapArgs.sshHostname != "" {
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname)) bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname))

@ -215,19 +215,18 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
if bootstrapArgs.tokenAuth { if bootstrapArgs.tokenAuth {
secretOpts.Username = "git" secretOpts.Username = "git"
secretOpts.Password = glToken secretOpts.Password = glToken
secretOpts.CAFile = caBundle
if bootstrapArgs.caFile != "" {
secretOpts.CAFilePath = bootstrapArgs.caFile
}
} else { } else {
keypair, err := sourcesecret.LoadKeyPairFromPath(bootstrapArgs.privateKeyFile, gitArgs.password)
if err != nil {
return err
}
secretOpts.Keypair = keypair
secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(bootstrapArgs.keyAlgorithm) secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(bootstrapArgs.keyAlgorithm)
secretOpts.RSAKeyBits = int(bootstrapArgs.keyRSABits) secretOpts.RSAKeyBits = int(bootstrapArgs.keyRSABits)
secretOpts.ECDSACurve = bootstrapArgs.keyECDSACurve.Curve secretOpts.ECDSACurve = bootstrapArgs.keyECDSACurve.Curve
secretOpts.SSHHostname = gitlabArgs.hostname
if bootstrapArgs.privateKeyFile != "" { secretOpts.SSHHostname = gitlabArgs.hostname
secretOpts.PrivateKeyPath = bootstrapArgs.privateKeyFile
}
if bootstrapArgs.sshHostname != "" { if bootstrapArgs.sshHostname != "" {
secretOpts.SSHHostname = bootstrapArgs.sshHostname secretOpts.SSHHostname = bootstrapArgs.sshHostname
} }
@ -246,6 +245,11 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
RecurseSubmodules: bootstrapArgs.recurseSubmodules, RecurseSubmodules: bootstrapArgs.recurseSubmodules,
} }
entityList, err := bootstrap.LoadEntityListFromPath(bootstrapArgs.gpgKeyRingPath)
if err != nil {
return err
}
// Bootstrap config // Bootstrap config
bootstrapOpts := []bootstrap.GitProviderOption{ bootstrapOpts := []bootstrap.GitProviderOption{
bootstrap.WithProviderRepository(gitlabArgs.owner, gitlabArgs.repository, gitlabArgs.personal), bootstrap.WithProviderRepository(gitlabArgs.owner, gitlabArgs.repository, gitlabArgs.personal),
@ -258,7 +262,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
bootstrap.WithKubeconfig(kubeconfigArgs, kubeclientOptions), bootstrap.WithKubeconfig(kubeconfigArgs, kubeclientOptions),
bootstrap.WithLogger(logger), bootstrap.WithLogger(logger),
bootstrap.WithCABundle(caBundle), bootstrap.WithCABundle(caBundle),
bootstrap.WithGitCommitSigning(bootstrapArgs.gpgKeyRingPath, bootstrapArgs.gpgPassphrase, bootstrapArgs.gpgKeyID), bootstrap.WithGitCommitSigning(entityList, bootstrapArgs.gpgPassphrase, bootstrapArgs.gpgKeyID),
} }
if bootstrapArgs.sshHostname != "" { if bootstrapArgs.sshHostname != "" {
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname)) bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname))

@ -21,6 +21,7 @@ import (
"crypto/elliptic" "crypto/elliptic"
"fmt" "fmt"
"net/url" "net/url"
"os"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
@ -135,8 +136,12 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
} }
switch u.Scheme { switch u.Scheme {
case "ssh": case "ssh":
keypair, err := sourcesecret.LoadKeyPairFromPath(secretGitArgs.privateKeyFile, secretGitArgs.password)
if err != nil {
return err
}
opts.Keypair = keypair
opts.SSHHostname = u.Host opts.SSHHostname = u.Host
opts.PrivateKeyPath = secretGitArgs.privateKeyFile
opts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(secretGitArgs.keyAlgorithm) opts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(secretGitArgs.keyAlgorithm)
opts.RSAKeyBits = int(secretGitArgs.rsaBits) opts.RSAKeyBits = int(secretGitArgs.rsaBits)
opts.ECDSACurve = secretGitArgs.ecdsaCurve.Curve opts.ECDSACurve = secretGitArgs.ecdsaCurve.Curve
@ -147,7 +152,13 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
} }
opts.Username = secretGitArgs.username opts.Username = secretGitArgs.username
opts.Password = secretGitArgs.password opts.Password = secretGitArgs.password
opts.CAFilePath = secretGitArgs.caFile if secretGitArgs.caFile != "" {
caBundle, err := os.ReadFile(secretGitArgs.caFile)
if err != nil {
return fmt.Errorf("unable to read TLS CA file: %w", err)
}
opts.CAFile = caBundle
}
default: default:
return fmt.Errorf("git URL scheme '%s' not supported, can be: ssh, http and https", u.Scheme) return fmt.Errorf("git URL scheme '%s' not supported, can be: ssh, http and https", u.Scheme)
} }

@ -18,6 +18,8 @@ package main
import ( import (
"context" "context"
"fmt"
"os"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
@ -74,15 +76,34 @@ func createSecretHelmCmdRun(cmd *cobra.Command, args []string) error {
return err return err
} }
caBundle := []byte{}
if secretHelmArgs.caFile != "" {
var err error
caBundle, err = os.ReadFile(secretHelmArgs.caFile)
if err != nil {
return fmt.Errorf("unable to read TLS CA file: %w", err)
}
}
var certFile, keyFile []byte
if secretHelmArgs.certFile != "" && secretHelmArgs.keyFile != "" {
if certFile, err = os.ReadFile(secretHelmArgs.certFile); err != nil {
return fmt.Errorf("failed to read cert file: %w", err)
}
if keyFile, err = os.ReadFile(secretHelmArgs.keyFile); err != nil {
return fmt.Errorf("failed to read key file: %w", err)
}
}
opts := sourcesecret.Options{ opts := sourcesecret.Options{
Name: name, Name: name,
Namespace: *kubeconfigArgs.Namespace, Namespace: *kubeconfigArgs.Namespace,
Labels: labels, Labels: labels,
Username: secretHelmArgs.username, Username: secretHelmArgs.username,
Password: secretHelmArgs.password, Password: secretHelmArgs.password,
CAFilePath: secretHelmArgs.caFile, CAFile: caBundle,
CertFilePath: secretHelmArgs.certFile, CertFile: certFile,
KeyFilePath: secretHelmArgs.keyFile, KeyFile: keyFile,
} }
secret, err := sourcesecret.Generate(opts) secret, err := sourcesecret.Generate(opts)
if err != nil { if err != nil {

@ -18,6 +18,8 @@ package main
import ( import (
"context" "context"
"fmt"
"os"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
@ -73,13 +75,32 @@ func createSecretTLSCmdRun(cmd *cobra.Command, args []string) error {
return err return err
} }
caBundle := []byte{}
if secretTLSArgs.caFile != "" {
var err error
caBundle, err = os.ReadFile(secretTLSArgs.caFile)
if err != nil {
return fmt.Errorf("unable to read TLS CA file: %w", err)
}
}
var certFile, keyFile []byte
if secretTLSArgs.certFile != "" && secretTLSArgs.keyFile != "" {
if certFile, err = os.ReadFile(secretTLSArgs.certFile); err != nil {
return fmt.Errorf("failed to read cert file: %w", err)
}
if keyFile, err = os.ReadFile(secretTLSArgs.keyFile); err != nil {
return fmt.Errorf("failed to read key file: %w", err)
}
}
opts := sourcesecret.Options{ opts := sourcesecret.Options{
Name: name, Name: name,
Namespace: *kubeconfigArgs.Namespace, Namespace: *kubeconfigArgs.Namespace,
Labels: labels, Labels: labels,
CAFilePath: secretTLSArgs.caFile, CAFile: caBundle,
CertFilePath: secretTLSArgs.certFile, CertFile: certFile,
KeyFilePath: secretTLSArgs.keyFile, KeyFile: keyFile,
} }
secret, err := sourcesecret.Generate(opts) secret, err := sourcesecret.Generate(opts)
if err != nil { if err != nil {

@ -259,16 +259,26 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
} }
switch u.Scheme { switch u.Scheme {
case "ssh": case "ssh":
keypair, err := sourcesecret.LoadKeyPairFromPath(sourceGitArgs.privateKeyFile, sourceGitArgs.password)
if err != nil {
return err
}
secretOpts.Keypair = keypair
secretOpts.SSHHostname = u.Host secretOpts.SSHHostname = u.Host
secretOpts.PrivateKeyPath = sourceGitArgs.privateKeyFile
secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(sourceGitArgs.keyAlgorithm) secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(sourceGitArgs.keyAlgorithm)
secretOpts.RSAKeyBits = int(sourceGitArgs.keyRSABits) secretOpts.RSAKeyBits = int(sourceGitArgs.keyRSABits)
secretOpts.ECDSACurve = sourceGitArgs.keyECDSACurve.Curve secretOpts.ECDSACurve = sourceGitArgs.keyECDSACurve.Curve
secretOpts.Password = sourceGitArgs.password secretOpts.Password = sourceGitArgs.password
case "https": case "https":
if sourceGitArgs.caFile != "" {
caBundle, err := os.ReadFile(sourceGitArgs.caFile)
if err != nil {
return fmt.Errorf("unable to read TLS CA file: %w", err)
}
secretOpts.CAFile = caBundle
}
secretOpts.Username = sourceGitArgs.username secretOpts.Username = sourceGitArgs.username
secretOpts.Password = sourceGitArgs.password secretOpts.Password = sourceGitArgs.password
secretOpts.CAFilePath = sourceGitArgs.caFile
case "http": case "http":
logger.Warningf("insecure configuration: credentials configured for an HTTP URL") logger.Warningf("insecure configuration: credentials configured for an HTTP URL")
secretOpts.Username = sourceGitArgs.username secretOpts.Username = sourceGitArgs.username

@ -168,6 +168,25 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
return err return err
} }
caBundle := []byte{}
if sourceHelmArgs.caFile != "" {
var err error
caBundle, err = os.ReadFile(sourceHelmArgs.caFile)
if err != nil {
return fmt.Errorf("unable to read TLS CA file: %w", err)
}
}
var certFile, keyFile []byte
if sourceHelmArgs.certFile != "" && sourceHelmArgs.keyFile != "" {
if certFile, err = os.ReadFile(sourceHelmArgs.certFile); err != nil {
return fmt.Errorf("failed to read cert file: %w", err)
}
if keyFile, err = os.ReadFile(sourceHelmArgs.keyFile); err != nil {
return fmt.Errorf("failed to read key file: %w", err)
}
}
logger.Generatef("generating HelmRepository source") logger.Generatef("generating HelmRepository source")
if sourceHelmArgs.secretRef == "" { if sourceHelmArgs.secretRef == "" {
secretName := fmt.Sprintf("helm-%s", name) secretName := fmt.Sprintf("helm-%s", name)
@ -176,9 +195,9 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
Namespace: *kubeconfigArgs.Namespace, Namespace: *kubeconfigArgs.Namespace,
Username: sourceHelmArgs.username, Username: sourceHelmArgs.username,
Password: sourceHelmArgs.password, Password: sourceHelmArgs.password,
CertFilePath: sourceHelmArgs.certFile, CAFile: caBundle,
KeyFilePath: sourceHelmArgs.keyFile, CertFile: certFile,
CAFilePath: sourceHelmArgs.caFile, KeyFile: keyFile,
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile, ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
} }
secret, err := sourcesecret.Generate(secretOpts) secret, err := sourcesecret.Generate(secretOpts)

@ -24,6 +24,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/ProtonMail/go-crypto/openpgp"
gogit "github.com/go-git/go-git/v5" gogit "github.com/go-git/go-git/v5"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
@ -56,9 +57,9 @@ type PlainGitBootstrapper struct {
author git.Author author git.Author
commitMessageAppendix string commitMessageAppendix string
gpgKeyRingPath string gpgKeyRing openpgp.EntityList
gpgPassphrase string gpgPassphrase string
gpgKeyID string gpgKeyID string
restClientGetter genericclioptions.RESTClientGetter restClientGetter genericclioptions.RESTClientGetter
restClientOptions *runclient.Options restClientOptions *runclient.Options
@ -139,7 +140,7 @@ func (b *PlainGitBootstrapper) ReconcileComponents(ctx context.Context, manifest
} }
// Git commit generated // Git commit generated
gpgOpts := git.WithGpgSigningOption(b.gpgKeyRingPath, b.gpgPassphrase, b.gpgKeyID) gpgOpts := git.WithGpgSigningOption(b.gpgKeyRing, b.gpgPassphrase, b.gpgKeyID)
commitMsg := fmt.Sprintf("Add Flux %s component manifests", options.Version) commitMsg := fmt.Sprintf("Add Flux %s component manifests", options.Version)
if b.commitMessageAppendix != "" { if b.commitMessageAppendix != "" {
commitMsg = commitMsg + "\n\n" + b.commitMessageAppendix commitMsg = commitMsg + "\n\n" + b.commitMessageAppendix
@ -195,7 +196,7 @@ func (b *PlainGitBootstrapper) ReconcileSourceSecret(ctx context.Context, option
} }
// Return early if exists and no custom config is passed // Return early if exists and no custom config is passed
if ok && len(options.CAFilePath+options.PrivateKeyPath+options.Username+options.Password) == 0 { if ok && options.Keypair == nil && len(options.CAFile) == 0 && len(options.Username+options.Password) == 0 {
b.logger.Successf("source secret up to date") b.logger.Successf("source secret up to date")
return nil return nil
} }
@ -284,7 +285,7 @@ func (b *PlainGitBootstrapper) ReconcileSyncConfig(ctx context.Context, options
b.logger.Successf("generated sync manifests") b.logger.Successf("generated sync manifests")
// Git commit generated // Git commit generated
gpgOpts := git.WithGpgSigningOption(b.gpgKeyRingPath, b.gpgPassphrase, b.gpgKeyID) gpgOpts := git.WithGpgSigningOption(b.gpgKeyRing, b.gpgPassphrase, b.gpgKeyID)
commitMsg := fmt.Sprintf("Add Flux sync manifests") commitMsg := fmt.Sprintf("Add Flux sync manifests")
if b.commitMessageAppendix != "" { if b.commitMessageAppendix != "" {
commitMsg = commitMsg + "\n\n" + b.commitMessageAppendix commitMsg = commitMsg + "\n\n" + b.commitMessageAppendix

@ -1,5 +1,9 @@
package git package git
import (
"github.com/ProtonMail/go-crypto/openpgp"
)
// Option is a some configuration that modifies options for a commit. // Option is a some configuration that modifies options for a commit.
type Option interface { type Option interface {
// ApplyToCommit applies this configuration to a given commit option. // ApplyToCommit applies this configuration to a given commit option.
@ -13,9 +17,9 @@ type CommitOptions struct {
// GPGSigningInfo contains information for signing a commit. // GPGSigningInfo contains information for signing a commit.
type GPGSigningInfo struct { type GPGSigningInfo struct {
KeyRingPath string KeyRing openpgp.EntityList
Passphrase string Passphrase string
KeyID string KeyID string
} }
type GpgSigningOption struct { type GpgSigningOption struct {
@ -26,17 +30,17 @@ func (w GpgSigningOption) ApplyToCommit(in *CommitOptions) {
in.GPGSigningInfo = w.GPGSigningInfo in.GPGSigningInfo = w.GPGSigningInfo
} }
func WithGpgSigningOption(path, passphrase, keyID string) Option { func WithGpgSigningOption(keyRing openpgp.EntityList, passphrase, keyID string) Option {
// Return nil if no path is set, even if other options are configured. // Return nil if no path is set, even if other options are configured.
if path == "" { if len(keyRing) == 0 {
return GpgSigningOption{} return GpgSigningOption{}
} }
return GpgSigningOption{ return GpgSigningOption{
GPGSigningInfo: &GPGSigningInfo{ GPGSigningInfo: &GPGSigningInfo{
KeyRingPath: path, KeyRing: keyRing,
Passphrase: passphrase, Passphrase: passphrase,
KeyID: keyID, KeyID: keyID,
}, },
} }
} }

@ -258,23 +258,13 @@ func isRemoteBranchNotFoundErr(err error, ref string) bool {
} }
func getOpenPgpEntity(info git.GPGSigningInfo) (*openpgp.Entity, error) { func getOpenPgpEntity(info git.GPGSigningInfo) (*openpgp.Entity, error) {
r, err := os.Open(info.KeyRingPath) if len(info.KeyRing) == 0 {
if err != nil {
return nil, fmt.Errorf("unable to open GPG key ring: %w", err)
}
entityList, err := openpgp.ReadKeyRing(r)
if err != nil {
return nil, err
}
if len(entityList) == 0 {
return nil, fmt.Errorf("empty GPG key ring") return nil, fmt.Errorf("empty GPG key ring")
} }
var entity *openpgp.Entity var entity *openpgp.Entity
if info.KeyID != "" { if info.KeyID != "" {
for _, ent := range entityList { for _, ent := range info.KeyRing {
if ent.PrimaryKey.KeyIdString() == info.KeyID { if ent.PrimaryKey.KeyIdString() == info.KeyID {
entity = ent entity = ent
} }
@ -284,10 +274,10 @@ func getOpenPgpEntity(info git.GPGSigningInfo) (*openpgp.Entity, error) {
return nil, fmt.Errorf("no GPG private key matching key id '%s' found", info.KeyID) return nil, fmt.Errorf("no GPG private key matching key id '%s' found", info.KeyID)
} }
} else { } else {
entity = entityList[0] entity = info.KeyRing[0]
} }
err = entity.PrivateKey.Decrypt([]byte(info.Passphrase)) err := entity.PrivateKey.Decrypt([]byte(info.Passphrase))
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to decrypt GPG private key: %w", err) return nil, fmt.Errorf("unable to decrypt GPG private key: %w", err)
} }

@ -4,8 +4,10 @@
package gogit package gogit
import ( import (
"os"
"testing" "testing"
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/fluxcd/flux2/pkg/bootstrap/git" "github.com/fluxcd/flux2/pkg/bootstrap/git"
) )
@ -49,10 +51,21 @@ func TestGetOpenPgpEntity(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
var entityList openpgp.EntityList
if tt.keyPath != "" {
r, err := os.Open(tt.keyPath)
if err != nil {
t.Errorf("unexpected error: %s", err)
}
entityList, err = openpgp.ReadKeyRing(r)
if err != nil {
t.Errorf("unexpected error: %s", err)
}
}
gpgInfo := git.GPGSigningInfo{ gpgInfo := git.GPGSigningInfo{
KeyRingPath: tt.keyPath, KeyRing: entityList,
Passphrase: tt.passphrase, Passphrase: tt.passphrase,
KeyID: tt.id, KeyID: tt.id,
} }
_, err := getOpenPgpEntity(gpgInfo) _, err := getOpenPgpEntity(gpgInfo)

@ -17,8 +17,12 @@ limitations under the License.
package bootstrap package bootstrap
import ( import (
"fmt"
"os"
"k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/genericclioptions"
"github.com/ProtonMail/go-crypto/openpgp"
runclient "github.com/fluxcd/pkg/runtime/client" runclient "github.com/fluxcd/pkg/runtime/client"
"github.com/fluxcd/flux2/pkg/bootstrap/git" "github.com/fluxcd/flux2/pkg/bootstrap/git"
@ -131,22 +135,22 @@ func (o loggerOption) applyGitProvider(b *GitProviderBootstrapper) {
b.logger = o.logger b.logger = o.logger
} }
func WithGitCommitSigning(path, passphrase, keyID string) Option { func WithGitCommitSigning(gpgKeyRing openpgp.EntityList, passphrase, keyID string) Option {
return gitCommitSigningOption{ return gitCommitSigningOption{
gpgKeyRingPath: path, gpgKeyRing: gpgKeyRing,
gpgPassphrase: passphrase, gpgPassphrase: passphrase,
gpgKeyID: keyID, gpgKeyID: keyID,
} }
} }
type gitCommitSigningOption struct { type gitCommitSigningOption struct {
gpgKeyRingPath string gpgKeyRing openpgp.EntityList
gpgPassphrase string gpgPassphrase string
gpgKeyID string gpgKeyID string
} }
func (o gitCommitSigningOption) applyGit(b *PlainGitBootstrapper) { func (o gitCommitSigningOption) applyGit(b *PlainGitBootstrapper) {
b.gpgKeyRingPath = o.gpgKeyRingPath b.gpgKeyRing = o.gpgKeyRing
b.gpgPassphrase = o.gpgPassphrase b.gpgPassphrase = o.gpgPassphrase
b.gpgKeyID = o.gpgKeyID b.gpgKeyID = o.gpgKeyID
} }
@ -154,3 +158,18 @@ func (o gitCommitSigningOption) applyGit(b *PlainGitBootstrapper) {
func (o gitCommitSigningOption) applyGitProvider(b *GitProviderBootstrapper) { func (o gitCommitSigningOption) applyGitProvider(b *GitProviderBootstrapper) {
o.applyGit(b.PlainGitBootstrapper) o.applyGit(b.PlainGitBootstrapper)
} }
func LoadEntityListFromPath(path string) (openpgp.EntityList, error) {
if path == "" {
return nil, nil
}
r, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("unable to open GPG key ring: %w", err)
}
entityList, err := openpgp.ReadKeyRing(r)
if err != nil {
return nil, err
}
return entityList, nil
}

@ -18,6 +18,8 @@ package sourcesecret
import ( import (
"crypto/elliptic" "crypto/elliptic"
"github.com/fluxcd/pkg/ssh"
) )
type PrivateKeyAlgorithm string type PrivateKeyAlgorithm string
@ -48,12 +50,12 @@ type Options struct {
PrivateKeyAlgorithm PrivateKeyAlgorithm PrivateKeyAlgorithm PrivateKeyAlgorithm
RSAKeyBits int RSAKeyBits int
ECDSACurve elliptic.Curve ECDSACurve elliptic.Curve
PrivateKeyPath string Keypair *ssh.KeyPair
Username string Username string
Password string Password string
CAFilePath string CAFile []byte
CertFilePath string CertFile []byte
KeyFilePath string KeyFile []byte
TargetPath string TargetPath string
ManifestFile string ManifestFile string
} }
@ -64,12 +66,11 @@ func MakeDefaultOptions() Options {
Namespace: "flux-system", Namespace: "flux-system",
Labels: map[string]string{}, Labels: map[string]string{},
PrivateKeyAlgorithm: RSAPrivateKeyAlgorithm, PrivateKeyAlgorithm: RSAPrivateKeyAlgorithm,
PrivateKeyPath: "",
Username: "", Username: "",
Password: "", Password: "",
CAFilePath: "", CAFile: []byte{},
CertFilePath: "", CertFile: []byte{},
KeyFilePath: "", KeyFile: []byte{},
ManifestFile: "secret.yaml", ManifestFile: "secret.yaml",
} }
} }

@ -66,10 +66,8 @@ func Generate(options Options) (*manifestgen.Manifest, error) {
switch { switch {
case options.Username != "" && options.Password != "": case options.Username != "" && options.Password != "":
// noop // noop
case len(options.PrivateKeyPath) > 0: case options.Keypair != nil:
if keypair, err = loadKeyPair(options.PrivateKeyPath, options.Password); err != nil { keypair = options.Keypair
return nil, err
}
case len(options.PrivateKeyAlgorithm) > 0: case len(options.PrivateKeyAlgorithm) > 0:
if keypair, err = generateKeyPair(options); err != nil { if keypair, err = generateKeyPair(options); err != nil {
return nil, err return nil, err
@ -83,23 +81,6 @@ func Generate(options Options) (*manifestgen.Manifest, error) {
} }
} }
var caFile []byte
if options.CAFilePath != "" {
if caFile, err = os.ReadFile(options.CAFilePath); err != nil {
return nil, fmt.Errorf("failed to read CA file: %w", err)
}
}
var certFile, keyFile []byte
if options.CertFilePath != "" && options.KeyFilePath != "" {
if certFile, err = os.ReadFile(options.CertFilePath); err != nil {
return nil, fmt.Errorf("failed to read cert file: %w", err)
}
if keyFile, err = os.ReadFile(options.KeyFilePath); err != nil {
return nil, fmt.Errorf("failed to read key file: %w", err)
}
}
var dockerCfgJson []byte var dockerCfgJson []byte
if options.Registry != "" { if options.Registry != "" {
dockerCfgJson, err = generateDockerConfigJson(options.Registry, options.Username, options.Password) dockerCfgJson, err = generateDockerConfigJson(options.Registry, options.Username, options.Password)
@ -108,7 +89,7 @@ func Generate(options Options) (*manifestgen.Manifest, error) {
} }
} }
secret := buildSecret(keypair, hostKey, caFile, certFile, keyFile, dockerCfgJson, options) secret := buildSecret(keypair, hostKey, options.CAFile, options.CertFile, options.KeyFile, dockerCfgJson, options)
b, err := yaml.Marshal(secret) b, err := yaml.Marshal(secret)
if err != nil { if err != nil {
return nil, err return nil, err
@ -120,6 +101,35 @@ func Generate(options Options) (*manifestgen.Manifest, error) {
}, nil }, nil
} }
func LoadKeyPairFromPath(path, password string) (*ssh.KeyPair, error) {
if path == "" {
return nil, nil
}
b, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to open private key file: %w", err)
}
return LoadKeyPair(b, password)
}
func LoadKeyPair(privateKey []byte, password string) (*ssh.KeyPair, error) {
var ppk cryptssh.Signer
var err error
if password != "" {
ppk, err = cryptssh.ParsePrivateKeyWithPassphrase(privateKey, []byte(password))
} else {
ppk, err = cryptssh.ParsePrivateKey(privateKey)
}
if err != nil {
return nil, err
}
return &ssh.KeyPair{
PublicKey: cryptssh.MarshalAuthorizedKey(ppk.PublicKey()),
PrivateKey: privateKey,
}, nil
}
func buildSecret(keypair *ssh.KeyPair, hostKey, caFile, certFile, keyFile, dockerCfg []byte, options Options) (secret corev1.Secret) { func buildSecret(keypair *ssh.KeyPair, hostKey, caFile, certFile, keyFile, dockerCfg []byte, options Options) (secret corev1.Secret) {
secret.TypeMeta = metav1.TypeMeta{ secret.TypeMeta = metav1.TypeMeta{
APIVersion: "v1", APIVersion: "v1",
@ -143,16 +153,16 @@ func buildSecret(keypair *ssh.KeyPair, hostKey, caFile, certFile, keyFile, docke
secret.StringData[PasswordSecretKey] = options.Password secret.StringData[PasswordSecretKey] = options.Password
} }
if caFile != nil { if len(caFile) != 0 {
secret.StringData[CAFileSecretKey] = string(caFile) secret.StringData[CAFileSecretKey] = string(caFile)
} }
if certFile != nil && keyFile != nil { if len(certFile) != 0 && len(keyFile) != 0 {
secret.StringData[CertFileSecretKey] = string(certFile) secret.StringData[CertFileSecretKey] = string(certFile)
secret.StringData[KeyFileSecretKey] = string(keyFile) secret.StringData[KeyFileSecretKey] = string(keyFile)
} }
if keypair != nil && hostKey != nil { if keypair != nil && len(hostKey) != 0 {
secret.StringData[PrivateKeySecretKey] = string(keypair.PrivateKey) secret.StringData[PrivateKeySecretKey] = string(keypair.PrivateKey)
secret.StringData[PublicKeySecretKey] = string(keypair.PublicKey) secret.StringData[PublicKeySecretKey] = string(keypair.PublicKey)
secret.StringData[KnownHostsSecretKey] = string(hostKey) secret.StringData[KnownHostsSecretKey] = string(hostKey)
@ -165,29 +175,6 @@ func buildSecret(keypair *ssh.KeyPair, hostKey, caFile, certFile, keyFile, docke
return return
} }
func loadKeyPair(path string, password string) (*ssh.KeyPair, error) {
b, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to open private key file: %w", err)
}
var ppk cryptssh.Signer
if password != "" {
ppk, err = cryptssh.ParsePrivateKeyWithPassphrase(b, []byte(password))
} else {
ppk, err = cryptssh.ParsePrivateKey(b)
}
if err != nil {
return nil, err
}
return &ssh.KeyPair{
PublicKey: cryptssh.MarshalAuthorizedKey(ppk.PublicKey()),
PrivateKey: b,
}, nil
}
func generateKeyPair(options Options) (*ssh.KeyPair, error) { func generateKeyPair(options Options) (*ssh.KeyPair, error) {
var keyGen ssh.KeyPairGenerator var keyGen ssh.KeyPairGenerator
switch options.PrivateKeyAlgorithm { switch options.PrivateKeyAlgorithm {

@ -48,7 +48,7 @@ func Test_passwordLoadKeyPair(t *testing.T) {
pk, _ := os.ReadFile(tt.privateKeyPath) pk, _ := os.ReadFile(tt.privateKeyPath)
ppk, _ := os.ReadFile(tt.publicKeyPath) ppk, _ := os.ReadFile(tt.publicKeyPath)
got, err := loadKeyPair(tt.privateKeyPath, tt.password) got, err := LoadKeyPair(pk, tt.password)
if err != nil { if err != nil {
t.Errorf("loadKeyPair() error = %v", err) t.Errorf("loadKeyPair() error = %v", err)
return return
@ -67,24 +67,13 @@ func Test_passwordLoadKeyPair(t *testing.T) {
func Test_PasswordlessLoadKeyPair(t *testing.T) { func Test_PasswordlessLoadKeyPair(t *testing.T) {
for algo, privateKey := range testdata.PEMBytes { for algo, privateKey := range testdata.PEMBytes {
t.Run(algo, func(t *testing.T) { t.Run(algo, func(t *testing.T) {
f, err := os.CreateTemp("", "test-private-key-") got, err := LoadKeyPair(privateKey, "")
if err != nil {
t.Fatalf("unable to create temporary file. err: %s", err)
}
defer os.Remove(f.Name())
if _, err = f.Write(privateKey); err != nil {
t.Fatalf("unable to write private key to file. err: %s", err)
}
got, err := loadKeyPair(f.Name(), "")
if err != nil { if err != nil {
t.Errorf("loadKeyPair() error = %v", err) t.Errorf("loadKeyPair() error = %v", err)
return return
} }
pk, _ := os.ReadFile(f.Name()) if !reflect.DeepEqual(got.PrivateKey, privateKey) {
if !reflect.DeepEqual(got.PrivateKey, pk) {
t.Errorf("PrivateKey %s != %s", got.PrivateKey, string(privateKey)) t.Errorf("PrivateKey %s != %s", got.PrivateKey, string(privateKey))
} }

Loading…
Cancel
Save