diff --git a/cmd/flux/create_secret_git.go b/cmd/flux/create_secret_git.go index 2948a2e0..b8bf05bb 100644 --- a/cmd/flux/create_secret_git.go +++ b/cmd/flux/create_secret_git.go @@ -37,7 +37,7 @@ var createSecretGitCmd = &cobra.Command{ Short: "Create or update a Kubernetes secret for Git authentication", Long: `The create secret git command generates a Kubernetes secret with Git credentials. For Git over SSH, the host and SSH keys are automatically generated and stored in the secret. -For Git over HTTP/S, the provided basic authentication credentials are stored in the secret.`, +For Git over HTTP/S, the provided basic authentication credentials or bearer authentication token are stored in the secret.`, Example: ` # Create a Git SSH authentication secret using an ECDSA P-521 curve public key flux create secret git podinfo-auth \ @@ -87,6 +87,7 @@ type secretGitFlags struct { ecdsaCurve flags.ECDSACurve caFile string privateKeyFile string + bearerToken string } var secretGitArgs = NewSecretGitFlags() @@ -100,6 +101,7 @@ func init() { createSecretGitCmd.Flags().Var(&secretGitArgs.ecdsaCurve, "ssh-ecdsa-curve", secretGitArgs.ecdsaCurve.Description()) createSecretGitCmd.Flags().StringVar(&secretGitArgs.caFile, "ca-file", "", "path to TLS CA file used for validating self-signed certificates") createSecretGitCmd.Flags().StringVar(&secretGitArgs.privateKeyFile, "private-key-file", "", "path to a passwordless private key file used for authenticating to the Git SSH server") + createSecretGitCmd.Flags().StringVar(&secretGitArgs.bearerToken, "bearer-token", "", "bearer authentication token") createSecretCmd.AddCommand(createSecretGitCmd) } @@ -147,11 +149,15 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error { opts.ECDSACurve = secretGitArgs.ecdsaCurve.Curve opts.Password = secretGitArgs.password case "http", "https": - if secretGitArgs.username == "" || secretGitArgs.password == "" { - return fmt.Errorf("for Git over HTTP/S the username and password are required") + if (secretGitArgs.username == "" || secretGitArgs.password == "") && secretGitArgs.bearerToken == "" { + return fmt.Errorf("for Git over HTTP/S the username and password, or a bearer token is required") } opts.Username = secretGitArgs.username opts.Password = secretGitArgs.password + opts.BearerToken = secretGitArgs.bearerToken + if secretGitArgs.username != "" && secretGitArgs.password != "" && secretGitArgs.bearerToken != "" { + return fmt.Errorf("user credentials and bearer token cannot be used together") + } if secretGitArgs.caFile != "" { caBundle, err := os.ReadFile(secretGitArgs.caFile) if err != nil { diff --git a/cmd/flux/create_secret_git_test.go b/cmd/flux/create_secret_git_test.go index 16ff400a..bdf2431d 100644 --- a/cmd/flux/create_secret_git_test.go +++ b/cmd/flux/create_secret_git_test.go @@ -30,6 +30,16 @@ func TestCreateGitSecret(t *testing.T) { args: "create secret git podinfo-auth --url=ssh://git@github.com/stefanprodan/podinfo --private-key-file=./testdata/create_secret/git/ecdsa-password.private --password=password --namespace=my-namespace --export", assert: assertGoldenFile("testdata/create_secret/git/git-ssh-secret-password.yaml"), }, + { + name: "git authentication with bearer token", + args: "create secret git bearer-token-auth --url=https://github.com/stefanprodan/podinfo --bearer-token=ghp_baR2qnFF0O41WlucePL3udt2N9vVZS4R0hAS --namespace=my-namespace --export", + assert: assertGoldenFile("testdata/create_secret/git/git-bearer-token.yaml"), + }, + { + name: "git authentication with basic auth and bearer token", + args: "create secret git podinfo-auth --url=https://github.com/stefanprodan/podinfo --username=aaa --password=zzzz --bearer-token=aaaa --namespace=my-namespace --export", + assert: assertError("user credentials and bearer token cannot be used together"), + }, } for _, tt := range tests { diff --git a/cmd/flux/testdata/create_secret/git/git-bearer-token.yaml b/cmd/flux/testdata/create_secret/git/git-bearer-token.yaml new file mode 100644 index 00000000..7dcfde2d --- /dev/null +++ b/cmd/flux/testdata/create_secret/git/git-bearer-token.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: v1 +kind: Secret +metadata: + name: bearer-token-auth + namespace: my-namespace +stringData: + bearerToken: ghp_baR2qnFF0O41WlucePL3udt2N9vVZS4R0hAS + diff --git a/pkg/manifestgen/sourcesecret/options.go b/pkg/manifestgen/sourcesecret/options.go index d9cec044..ada27ce8 100644 --- a/pkg/manifestgen/sourcesecret/options.go +++ b/pkg/manifestgen/sourcesecret/options.go @@ -39,6 +39,7 @@ const ( PrivateKeySecretKey = "identity" PublicKeySecretKey = "identity.pub" KnownHostsSecretKey = "known_hosts" + BearerTokenKey = "bearerToken" ) type Options struct { @@ -58,6 +59,7 @@ type Options struct { KeyFile []byte TargetPath string ManifestFile string + BearerToken string } func MakeDefaultOptions() Options { @@ -72,5 +74,6 @@ func MakeDefaultOptions() Options { CertFile: []byte{}, KeyFile: []byte{}, ManifestFile: "secret.yaml", + BearerToken: "", } } diff --git a/pkg/manifestgen/sourcesecret/sourcesecret.go b/pkg/manifestgen/sourcesecret/sourcesecret.go index a5ab6c43..5c0d40d1 100644 --- a/pkg/manifestgen/sourcesecret/sourcesecret.go +++ b/pkg/manifestgen/sourcesecret/sourcesecret.go @@ -152,6 +152,9 @@ func buildSecret(keypair *ssh.KeyPair, hostKey, caFile, certFile, keyFile, docke secret.StringData[UsernameSecretKey] = options.Username secret.StringData[PasswordSecretKey] = options.Password } + if options.BearerToken != "" { + secret.StringData[BearerTokenKey] = options.BearerToken + } if len(caFile) != 0 { secret.StringData[CAFileSecretKey] = string(caFile)