diff --git a/cmd/kops/BUILD.bazel b/cmd/kops/BUILD.bazel index 8541ab6d38..986f683d38 100644 --- a/cmd/kops/BUILD.bazel +++ b/cmd/kops/BUILD.bazel @@ -11,13 +11,14 @@ go_library( "create_secret_cilium_encryptionconfig.go", "create_secret_dockerconfig.go", "create_secret_encryptionconfig.go", - "create_secret_sshpublickey.go", "create_secret_weave_encryptionconfig.go", + "create_sshpublickey.go", "delete.go", "delete_cluster.go", "delete_instance.go", "delete_instancegroup.go", "delete_secret.go", + "delete_sshpublickey.go", "distrust.go", "distrust_keypair.go", "edit.go", @@ -33,6 +34,7 @@ go_library( "get_instances.go", "get_keypairs.go", "get_secrets.go", + "get_sshpublickeys.go", "main.go", "promote.go", "promote_keypair.go", diff --git a/cmd/kops/create.go b/cmd/kops/create.go index 07794ece59..f80507207a 100644 --- a/cmd/kops/create.go +++ b/cmd/kops/create.go @@ -59,21 +59,8 @@ var ( # Create an instancegroup based on the YAML passed into stdin. cat instancegroup.yaml | kops create -f - - - # Create a cluster in AWS. - kops create cluster --name=k8s-cluster.example.com \ - --state=s3://my-state-store --zones=us-east-1a \ - --node-count=2 --node-size=t3.small --master-size=t3.small \ - --dns-zone=example.com - - # Create an instancegroup for the k8s-cluster.example.com cluster. - kops create ig --name=k8s-cluster.example.com node-example \ - --role node --subnet my-subnet-name - - # Create a new ssh public key called admin. - kops create secret sshpublickey admin -i ~/.ssh/id_rsa.pub \ - --name k8s-cluster.example.com --state s3://my-state-store `)) + createShort = i18n.T("Create a resource by command line, filename or stdin.") ) @@ -102,6 +89,7 @@ func NewCmdCreate(f *util.Factory, out io.Writer) *cobra.Command { cmd.AddCommand(NewCmdCreateInstanceGroup(f, out)) cmd.AddCommand(NewCmdCreateKeypair(f, out)) cmd.AddCommand(NewCmdCreateSecret(f, out)) + cmd.AddCommand(NewCmdCreateSSHPublicKey(f, out)) return cmd } @@ -203,7 +191,7 @@ func RunCreate(ctx context.Context, f *util.Factory, out io.Writer, c *CreateOpt } sshKeyArr := []byte(v.Spec.PublicKey) - err = sshCredentialStore.AddSSHPublicKey("admin", sshKeyArr) + err = sshCredentialStore.AddSSHPublicKey(sshKeyArr) if err != nil { return err } diff --git a/cmd/kops/create_cluster.go b/cmd/kops/create_cluster.go index 0963282b6a..0f5b574ed5 100644 --- a/cmd/kops/create_cluster.go +++ b/cmd/kops/create_cluster.go @@ -725,8 +725,8 @@ func RunCreateCluster(ctx context.Context, f *util.Factory, out io.Writer, c *Cr return err } - for k, data := range c.SSHPublicKeys { - err = sshCredentialStore.AddSSHPublicKey(k, data) + for _, data := range c.SSHPublicKeys { + err = sshCredentialStore.AddSSHPublicKey(data) if err != nil { return fmt.Errorf("error adding SSH public key: %v", err) } diff --git a/cmd/kops/create_secret.go b/cmd/kops/create_secret.go index aca8e0b9c8..eb14d3278f 100644 --- a/cmd/kops/create_secret.go +++ b/cmd/kops/create_secret.go @@ -22,42 +22,36 @@ import ( "github.com/spf13/cobra" "k8s.io/kops/cmd/kops/util" "k8s.io/kubectl/pkg/util/i18n" - "k8s.io/kubectl/pkg/util/templates" ) var ( - createSecretLong = templates.LongDesc(i18n.T(` - Create a secret`)) - - createSecretExample = templates.Examples(i18n.T(` - # Create a new ssh public key called admin. - kops create secret sshpublickey admin -i ~/.ssh/id_rsa.pub \ - --name k8s-cluster.example.com --state s3://my-state-store - - kops create secret dockerconfig -f ~/.docker/config.json \ - --name k8s-cluster.example.com --state s3://my-state-store - - kops create secret encryptionconfig -f ~/.encryptionconfig.yaml \ - --name k8s-cluster.example.com --state s3://my-state-store - `)) - createSecretShort = i18n.T(`Create a secret.`) ) func NewCmdCreateSecret(f *util.Factory, out io.Writer) *cobra.Command { cmd := &cobra.Command{ - Use: "secret", - Short: createSecretShort, - Long: createSecretLong, - Example: createSecretExample, + Use: "secret", + Short: createSecretShort, } // create subcommands - cmd.AddCommand(NewCmdCreateSecretPublicKey(f, out)) cmd.AddCommand(NewCmdCreateSecretDockerConfig(f, out)) cmd.AddCommand(NewCmdCreateSecretEncryptionConfig(f, out)) cmd.AddCommand(NewCmdCreateSecretWeaveEncryptionConfig(f, out)) cmd.AddCommand(NewCmdCreateSecretCiliumEncryptionConfig(f, out)) + sshPublicKey := NewCmdCreateSSHPublicKey(f, out) + sshPublicKey.Hidden = true + innerArgs := sshPublicKey.Args + sshPublicKey.Args = func(cmd *cobra.Command, args []string) error { + if len(args) > 0 && args[0] == "admin" { + // Backwards compatibility + args = args[1:] + } + + return innerArgs(cmd, args) + } + cmd.AddCommand(sshPublicKey) + return cmd } diff --git a/cmd/kops/create_secret_sshpublickey.go b/cmd/kops/create_secret_sshpublickey.go deleted file mode 100644 index fd5550dac8..0000000000 --- a/cmd/kops/create_secret_sshpublickey.go +++ /dev/null @@ -1,125 +0,0 @@ -/* -Copyright 2019 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "context" - "fmt" - "io" - "io/ioutil" - "os" - - "github.com/spf13/cobra" - "k8s.io/kops/cmd/kops/util" - "k8s.io/kubectl/pkg/util/i18n" - "k8s.io/kubectl/pkg/util/templates" -) - -var ( - createSecretSSHPublicKeyLong = templates.LongDesc(i18n.T(` - Create a new ssh public key, and store the key in the state store. The - key is not updated by this command.`)) - - createSecretSSHPublicKeyExample = templates.Examples(i18n.T(` - # Create a new ssh public key called admin. - kops create secret sshpublickey admin -i ~/.ssh/id_rsa.pub \ - --name k8s-cluster.example.com --state s3://my-state-store - `)) - - createSecretSSHPublicKeyShort = i18n.T(`Create an ssh public key.`) -) - -type CreateSecretPublickeyOptions struct { - ClusterName string - Name string - PublicKeyPath string -} - -func NewCmdCreateSecretPublicKey(f *util.Factory, out io.Writer) *cobra.Command { - options := &CreateSecretPublickeyOptions{} - - cmd := &cobra.Command{ - Use: "sshpublickey", - Short: createSecretSSHPublicKeyShort, - Long: createSecretSSHPublicKeyLong, - Example: createSecretSSHPublicKeyExample, - Run: func(cmd *cobra.Command, args []string) { - ctx := context.TODO() - - if len(args) == 0 { - exitWithError(fmt.Errorf("syntax: NAME -i ")) - } - if len(args) != 1 { - exitWithError(fmt.Errorf("syntax: NAME -i ")) - } - options.Name = args[0] - - err := rootCommand.ProcessArgs(args[1:]) - if err != nil { - exitWithError(err) - } - - options.ClusterName = rootCommand.ClusterName(true) - - err = RunCreateSecretPublicKey(ctx, f, os.Stdout, options) - if err != nil { - exitWithError(err) - } - }, - } - - cmd.Flags().StringVarP(&options.PublicKeyPath, "pubkey", "i", "", "Path to SSH public key") - - return cmd -} - -func RunCreateSecretPublicKey(ctx context.Context, f *util.Factory, out io.Writer, options *CreateSecretPublickeyOptions) error { - if options.PublicKeyPath == "" { - return fmt.Errorf("public key path is required (use -i)") - } - - if options.Name == "" { - return fmt.Errorf("Name is required") - } - - cluster, err := GetCluster(ctx, f, options.ClusterName) - if err != nil { - return err - } - - clientset, err := f.Clientset() - if err != nil { - return err - } - - sshCredentialStore, err := clientset.SSHCredentialStore(cluster) - if err != nil { - return err - } - - data, err := ioutil.ReadFile(options.PublicKeyPath) - if err != nil { - return fmt.Errorf("error reading SSH public key %v: %v", options.PublicKeyPath, err) - } - - err = sshCredentialStore.AddSSHPublicKey(options.Name, data) - if err != nil { - return fmt.Errorf("error adding SSH public key: %v", err) - } - - return nil -} diff --git a/cmd/kops/create_sshpublickey.go b/cmd/kops/create_sshpublickey.go new file mode 100644 index 0000000000..5cb0090265 --- /dev/null +++ b/cmd/kops/create_sshpublickey.go @@ -0,0 +1,110 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "context" + "fmt" + "io" + "io/ioutil" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "k8s.io/kops/cmd/kops/util" + "k8s.io/kops/pkg/commands/commandutils" + "k8s.io/kubectl/pkg/util/i18n" + "k8s.io/kubectl/pkg/util/templates" +) + +var ( + createSSHPublicKeyLong = templates.LongDesc(i18n.T(` + Create a new SSH public key, and store the key in the state store. The + key is not updated by this command.`)) + + createSSHPublicKeyExample = templates.Examples(i18n.T(` + # Create a new SSH public key from the file ""~/.ssh/id_rsa.pub". + kops create sshpublickey k8s-cluster.example.com -i ~/.ssh/id_rsa.pub + `)) + + createSSHPublicKeyShort = i18n.T(`Create an SSH public key.`) +) + +type CreateSSHPublicKeyOptions struct { + ClusterName string + PublicKeyPath string +} + +func NewCmdCreateSSHPublicKey(f *util.Factory, out io.Writer) *cobra.Command { + options := &CreateSSHPublicKeyOptions{} + + cmd := &cobra.Command{ + Use: "sshpublickey [CLUSTER]", + Short: createSSHPublicKeyShort, + Long: createSSHPublicKeyLong, + Example: createSSHPublicKeyExample, + Args: rootCommand.clusterNameArgs(&options.ClusterName), + ValidArgsFunction: commandutils.CompleteClusterName(&rootCommand, true, false), + RunE: func(cmd *cobra.Command, args []string) error { + return RunCreateSSHPublicKey(context.TODO(), f, out, options) + }, + } + + cmd.Flags().StringVarP(&options.PublicKeyPath, "ssh-public-key", "i", "", "Path to SSH public key") + cmd.MarkFlagRequired("ssh-public-key") + cmd.RegisterFlagCompletionFunc("ssh-public-key", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return []string{"pub"}, cobra.ShellCompDirectiveFilterFileExt + }) + + cmd.Flags().SetNormalizeFunc(func(f *pflag.FlagSet, name string) pflag.NormalizedName { + switch name { + case "pubkey": + name = "ssh-public-key" + } + return pflag.NormalizedName(name) + }) + + return cmd +} + +func RunCreateSSHPublicKey(ctx context.Context, f *util.Factory, out io.Writer, options *CreateSSHPublicKeyOptions) error { + cluster, err := GetCluster(ctx, f, options.ClusterName) + if err != nil { + return err + } + + clientset, err := f.Clientset() + if err != nil { + return err + } + + sshCredentialStore, err := clientset.SSHCredentialStore(cluster) + if err != nil { + return err + } + + data, err := ioutil.ReadFile(options.PublicKeyPath) + if err != nil { + return fmt.Errorf("error reading SSH public key %v: %v", options.PublicKeyPath, err) + } + + err = sshCredentialStore.AddSSHPublicKey(data) + if err != nil { + return fmt.Errorf("error adding SSH public key: %v", err) + } + + return nil +} diff --git a/cmd/kops/delete.go b/cmd/kops/delete.go index 7669fff4b5..c606507282 100644 --- a/cmd/kops/delete.go +++ b/cmd/kops/delete.go @@ -74,6 +74,7 @@ func NewCmdDelete(f *util.Factory, out io.Writer) *cobra.Command { cmd.AddCommand(NewCmdDeleteInstance(f, out)) cmd.AddCommand(NewCmdDeleteInstanceGroup(f, out)) cmd.AddCommand(NewCmdDeleteSecret(f, out)) + cmd.AddCommand(NewCmdDeleteSSHPublicKey(f, out)) return cmd } diff --git a/cmd/kops/delete_secret.go b/cmd/kops/delete_secret.go index 9f86d3a094..c233b8108a 100644 --- a/cmd/kops/delete_secret.go +++ b/cmd/kops/delete_secret.go @@ -23,7 +23,6 @@ import ( "github.com/spf13/cobra" "k8s.io/kops/cmd/kops/util" - "k8s.io/kops/pkg/apis/kops" "k8s.io/kops/upup/pkg/fi" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" @@ -94,6 +93,12 @@ func RunDeleteSecret(ctx context.Context, f *util.Factory, out io.Writer, option return fmt.Errorf("SecretName is required") } + if options.SecretType == "sshpublickey" { + return RunDeleteSSHPublicKey(ctx, f, out, &DeleteSSHPublicKeyOptions{ + ClusterName: options.ClusterName, + }) + } + clientset, err := f.Clientset() if err != nil { return err @@ -109,12 +114,7 @@ func RunDeleteSecret(ctx context.Context, f *util.Factory, out io.Writer, option return err } - sshCredentialStore, err := clientset.SSHCredentialStore(cluster) - if err != nil { - return err - } - - secrets, err := listSecrets(secretStore, sshCredentialStore, options.SecretType, []string{options.SecretName}) + secrets, err := listSecrets(secretStore, options.SecretType, []string{options.SecretName}) if err != nil { return err } @@ -138,17 +138,7 @@ func RunDeleteSecret(ctx context.Context, f *util.Factory, out io.Writer, option return fmt.Errorf("found multiple matching secrets; specify the id of the key") } - switch secrets[0].Type { - case kops.SecretTypeSecret: - err = secretStore.DeleteSecret(secrets[0].Name) - case SecretTypeSSHPublicKey: - sshCredential := &kops.SSHCredential{} - sshCredential.Name = secrets[0].Name - if secrets[0].Data != nil { - sshCredential.Spec.PublicKey = string(secrets[0].Data) - } - err = sshCredentialStore.DeleteSSHCredential(sshCredential) - } + err = secretStore.DeleteSecret(secrets[0].Name) if err != nil { return fmt.Errorf("error deleting secret: %v", err) } diff --git a/cmd/kops/delete_sshpublickey.go b/cmd/kops/delete_sshpublickey.go new file mode 100644 index 0000000000..7891672543 --- /dev/null +++ b/cmd/kops/delete_sshpublickey.go @@ -0,0 +1,85 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "context" + "fmt" + "io" + + "github.com/spf13/cobra" + "k8s.io/kops/cmd/kops/util" + "k8s.io/kops/pkg/commands/commandutils" + "k8s.io/kubectl/pkg/util/i18n" + "k8s.io/kubectl/pkg/util/templates" +) + +var ( + deleteSSHPublicKeyExample = templates.Examples(i18n.T(` + # Delete the SSH public key for a cluster + kops delete sshpublickey k8s-cluster.example.com + + `)) + + deleteSSHPublicKeyShort = i18n.T(`Delete an SSH public key.`) +) + +type DeleteSSHPublicKeyOptions struct { + ClusterName string +} + +func NewCmdDeleteSSHPublicKey(f *util.Factory, out io.Writer) *cobra.Command { + options := &DeleteSSHPublicKeyOptions{} + + cmd := &cobra.Command{ + Use: "sshpublickey [CLUSTER]", + Short: deleteSSHPublicKeyShort, + Example: deleteSSHPublicKeyExample, + Args: rootCommand.clusterNameArgs(&options.ClusterName), + ValidArgsFunction: commandutils.CompleteClusterName(&rootCommand, true, false), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.TODO() + + return RunDeleteSSHPublicKey(ctx, f, out, options) + }, + } + + return cmd +} + +func RunDeleteSSHPublicKey(ctx context.Context, f *util.Factory, out io.Writer, options *DeleteSSHPublicKeyOptions) error { + clientset, err := f.Clientset() + if err != nil { + return err + } + + cluster, err := GetCluster(ctx, f, options.ClusterName) + if err != nil { + return err + } + + sshCredentialStore, err := clientset.SSHCredentialStore(cluster) + if err != nil { + return err + } + + if err := sshCredentialStore.DeleteSSHCredential(); err != nil { + return fmt.Errorf("error deleting SSH public key: %v", err) + } + + return nil +} diff --git a/cmd/kops/get.go b/cmd/kops/get.go index 07cab12638..cd7eec468c 100644 --- a/cmd/kops/get.go +++ b/cmd/kops/get.go @@ -90,9 +90,10 @@ func NewCmdGet(f *util.Factory, out io.Writer) *cobra.Command { cmd.AddCommand(NewCmdGetAssets(f, out, options)) cmd.AddCommand(NewCmdGetCluster(f, out, options)) cmd.AddCommand(NewCmdGetInstanceGroups(f, out, options)) + cmd.AddCommand(NewCmdGetInstances(f, out, options)) cmd.AddCommand(NewCmdGetKeypairs(f, out, options)) cmd.AddCommand(NewCmdGetSecrets(f, out, options)) - cmd.AddCommand(NewCmdGetInstances(f, out, options)) + cmd.AddCommand(NewCmdGetSSHPublicKeys(f, out, options)) return cmd } diff --git a/cmd/kops/get_secrets.go b/cmd/kops/get_secrets.go index b3ad1d0ad3..881121e842 100644 --- a/cmd/kops/get_secrets.go +++ b/cmd/kops/get_secrets.go @@ -24,20 +24,14 @@ import ( "strings" "github.com/spf13/cobra" - "k8s.io/klog/v2" "k8s.io/kops/cmd/kops/util" "k8s.io/kops/pkg/apis/kops" - "k8s.io/kops/pkg/sshcredentials" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/util/pkg/tables" "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" ) -// SecretTypeSSHPublicKey is set in a KeysetItem.Type for an SSH public keypair -// As we move fully to using API objects this should go away. -const SecretTypeSSHPublicKey = kops.KeysetType("SSHPublicKey") - var ( getSecretLong = templates.LongDesc(i18n.T(` Display one or many secrets.`)) @@ -80,17 +74,17 @@ func NewCmdGetSecrets(f *util.Factory, out io.Writer, getOptions *GetOptions) *c return cmd } -func listSecrets(secretStore fi.SecretStore, sshCredentialStore fi.SSHCredentialStore, secretType string, names []string) ([]*fi.KeystoreItem, error) { +func listSecrets(secretStore fi.SecretStore, secretType string, names []string) ([]*fi.KeystoreItem, error) { var items []*fi.KeystoreItem findType := strings.ToLower(secretType) switch findType { - case "": - // OK - case "sshpublickey", "secret": + case "", "secret": // OK + case "sshpublickey": + return nil, fmt.Errorf("use 'kops get sshpublickey' instead") case "keypair": - return nil, fmt.Errorf("use 'kops get keypairs %s' instead", secretType) + return nil, fmt.Errorf("use 'kops get keypairs' instead") default: return nil, fmt.Errorf("unknown secret type %q", secretType) } @@ -114,33 +108,6 @@ func listSecrets(secretStore fi.SecretStore, sshCredentialStore fi.SSHCredential } } - if findType == "" || findType == strings.ToLower(string(SecretTypeSSHPublicKey)) { - l, err := sshCredentialStore.ListSSHCredentials() - if err != nil { - return nil, fmt.Errorf("error listing SSH credentials %v", err) - } - - for i := range l { - id, err := sshcredentials.Fingerprint(l[i].Spec.PublicKey) - if err != nil { - klog.Warningf("unable to compute fingerprint for public key %q", l[i].Name) - } - item := &fi.KeystoreItem{ - Name: l[i].Name, - ID: id, - Type: SecretTypeSSHPublicKey, - } - if l[i].Spec.PublicKey != "" { - item.Data = []byte(l[i].Spec.PublicKey) - } - if findType != "" && findType != strings.ToLower(string(item.Type)) { - continue - } - - items = append(items, item) - } - } - if len(names) != 0 { var matches []*fi.KeystoreItem for _, arg := range names { @@ -180,12 +147,7 @@ func RunGetSecrets(ctx context.Context, options *GetSecretsOptions, args []strin return err } - sshCredentialStore, err := clientset.SSHCredentialStore(cluster) - if err != nil { - return err - } - - items, err := listSecrets(secretStore, sshCredentialStore, options.Type, args) + items, err := listSecrets(secretStore, options.Type, args) if err != nil { return err } diff --git a/cmd/kops/get_sshpublickeys.go b/cmd/kops/get_sshpublickeys.go new file mode 100644 index 0000000000..986eb9a87a --- /dev/null +++ b/cmd/kops/get_sshpublickeys.go @@ -0,0 +1,141 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "context" + "encoding/json" + "fmt" + "io" + + "github.com/spf13/cobra" + "k8s.io/klog/v2" + "k8s.io/kops/cmd/kops/util" + "k8s.io/kops/pkg/commands/commandutils" + "k8s.io/kops/pkg/sshcredentials" + "k8s.io/kops/util/pkg/tables" + "k8s.io/kubectl/pkg/util/i18n" + "k8s.io/kubectl/pkg/util/templates" + "sigs.k8s.io/yaml" +) + +var ( + getSSHPublicKeysExample = templates.Examples(i18n.T(` + # Get the SSH public key + kops get sshpublickey`)) + + getSSHPublicKeysShort = i18n.T(`Get one or many secrets.`) +) + +type GetSSHPublicKeysOptions struct { + *GetOptions +} + +func NewCmdGetSSHPublicKeys(f *util.Factory, out io.Writer, getOptions *GetOptions) *cobra.Command { + options := GetSSHPublicKeysOptions{ + GetOptions: getOptions, + } + cmd := &cobra.Command{ + Use: "sshpublickeys [CLUSTER]", + Aliases: []string{"sshpublickey", "ssh"}, + Short: getSSHPublicKeysShort, + Example: getSSHPublicKeysExample, + Args: rootCommand.clusterNameArgs(&options.ClusterName), + ValidArgsFunction: commandutils.CompleteClusterName(&rootCommand, true, false), + RunE: func(cmd *cobra.Command, args []string) error { + return RunGetSSHPublicKeys(context.TODO(), f, out, &options) + }, + } + + return cmd +} + +type SSHKeyItem struct { + ID string `json:"id"` + PublicKey string `json:"publicKey"` +} + +func RunGetSSHPublicKeys(ctx context.Context, f *util.Factory, out io.Writer, options *GetSSHPublicKeysOptions) error { + clientset, err := f.Clientset() + if err != nil { + return err + } + + cluster, err := clientset.GetCluster(ctx, options.ClusterName) + if err != nil { + return err + } + + sshCredentialStore, err := clientset.SSHCredentialStore(cluster) + if err != nil { + return err + } + + var items []*SSHKeyItem + + l, err := sshCredentialStore.FindSSHPublicKeys() + if err != nil { + return fmt.Errorf("listing SSH credentials %v", err) + } + + for _, key := range l { + id, err := sshcredentials.Fingerprint(key.Spec.PublicKey) + if err != nil { + klog.Warningf("unable to compute fingerprint for public key") + } + item := &SSHKeyItem{ + ID: id, + PublicKey: key.Spec.PublicKey, + } + + items = append(items, item) + } + + if len(items) == 0 { + return fmt.Errorf("no SSH public key found") + } + switch options.Output { + + case OutputTable: + t := &tables.Table{} + t.AddColumn("ID", func(i *SSHKeyItem) string { + return i.ID + }) + return t.Render(items, out, "ID") + + case OutputYaml: + y, err := yaml.Marshal(items) + if err != nil { + return fmt.Errorf("unable to marshal YAML: %v", err) + } + if _, err := out.Write(y); err != nil { + return fmt.Errorf("error writing to output: %v", err) + } + case OutputJSON: + j, err := json.Marshal(items) + if err != nil { + return fmt.Errorf("unable to marshal JSON: %v", err) + } + if _, err := out.Write(j); err != nil { + return fmt.Errorf("error writing to output: %v", err) + } + default: + return fmt.Errorf("unknown output format: %q", options.Output) + } + + return nil +} diff --git a/cmd/kops/integration_test.go b/cmd/kops/integration_test.go index 894bc6514c..dbecca3c94 100644 --- a/cmd/kops/integration_test.go +++ b/cmd/kops/integration_test.go @@ -698,12 +698,11 @@ func (i *integrationTest) setupCluster(t *testing.T, inputYAML string, ctx conte } if i.sshKey { - options := &CreateSecretPublickeyOptions{} + options := &CreateSSHPublicKeyOptions{} options.ClusterName = i.clusterName - options.Name = "admin" options.PublicKeyPath = path.Join(i.srcDir, "id_rsa.pub") - err := RunCreateSecretPublicKey(ctx, factory, &stdout, options) + err := RunCreateSSHPublicKey(ctx, factory, &stdout, options) if err != nil { t.Fatalf("error running %q create public key: %v", inputYAML, err) } diff --git a/cmd/kops/replace.go b/cmd/kops/replace.go index b3b38391ed..f8641cfc17 100644 --- a/cmd/kops/replace.go +++ b/cmd/kops/replace.go @@ -207,7 +207,7 @@ func RunReplace(ctx context.Context, f *util.Factory, out io.Writer, c *ReplaceO } sshKeyArr := []byte(v.Spec.PublicKey) - err = sshCredentialStore.AddSSHPublicKey("admin", sshKeyArr) + err = sshCredentialStore.AddSSHPublicKey(sshKeyArr) if err != nil { return fmt.Errorf("error replacing SSHCredential: %v", err) } diff --git a/cmd/kops/update_cluster.go b/cmd/kops/update_cluster.go index 2ddf12d3fb..c91cc5998f 100644 --- a/cmd/kops/update_cluster.go +++ b/cmd/kops/update_cluster.go @@ -233,7 +233,7 @@ func RunUpdateCluster(ctx context.Context, f *util.Factory, out io.Writer, c *Up if err != nil { return results, fmt.Errorf("error reading SSH key file %q: %v", c.SSHPublicKey, err) } - err = sshCredentialStore.AddSSHPublicKey(fi.SecretNameSSHPrimary, authorized) + err = sshCredentialStore.AddSSHPublicKey(authorized) if err != nil { return results, fmt.Errorf("error adding SSH public key: %v", err) } diff --git a/docs/cli/kops_create.md b/docs/cli/kops_create.md index 0e26bbb4c0..fdadb6c6e2 100644 --- a/docs/cli/kops_create.md +++ b/docs/cli/kops_create.md @@ -30,20 +30,6 @@ kops create {-f FILENAME}... [flags] # Create an instancegroup based on the YAML passed into stdin. cat instancegroup.yaml | kops create -f - - - # Create a cluster in AWS. - kops create cluster --name=k8s-cluster.example.com \ - --state=s3://my-state-store --zones=us-east-1a \ - --node-count=2 --node-size=t3.small --master-size=t3.small \ - --dns-zone=example.com - - # Create an instancegroup for the k8s-cluster.example.com cluster. - kops create ig --name=k8s-cluster.example.com node-example \ - --role node --subnet my-subnet-name - - # Create a new ssh public key called admin. - kops create secret sshpublickey admin -i ~/.ssh/id_rsa.pub \ - --name k8s-cluster.example.com --state s3://my-state-store ``` ### Options @@ -81,4 +67,5 @@ kops create {-f FILENAME}... [flags] * [kops create instancegroup](kops_create_instancegroup.md) - Create an instancegroup. * [kops create keypair](kops_create_keypair.md) - Add a CA certificate and private key to a keyset. * [kops create secret](kops_create_secret.md) - Create a secret. +* [kops create sshpublickey](kops_create_sshpublickey.md) - Create an SSH public key. diff --git a/docs/cli/kops_create_secret.md b/docs/cli/kops_create_secret.md index 693f0fa3fd..44daf7c51b 100644 --- a/docs/cli/kops_create_secret.md +++ b/docs/cli/kops_create_secret.md @@ -5,24 +5,6 @@ Create a secret. -### Synopsis - -Create a secret - -### Examples - -``` - # Create a new ssh public key called admin. - kops create secret sshpublickey admin -i ~/.ssh/id_rsa.pub \ - --name k8s-cluster.example.com --state s3://my-state-store - - kops create secret dockerconfig -f ~/.docker/config.json \ - --name k8s-cluster.example.com --state s3://my-state-store - - kops create secret encryptionconfig -f ~/.encryptionconfig.yaml \ - --name k8s-cluster.example.com --state s3://my-state-store -``` - ### Options ``` @@ -56,6 +38,5 @@ Create a secret * [kops create secret ciliumpassword](kops_create_secret_ciliumpassword.md) - Create a cilium encryption key. * [kops create secret dockerconfig](kops_create_secret_dockerconfig.md) - Create a docker config. * [kops create secret encryptionconfig](kops_create_secret_encryptionconfig.md) - Create an encryption config. -* [kops create secret sshpublickey](kops_create_secret_sshpublickey.md) - Create an ssh public key. * [kops create secret weavepassword](kops_create_secret_weavepassword.md) - Create a weave encryption config. diff --git a/docs/cli/kops_create_secret_sshpublickey.md b/docs/cli/kops_create_sshpublickey.md similarity index 79% rename from docs/cli/kops_create_secret_sshpublickey.md rename to docs/cli/kops_create_sshpublickey.md index 725f7e61d5..f3e3f4358b 100644 --- a/docs/cli/kops_create_secret_sshpublickey.md +++ b/docs/cli/kops_create_sshpublickey.md @@ -1,31 +1,30 @@ -## kops create secret sshpublickey +## kops create sshpublickey -Create an ssh public key. +Create an SSH public key. ### Synopsis -Create a new ssh public key, and store the key in the state store. The key is not updated by this command. +Create a new SSH public key, and store the key in the state store. The key is not updated by this command. ``` -kops create secret sshpublickey [flags] +kops create sshpublickey [CLUSTER] [flags] ``` ### Examples ``` - # Create a new ssh public key called admin. - kops create secret sshpublickey admin -i ~/.ssh/id_rsa.pub \ - --name k8s-cluster.example.com --state s3://my-state-store + # Create a new SSH public key from the file ""~/.ssh/id_rsa.pub". + kops create sshpublickey k8s-cluster.example.com -i ~/.ssh/id_rsa.pub ``` ### Options ``` - -h, --help help for sshpublickey - -i, --pubkey string Path to SSH public key + -h, --help help for sshpublickey + -i, --ssh-public-key string Path to SSH public key ``` ### Options inherited from parent commands @@ -51,5 +50,5 @@ kops create secret sshpublickey [flags] ### SEE ALSO -* [kops create secret](kops_create_secret.md) - Create a secret. +* [kops create](kops_create.md) - Create a resource by command line, filename or stdin. diff --git a/docs/cli/kops_delete.md b/docs/cli/kops_delete.md index 2efa10ea91..bd4e57c20f 100644 --- a/docs/cli/kops_delete.md +++ b/docs/cli/kops_delete.md @@ -55,4 +55,5 @@ kops delete {-f FILENAME}... [flags] * [kops delete instance](kops_delete_instance.md) - Delete an instance. * [kops delete instancegroup](kops_delete_instancegroup.md) - Delete instance group. * [kops delete secret](kops_delete_secret.md) - Delete a secret +* [kops delete sshpublickey](kops_delete_sshpublickey.md) - Delete an SSH public key. diff --git a/docs/cli/kops_delete_sshpublickey.md b/docs/cli/kops_delete_sshpublickey.md new file mode 100644 index 0000000000..83d06fef5c --- /dev/null +++ b/docs/cli/kops_delete_sshpublickey.md @@ -0,0 +1,49 @@ + + + +## kops delete sshpublickey + +Delete an SSH public key. + +``` +kops delete sshpublickey [CLUSTER] [flags] +``` + +### Examples + +``` + # Delete the SSH public key for a cluster + kops delete sshpublickey k8s-cluster.example.com +``` + +### Options + +``` + -h, --help help for sshpublickey +``` + +### Options inherited from parent commands + +``` + --add_dir_header If true, adds the file directory to the header of the log messages + --alsologtostderr log to standard error as well as files + --config string yaml config file (default is $HOME/.kops.yaml) + --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log_dir string If non-empty, write log files in this directory + --log_file string If non-empty, use this log file + --log_file_max_size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) + --logtostderr log to standard error instead of files (default true) + --name string Name of cluster. Overrides KOPS_CLUSTER_NAME environment variable + --one_output If true, only write logs to their native severity level (vs also writing to each lower severity level) + --skip_headers If true, avoid header prefixes in the log messages + --skip_log_headers If true, avoid headers when opening log files + --state string Location of state storage (kops 'config' file). Overrides KOPS_STATE_STORE environment variable + --stderrthreshold severity logs at or above this threshold go to stderr (default 2) + -v, --v Level number for the log level verbosity + --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging +``` + +### SEE ALSO + +* [kops delete](kops_delete.md) - Delete clusters, instancegroups, instances, and secrets. + diff --git a/docs/cli/kops_get.md b/docs/cli/kops_get.md index d4965b7269..72c8ab08c1 100644 --- a/docs/cli/kops_get.md +++ b/docs/cli/kops_get.md @@ -67,4 +67,5 @@ kops get [flags] * [kops get instances](kops_get_instances.md) - Display cluster instances. * [kops get keypairs](kops_get_keypairs.md) - Get one or many keypairs. * [kops get secrets](kops_get_secrets.md) - Get one or many secrets. +* [kops get sshpublickeys](kops_get_sshpublickeys.md) - Get one or many secrets. diff --git a/docs/cli/kops_get_sshpublickeys.md b/docs/cli/kops_get_sshpublickeys.md new file mode 100644 index 0000000000..3f8f5864af --- /dev/null +++ b/docs/cli/kops_get_sshpublickeys.md @@ -0,0 +1,50 @@ + + + +## kops get sshpublickeys + +Get one or many secrets. + +``` +kops get sshpublickeys [CLUSTER] [flags] +``` + +### Examples + +``` + # Get the SSH public key + kops get sshpublickey +``` + +### Options + +``` + -h, --help help for sshpublickeys +``` + +### Options inherited from parent commands + +``` + --add_dir_header If true, adds the file directory to the header of the log messages + --alsologtostderr log to standard error as well as files + --config string yaml config file (default is $HOME/.kops.yaml) + --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log_dir string If non-empty, write log files in this directory + --log_file string If non-empty, use this log file + --log_file_max_size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) + --logtostderr log to standard error instead of files (default true) + --name string Name of cluster. Overrides KOPS_CLUSTER_NAME environment variable + --one_output If true, only write logs to their native severity level (vs also writing to each lower severity level) + -o, --output string output format. One of: table, yaml, json (default "table") + --skip_headers If true, avoid header prefixes in the log messages + --skip_log_headers If true, avoid headers when opening log files + --state string Location of state storage (kops 'config' file). Overrides KOPS_STATE_STORE environment variable + --stderrthreshold severity logs at or above this threshold go to stderr (default 2) + -v, --v Level number for the log level verbosity + --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging +``` + +### SEE ALSO + +* [kops get](kops_get.md) - Get one or many resources. + diff --git a/examples/kops-api-example/up.go b/examples/kops-api-example/up.go index d42912cdf5..7dac5a3bcd 100644 --- a/examples/kops-api-example/up.go +++ b/examples/kops-api-example/up.go @@ -120,7 +120,7 @@ func up(ctx context.Context) error { if err != nil { return fmt.Errorf("error reading SSH key file %q: %v", f, err) } - err = sshCredentialStore.AddSSHPublicKey(fi.SecretNameSSHPrimary, pubKey) + err = sshCredentialStore.AddSSHPublicKey(pubKey) if err != nil { return fmt.Errorf("error adding SSH public key: %v", err) } diff --git a/pkg/instancegroups/rollingupdate_os_test.go b/pkg/instancegroups/rollingupdate_os_test.go index 098e526b17..cda627df61 100644 --- a/pkg/instancegroups/rollingupdate_os_test.go +++ b/pkg/instancegroups/rollingupdate_os_test.go @@ -34,7 +34,6 @@ import ( "k8s.io/kops/pkg/client/simple/vfsclientset" "k8s.io/kops/pkg/cloudinstances" "k8s.io/kops/pkg/testutils" - "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup" "k8s.io/kops/upup/pkg/fi/cloudup/openstack" "k8s.io/kops/util/pkg/vfs" @@ -75,7 +74,7 @@ func getTestSetupOS(t *testing.T) (*RollingUpdateCluster, *openstack.MockCloud) t.Fatalf("Failed to get credential store: %v", err) } - sshCredentialStore.AddSSHPublicKey(fi.SecretNameSSHPrimary, sshPublicKey) + sshCredentialStore.AddSSHPublicKey(sshPublicKey) c := &RollingUpdateCluster{ Cloud: mockcloud, diff --git a/upup/pkg/fi/ca.go b/upup/pkg/fi/ca.go index 5dca18b8f4..9ed933cf9c 100644 --- a/upup/pkg/fi/ca.go +++ b/upup/pkg/fi/ca.go @@ -98,17 +98,14 @@ type CAStore interface { // SSHCredentialStore holds SSHCredential objects type SSHCredentialStore interface { - // DeleteSSHCredential deletes the specified SSH credential - DeleteSSHCredential(item *kops.SSHCredential) error + // DeleteSSHCredential deletes the specified SSH credential. + DeleteSSHCredential() error - // ListSSHCredentials will list all the SSH credentials - ListSSHCredentials() ([]*kops.SSHCredential, error) + // AddSSHPublicKey adds an SSH public key. + AddSSHPublicKey(data []byte) error - // AddSSHPublicKey adds an SSH public key - AddSSHPublicKey(name string, data []byte) error - - // FindSSHPublicKeys retrieves the SSH public keys with the specific name - FindSSHPublicKeys(name string) ([]*kops.SSHCredential, error) + // FindSSHPublicKeys retrieves the SSH public keys. + FindSSHPublicKeys() ([]*kops.SSHCredential, error) } // FindPrimaryKeypair is a common implementation of pki.FindPrimaryKeypair. diff --git a/upup/pkg/fi/clientset_castore.go b/upup/pkg/fi/clientset_castore.go index b4d498ce55..21b4dbddcc 100644 --- a/upup/pkg/fi/clientset_castore.go +++ b/upup/pkg/fi/clientset_castore.go @@ -196,26 +196,6 @@ func (c *ClientsetCAStore) ListKeysets() (map[string]*Keyset, error) { return items, nil } -// ListSSHCredentials implements SSHCredentialStore::ListSSHCredentials -func (c *ClientsetCAStore) ListSSHCredentials() ([]*kops.SSHCredential, error) { - ctx := context.TODO() - - var items []*kops.SSHCredential - - { - list, err := c.clientset.SSHCredentials(c.namespace).List(ctx, metav1.ListOptions{}) - if err != nil { - return nil, fmt.Errorf("error listing SSHCredentials: %v", err) - } - - for i := range list.Items { - items = append(items, &list.Items[i]) - } - } - - return items, nil -} - // StoreKeyset implements CAStore::StoreKeyset func (c *ClientsetCAStore) StoreKeyset(name string, keyset *Keyset) error { ctx := context.TODO() @@ -260,47 +240,47 @@ func (c *ClientsetCAStore) storeKeyset(ctx context.Context, name string, keyset } // addSSHCredential saves the specified SSH Credential to the registry, doing an update or insert -func (c *ClientsetCAStore) addSSHCredential(ctx context.Context, name string, publicKey string) error { +func (c *ClientsetCAStore) addSSHCredential(ctx context.Context, publicKey string) error { create := false client := c.clientset.SSHCredentials(c.namespace) - sshCredential, err := client.Get(ctx, name, metav1.GetOptions{}) + sshCredential, err := client.Get(ctx, "admin", metav1.GetOptions{}) if err != nil { if errors.IsNotFound(err) { sshCredential = nil } else { - return fmt.Errorf("error reading SSHCredential %q: %v", name, err) + return fmt.Errorf("error reading SSHCredential: %v", err) } } if sshCredential == nil { sshCredential = &kops.SSHCredential{} - sshCredential.Name = name + sshCredential.Name = "admin" create = true } sshCredential.Spec.PublicKey = publicKey if create { if _, err := client.Create(ctx, sshCredential, metav1.CreateOptions{}); err != nil { - return fmt.Errorf("error creating SSHCredential %q: %v", name, err) + return fmt.Errorf("error creating SSHCredential: %v", err) } } else { if _, err := client.Update(ctx, sshCredential, metav1.UpdateOptions{}); err != nil { - return fmt.Errorf("error updating SSHCredential %q: %v", name, err) + return fmt.Errorf("error updating SSHCredential: %v", err) } } return nil } -// deleteSSHCredential deletes the specified SSHCredential from the registry -func (c *ClientsetCAStore) deleteSSHCredential(ctx context.Context, name string) error { +// deleteSSHCredential deletes the SSHCredential from the registry. +func (c *ClientsetCAStore) deleteSSHCredential(ctx context.Context) error { client := c.clientset.SSHCredentials(c.namespace) - err := client.Delete(ctx, name, metav1.DeleteOptions{}) + err := client.Delete(ctx, "admin", metav1.DeleteOptions{}) if err != nil { - return fmt.Errorf("error deleting SSHCredential %q: %v", name, err) + return fmt.Errorf("error deleting SSHCredential: %v", err) } return nil } // AddSSHPublicKey implements CAStore::AddSSHPublicKey -func (c *ClientsetCAStore) AddSSHPublicKey(name string, pubkey []byte) error { +func (c *ClientsetCAStore) AddSSHPublicKey(pubkey []byte) error { ctx := context.TODO() _, _, _, _, err := ssh.ParseAuthorizedKey(pubkey) @@ -308,28 +288,19 @@ func (c *ClientsetCAStore) AddSSHPublicKey(name string, pubkey []byte) error { return fmt.Errorf("error parsing SSH public key: %v", err) } - // TODO: Reintroduce or remove - //// compute fingerprint to serve as id - //h := md5.New() - //_, err = h.Write(sshPublicKey.Marshal()) - //if err != nil { - // return err - //} - //id = formatFingerprint(h.Sum(nil)) - - return c.addSSHCredential(ctx, name, string(pubkey)) + return c.addSSHCredential(ctx, string(pubkey)) } // FindSSHPublicKeys implements CAStore::FindSSHPublicKeys -func (c *ClientsetCAStore) FindSSHPublicKeys(name string) ([]*kops.SSHCredential, error) { +func (c *ClientsetCAStore) FindSSHPublicKeys() ([]*kops.SSHCredential, error) { ctx := context.TODO() - o, err := c.clientset.SSHCredentials(c.namespace).Get(ctx, name, metav1.GetOptions{}) + o, err := c.clientset.SSHCredentials(c.namespace).Get(ctx, "admin", metav1.GetOptions{}) if err != nil { if errors.IsNotFound(err) { return nil, nil } - return nil, fmt.Errorf("error reading SSHCredential %q: %v", name, err) + return nil, fmt.Errorf("error reading SSHCredential: %v", err) } items := []*kops.SSHCredential{o} @@ -337,10 +308,10 @@ func (c *ClientsetCAStore) FindSSHPublicKeys(name string) ([]*kops.SSHCredential } // DeleteSSHCredential implements SSHCredentialStore::DeleteSSHCredential -func (c *ClientsetCAStore) DeleteSSHCredential(item *kops.SSHCredential) error { +func (c *ClientsetCAStore) DeleteSSHCredential() error { ctx := context.TODO() - return c.deleteSSHCredential(ctx, item.Name) + return c.deleteSSHCredential(ctx) } func (c *ClientsetCAStore) MirrorTo(basedir vfs.Path) error { @@ -355,7 +326,7 @@ func (c *ClientsetCAStore) MirrorTo(basedir vfs.Path) error { } } - sshCredentials, err := c.ListSSHCredentials() + sshCredentials, err := c.FindSSHPublicKeys() if err != nil { return fmt.Errorf("error listing SSHCredentials: %v", err) } diff --git a/upup/pkg/fi/cloudup/apply_cluster.go b/upup/pkg/fi/cloudup/apply_cluster.go index 1b7b989597..bf2ca9d4be 100644 --- a/upup/pkg/fi/cloudup/apply_cluster.go +++ b/upup/pkg/fi/cloudup/apply_cluster.go @@ -383,7 +383,7 @@ func (c *ApplyClusterCmd) Run(ctx context.Context) error { var sshPublicKeys [][]byte { - keys, err := sshCredentialStore.FindSSHPublicKeys(fi.SecretNameSSHPrimary) + keys, err := sshCredentialStore.FindSSHPublicKeys() if err != nil { return fmt.Errorf("error retrieving SSH public key %q: %v", fi.SecretNameSSHPrimary, err) } diff --git a/upup/pkg/fi/vfs_castore.go b/upup/pkg/fi/vfs_castore.go index 0bf01ae757..6389954c54 100644 --- a/upup/pkg/fi/vfs_castore.go +++ b/upup/pkg/fi/vfs_castore.go @@ -268,44 +268,6 @@ func (c *VFSCAStore) ListKeysets() (map[string]*Keyset, error) { return keysets, nil } -// ListSSHCredentials implements SSHCredentialStore::ListSSHCredentials -func (c *VFSCAStore) ListSSHCredentials() ([]*kops.SSHCredential, error) { - var items []*kops.SSHCredential - - { - baseDir := c.basedir.Join("ssh", "public") - files, err := baseDir.ReadTree() - if err != nil { - return nil, fmt.Errorf("error reading directory %q: %v", baseDir, err) - } - - for _, f := range files { - relativePath, err := vfs.RelativePath(baseDir, f) - if err != nil { - return nil, err - } - - tokens := strings.Split(relativePath, "/") - if len(tokens) != 2 { - klog.V(2).Infof("ignoring unexpected file in keystore: %q", f) - continue - } - - pubkey, err := f.ReadFile() - if err != nil { - return nil, fmt.Errorf("error reading SSH credential %q: %v", f, err) - } - - item := &kops.SSHCredential{} - item.Name = tokens[0] - item.Spec.PublicKey = string(pubkey) - items = append(items, item) - } - } - - return items, nil -} - // MirrorTo will copy keys to a vfs.Path, which is often easier for a machine to read func (c *VFSCAStore) MirrorTo(basedir vfs.Path) error { if basedir.Path() == c.basedir.Path() { @@ -325,7 +287,7 @@ func (c *VFSCAStore) MirrorTo(basedir vfs.Path) error { } } - sshCredentials, err := c.ListSSHCredentials() + sshCredentials, err := c.FindSSHPublicKeys() if err != nil { return fmt.Errorf("error listing SSHCredentials: %v", err) } @@ -429,13 +391,13 @@ func (c *VFSCAStore) findPrivateKeyset(id string) (*Keyset, error) { } // AddSSHPublicKey stores an SSH public key -func (c *VFSCAStore) AddSSHPublicKey(name string, pubkey []byte) error { +func (c *VFSCAStore) AddSSHPublicKey(pubkey []byte) error { id, err := sshcredentials.Fingerprint(string(pubkey)) if err != nil { - return fmt.Errorf("error fingerprinting SSH public key %q: %v", name, err) + return fmt.Errorf("error fingerprinting SSH public key: %v", err) } - p := c.buildSSHPublicKeyPath(name, id) + p := c.buildSSHPublicKeyPath(id) acl, err := acls.GetACL(p, c.cluster) if err != nil { @@ -445,14 +407,14 @@ func (c *VFSCAStore) AddSSHPublicKey(name string, pubkey []byte) error { return p.WriteFile(bytes.NewReader(pubkey), acl) } -func (c *VFSCAStore) buildSSHPublicKeyPath(name string, id string) vfs.Path { +func (c *VFSCAStore) buildSSHPublicKeyPath(id string) vfs.Path { // id is fingerprint with colons, but we store without colons id = strings.Replace(id, ":", "", -1) - return c.basedir.Join("ssh", "public", name, id) + return c.basedir.Join("ssh", "public", "admin", id) } -func (c *VFSCAStore) FindSSHPublicKeys(name string) ([]*kops.SSHCredential, error) { - p := c.basedir.Join("ssh", "public", name) +func (c *VFSCAStore) FindSSHPublicKeys() ([]*kops.SSHCredential, error) { + p := c.basedir.Join("ssh", "public", "admin") files, err := p.ReadDir() if err != nil { @@ -475,7 +437,7 @@ func (c *VFSCAStore) FindSSHPublicKeys(name string) ([]*kops.SSHCredential, erro } item := &kops.SSHCredential{} - item.Name = name + item.Name = "admin" item.Spec.PublicKey = string(data) items = append(items, item) } @@ -483,14 +445,20 @@ func (c *VFSCAStore) FindSSHPublicKeys(name string) ([]*kops.SSHCredential, erro return items, nil } -func (c *VFSCAStore) DeleteSSHCredential(item *kops.SSHCredential) error { - if item.Spec.PublicKey == "" { - return fmt.Errorf("must specific public key to delete SSHCredential") - } - id, err := sshcredentials.Fingerprint(item.Spec.PublicKey) +func (c *VFSCAStore) DeleteSSHCredential() error { + p := c.basedir.Join("ssh", "public", "admin") + + files, err := p.ReadDir() if err != nil { - return fmt.Errorf("invalid PublicKey when deleting SSHCredential: %v", err) + if os.IsNotExist(err) { + return nil + } + return err } - p := c.buildSSHPublicKeyPath(item.Name, id) - return p.Remove() + for _, f := range files { + if err := f.Remove(); err != nil { + return err + } + } + return nil }