Set password in secret
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
This commit is contained in:
@@ -56,6 +56,9 @@ command will perform an upgrade if needed.`,
|
|||||||
|
|
||||||
# Run bootstrap for a Git repository with a passwordless private key
|
# Run bootstrap for a Git repository with a passwordless private key
|
||||||
flux bootstrap git --url=ssh://git@example.com/repository.git --private-key-file=<path/to/private.key>
|
flux bootstrap git --url=ssh://git@example.com/repository.git --private-key-file=<path/to/private.key>
|
||||||
|
|
||||||
|
# Run bootstrap for a Git repository with a private key and password
|
||||||
|
flux bootstrap git --url=ssh://git@example.com/repository.git --private-key-file=<path/to/private.key> --password=<password>
|
||||||
`,
|
`,
|
||||||
RunE: bootstrapGitCmdRun,
|
RunE: bootstrapGitCmdRun,
|
||||||
}
|
}
|
||||||
@@ -163,6 +166,7 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
repositoryURL.Host = repositoryURL.Hostname()
|
repositoryURL.Host = repositoryURL.Hostname()
|
||||||
} else {
|
} else {
|
||||||
secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(bootstrapArgs.keyAlgorithm)
|
secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(bootstrapArgs.keyAlgorithm)
|
||||||
|
secretOpts.Password = gitArgs.password
|
||||||
secretOpts.RSAKeyBits = int(bootstrapArgs.keyRSABits)
|
secretOpts.RSAKeyBits = int(bootstrapArgs.keyRSABits)
|
||||||
secretOpts.ECDSACurve = bootstrapArgs.keyECDSACurve.Curve
|
secretOpts.ECDSACurve = bootstrapArgs.keyECDSACurve.Curve
|
||||||
|
|
||||||
@@ -229,7 +233,7 @@ func transportForURL(u *url.URL) (transport.AuthMethod, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
case "ssh":
|
case "ssh":
|
||||||
if bootstrapArgs.privateKeyFile != "" {
|
if bootstrapArgs.privateKeyFile != "" {
|
||||||
return ssh.NewPublicKeysFromFile(u.User.Username(), bootstrapArgs.privateKeyFile, "")
|
return ssh.NewPublicKeysFromFile(u.User.Username(), bootstrapArgs.privateKeyFile, gitArgs.password)
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -236,6 +236,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
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
|
||||||
case "https":
|
case "https":
|
||||||
secretOpts.Username = sourceGitArgs.username
|
secretOpts.Username = sourceGitArgs.username
|
||||||
secretOpts.Password = sourceGitArgs.password
|
secretOpts.Password = sourceGitArgs.password
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ flux bootstrap git [flags]
|
|||||||
# Run bootstrap for a Git repository with a passwordless private key
|
# Run bootstrap for a Git repository with a passwordless private key
|
||||||
flux bootstrap git --url=ssh://git@example.com/repository.git --private-key-file=<path/to/private.key>
|
flux bootstrap git --url=ssh://git@example.com/repository.git --private-key-file=<path/to/private.key>
|
||||||
|
|
||||||
|
# Run bootstrap for a Git repository with a private key and password
|
||||||
|
flux bootstrap git --url=ssh://git@example.com/repository.git --private-key-file=<path/to/private.key> --password=<password>
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|||||||
@@ -41,10 +41,10 @@ func Generate(options Options) (*manifestgen.Manifest, error) {
|
|||||||
|
|
||||||
var keypair *ssh.KeyPair
|
var keypair *ssh.KeyPair
|
||||||
switch {
|
switch {
|
||||||
case options.Username != "", options.Password != "":
|
case options.Username != "" && options.Password != "":
|
||||||
// noop
|
// noop
|
||||||
case len(options.PrivateKeyPath) > 0:
|
case len(options.PrivateKeyPath) > 0:
|
||||||
if keypair, err = loadKeyPair(options.PrivateKeyPath); err != nil {
|
if keypair, err = loadKeyPair(options.PrivateKeyPath, options.Password); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
case len(options.PrivateKeyAlgorithm) > 0:
|
case len(options.PrivateKeyAlgorithm) > 0:
|
||||||
@@ -101,7 +101,7 @@ func buildSecret(keypair *ssh.KeyPair, hostKey, caFile, certFile, keyFile []byte
|
|||||||
secret.Labels = options.Labels
|
secret.Labels = options.Labels
|
||||||
secret.StringData = map[string]string{}
|
secret.StringData = map[string]string{}
|
||||||
|
|
||||||
if options.Username != "" || options.Password != "" {
|
if options.Username != "" && options.Password != "" {
|
||||||
secret.StringData[UsernameSecretKey] = options.Username
|
secret.StringData[UsernameSecretKey] = options.Username
|
||||||
secret.StringData[PasswordSecretKey] = options.Password
|
secret.StringData[PasswordSecretKey] = options.Password
|
||||||
}
|
}
|
||||||
@@ -119,18 +119,28 @@ func buildSecret(keypair *ssh.KeyPair, hostKey, caFile, certFile, keyFile []byte
|
|||||||
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)
|
||||||
|
// set password if present
|
||||||
|
if options.Password != "" {
|
||||||
|
secret.StringData[PasswordSecretKey] = string(options.Password)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadKeyPair(path string) (*ssh.KeyPair, error) {
|
func loadKeyPair(path string, password string) (*ssh.KeyPair, error) {
|
||||||
b, err := ioutil.ReadFile(path)
|
b, err := ioutil.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to open private key file: %w", err)
|
return nil, fmt.Errorf("failed to open private key file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ppk, err := cryptssh.ParsePrivateKey(b)
|
var ppk cryptssh.Signer
|
||||||
|
if password != "" {
|
||||||
|
ppk, err = cryptssh.ParsePrivateKeyWithPassphrase(b, []byte(password))
|
||||||
|
} else {
|
||||||
|
ppk, err = cryptssh.ParsePrivateKey(b)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,23 +18,82 @@ package sourcesecret
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
"golang.org/x/crypto/ssh/testdata"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_loadKeyPair(t *testing.T) {
|
func Test_passwordLoadKeyPair(t *testing.T) {
|
||||||
pk, _ := ioutil.ReadFile("testdata/rsa")
|
tests := []struct {
|
||||||
ppk, _ := ioutil.ReadFile("testdata/rsa.pub")
|
name string
|
||||||
|
privateKeyPath string
|
||||||
|
publicKeyPath string
|
||||||
|
password string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "private key pair with password",
|
||||||
|
privateKeyPath: "testdata/password_rsa",
|
||||||
|
publicKeyPath: "testdata/password_rsa.pub",
|
||||||
|
password: "password",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
got, err := loadKeyPair("testdata/rsa")
|
for _, tt := range tests {
|
||||||
if err != nil {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
t.Errorf("loadKeyPair() error = %v", err)
|
pk, _ := ioutil.ReadFile(tt.privateKeyPath)
|
||||||
return
|
ppk, _ := ioutil.ReadFile(tt.publicKeyPath)
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got.PrivateKey, pk) {
|
got, err := loadKeyPair(tt.privateKeyPath, tt.password)
|
||||||
t.Errorf("PrivateKey %s != %s", got.PrivateKey, pk)
|
if err != nil {
|
||||||
}
|
t.Errorf("loadKeyPair() error = %v", err)
|
||||||
if !reflect.DeepEqual(got.PublicKey, ppk) {
|
return
|
||||||
t.Errorf("PublicKey %s != %s", got.PublicKey, ppk)
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(got.PrivateKey, pk) {
|
||||||
|
t.Errorf("PrivateKey %s != %s", got.PrivateKey, pk)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(got.PublicKey, ppk) {
|
||||||
|
t.Errorf("PublicKey %s != %s", got.PublicKey, ppk)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_PasswordlessLoadKeyPair(t *testing.T) {
|
||||||
|
for algo, privateKey := range testdata.PEMBytes {
|
||||||
|
t.Run(algo, func(t *testing.T) {
|
||||||
|
f, err := ioutil.TempFile("", "test-private-key-")
|
||||||
|
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 {
|
||||||
|
t.Errorf("loadKeyPair() error = %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pk, _ := ioutil.ReadFile(f.Name())
|
||||||
|
if !reflect.DeepEqual(got.PrivateKey, pk) {
|
||||||
|
t.Errorf("PrivateKey %s != %s", got.PrivateKey, string(privateKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
signer, err := ssh.ParsePrivateKey(privateKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(got.PublicKey, ssh.MarshalAuthorizedKey(signer.PublicKey())) {
|
||||||
|
t.Errorf("PublicKey %s != %s", got.PublicKey, ssh.MarshalAuthorizedKey(signer.PublicKey()))
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
17
pkg/manifestgen/sourcesecret/testdata/password_rsa
vendored
Normal file
17
pkg/manifestgen/sourcesecret/testdata/password_rsa
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||||
|
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABB18lOcbN
|
||||||
|
Q7pk768hyQUymCAAAAEAAAAAEAAACXAAAAB3NzaC1yc2EAAAADAQABAAAAgQCi/p5KtpRl
|
||||||
|
tu3yxEsc+NtR9kRBuYhsh55609dNiTonLlL7K00pLCEWrtEIVM7FzD8IA6Q1NMNBFijje3
|
||||||
|
6gnqMlo6M/cJObkGfZFC025diLI5/ND5R/l60XLtRZfh92K9nCsQKsJaW+R8LTtBdhil/q
|
||||||
|
M/+u07sFHzzk6/36FStg/QAAAiAQAjBJ6TYZuu4iHrrQgbhmlQem18Xjm8f2M9M0BrYgJV
|
||||||
|
F5OVL7mL/bsFTp0IA92HXnxGs0gF4ue5ujCE7SWOyr4SpJfgijExnmDPkZ9nhCG06MsCQ9
|
||||||
|
uU7tTtEnrYcZ5/mmEe8E/O74Mo8xBqI8Unv95eF4p/tAMcDctVX39/lSjP1UZYL6vrLk2L
|
||||||
|
SoEWK2DmZ2ZYdtBJ20AGDJ7MRIX3X/+qZkuYcy7GfPTuDKPIyrr7UEIYH//x8RaGuvOUuu
|
||||||
|
P5bXGOBCTayZgGeWwNDePVITxMhpqTYjy7hqJ1ppBEv1svvbax5ksbwTRzU6quN75o+tbX
|
||||||
|
hf5v0HbiRl3w6LtuwciiQtGsgijxt1noViZvpMLam5EJ3+eTnKnfEPxBMaCx7qepqPT9yI
|
||||||
|
GJ/myyB/+FMkVe9epBeO2wyBTPPzr9O8co8SbcFkFEpcmxKk8toPf4F4XGs7lnibsDXGWE
|
||||||
|
s2+WPmf17WLgpTMLutSVFIxw/V6ajnVrTTTE9AOwC9TYBx152YTWGILUlMMQ4AdNri8H80
|
||||||
|
k/RMv18Tut6k4v6I1aKfUSfXRaaxwagt6zMJ2TvYJFsskfTboM/FjzcDvl4jWQvfcoKJJa
|
||||||
|
da6L6TwnfL03qI+tdT84R4a9oYgZ27WB0ekPnmDpWgIl5jN/F2mHz2/KpuAzC7JgpPdzty
|
||||||
|
271M16OmGvgerrvc/57d0bh4x/E9gj3ZMpkoXBH/lfs5c7LbvRxQ
|
||||||
|
-----END OPENSSH PRIVATE KEY-----
|
||||||
1
pkg/manifestgen/sourcesecret/testdata/password_rsa.pub
vendored
Normal file
1
pkg/manifestgen/sourcesecret/testdata/password_rsa.pub
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCi/p5KtpRltu3yxEsc+NtR9kRBuYhsh55609dNiTonLlL7K00pLCEWrtEIVM7FzD8IA6Q1NMNBFijje36gnqMlo6M/cJObkGfZFC025diLI5/ND5R/l60XLtRZfh92K9nCsQKsJaW+R8LTtBdhil/qM/+u07sFHzzk6/36FStg/Q==
|
||||||
Reference in New Issue
Block a user