mirror of https://github.com/kubernetes/kops.git
Reduce the lifetime of exported kubecfg credentials
This commit is contained in:
parent
90ec586ef3
commit
a45b07c156
|
|
@ -37,6 +37,7 @@ import (
|
|||
"k8s.io/kops/pkg/assets"
|
||||
"k8s.io/kops/pkg/commands"
|
||||
"k8s.io/kops/pkg/featureflag"
|
||||
"k8s.io/kops/pkg/kubeconfig"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup"
|
||||
"k8s.io/kops/upup/pkg/fi/utils"
|
||||
|
|
@ -613,7 +614,7 @@ func RunCreateCluster(ctx context.Context, f *util.Factory, out io.Writer, c *Cr
|
|||
updateClusterOptions.Yes = c.Yes
|
||||
updateClusterOptions.Target = c.Target
|
||||
updateClusterOptions.OutDir = c.OutDir
|
||||
updateClusterOptions.admin = true
|
||||
updateClusterOptions.admin = kubeconfig.DefaultKubecfgAdminLifetime
|
||||
updateClusterOptions.CreateKubecfg = true
|
||||
|
||||
// SSHPublicKey has already been mapped
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
|
@ -53,7 +54,7 @@ var (
|
|||
type ExportKubecfgOptions struct {
|
||||
KubeConfigPath string
|
||||
all bool
|
||||
admin bool
|
||||
admin time.Duration
|
||||
user string
|
||||
}
|
||||
|
||||
|
|
@ -76,7 +77,8 @@ func NewCmdExportKubecfg(f *util.Factory, out io.Writer) *cobra.Command {
|
|||
|
||||
cmd.Flags().StringVar(&options.KubeConfigPath, "kubeconfig", options.KubeConfigPath, "the location of the kubeconfig file to create.")
|
||||
cmd.Flags().BoolVar(&options.all, "all", options.all, "export all clusters from the kops state store")
|
||||
cmd.Flags().BoolVar(&options.admin, "admin", options.admin, "export the cluster admin user and add it to the context")
|
||||
cmd.Flags().DurationVar(&options.admin, "admin", options.admin, "export a cluster admin user credential with the given lifetime and add it to the cluster context")
|
||||
cmd.Flags().Lookup("admin").NoOptDefVal = kubeconfig.DefaultKubecfgAdminLifetime.String()
|
||||
cmd.Flags().StringVar(&options.user, "user", options.user, "add an existing user to the cluster context")
|
||||
|
||||
return cmd
|
||||
|
|
@ -92,7 +94,7 @@ func RunExportKubecfg(ctx context.Context, f *util.Factory, out io.Writer, optio
|
|||
return fmt.Errorf("cannot use both --all flag and positional arguments")
|
||||
}
|
||||
}
|
||||
if options.admin && options.user != "" {
|
||||
if options.admin != 0 && options.user != "" {
|
||||
return fmt.Errorf("cannot use both --admin and --user")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
|
@ -67,7 +68,7 @@ type UpdateClusterOptions struct {
|
|||
AllowKopsDowngrade bool
|
||||
|
||||
CreateKubecfg bool
|
||||
admin bool
|
||||
admin time.Duration
|
||||
user string
|
||||
|
||||
Phase string
|
||||
|
|
@ -116,7 +117,8 @@ func NewCmdUpdateCluster(f *util.Factory, out io.Writer) *cobra.Command {
|
|||
cmd.Flags().StringVar(&options.SSHPublicKey, "ssh-public-key", options.SSHPublicKey, "SSH public key to use (deprecated: use kops create secret instead)")
|
||||
cmd.Flags().StringVar(&options.OutDir, "out", options.OutDir, "Path to write any local output")
|
||||
cmd.Flags().BoolVar(&options.CreateKubecfg, "create-kube-config", options.CreateKubecfg, "Will control automatically creating the kube config file on your local filesystem")
|
||||
cmd.Flags().BoolVar(&options.admin, "admin", options.admin, "Also export the admin user. Implies --create-kube-config")
|
||||
cmd.Flags().DurationVar(&options.admin, "admin", options.admin, "Also export a cluster admin user credential with the specified lifetime and add it to the cluster context")
|
||||
cmd.Flags().Lookup("admin").NoOptDefVal = kubeconfig.DefaultKubecfgAdminLifetime.String()
|
||||
cmd.Flags().StringVar(&options.user, "user", options.user, "Existing user to add to the cluster context. Implies --create-kube-config")
|
||||
cmd.Flags().BoolVar(&options.AllowKopsDowngrade, "allow-kops-downgrade", options.AllowKopsDowngrade, "Allow an older version of kops to update the cluster than last used")
|
||||
cmd.Flags().StringVar(&options.Phase, "phase", options.Phase, "Subset of tasks to run: "+strings.Join(cloudup.Phases.List(), ", "))
|
||||
|
|
@ -141,15 +143,15 @@ func RunUpdateCluster(ctx context.Context, f *util.Factory, clusterName string,
|
|||
isDryrun := false
|
||||
targetName := c.Target
|
||||
|
||||
if c.admin && c.user != "" {
|
||||
if c.admin != 0 && c.user != "" {
|
||||
return nil, fmt.Errorf("cannot use both --admin and --user")
|
||||
}
|
||||
|
||||
if c.CreateKubecfg && !c.admin && c.user == "" {
|
||||
if c.CreateKubecfg && c.admin == 0 && c.user == "" {
|
||||
return nil, fmt.Errorf("--create-kube-config requires that either --admin or --user is set")
|
||||
}
|
||||
|
||||
if c.admin && !c.CreateKubecfg {
|
||||
if c.admin != 0 && !c.CreateKubecfg {
|
||||
klog.Info("--admin implies --create-kube-config")
|
||||
c.CreateKubecfg = true
|
||||
}
|
||||
|
|
@ -296,25 +298,15 @@ func RunUpdateCluster(ctx context.Context, f *util.Factory, clusterName string,
|
|||
}
|
||||
firstRun = !hasKubecfg
|
||||
|
||||
kubecfgCert, err := keyStore.FindCert("kubecfg")
|
||||
klog.Infof("Exporting kubecfg for cluster")
|
||||
conf, err := kubeconfig.BuildKubecfg(cluster, keyStore, secretStore, &commands.CloudDiscoveryStatusStore{}, clientcmd.NewDefaultPathOptions(), c.admin, c.user)
|
||||
if err != nil {
|
||||
// This is only a convenience; don't error because of it
|
||||
klog.Warningf("Ignoring error trying to fetch kubecfg cert - won't export kubecfg: %v", err)
|
||||
kubecfgCert = nil
|
||||
return nil, err
|
||||
}
|
||||
if kubecfgCert != nil {
|
||||
klog.Infof("Exporting kubecfg for cluster")
|
||||
conf, err := kubeconfig.BuildKubecfg(cluster, keyStore, secretStore, &commands.CloudDiscoveryStatusStore{}, clientcmd.NewDefaultPathOptions(), c.admin, c.user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = conf.WriteKubecfg()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
klog.Infof("kubecfg cert not found; won't export kubecfg")
|
||||
err = conf.WriteKubecfg()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,11 +26,11 @@ kops export kubecfg CLUSTERNAME [flags]
|
|||
### Options
|
||||
|
||||
```
|
||||
--admin export the cluster admin user and add it to the context
|
||||
--all export all clusters from the kops state store
|
||||
-h, --help help for kubecfg
|
||||
--kubeconfig string the location of the kubeconfig file to create.
|
||||
--user string add an existing user to the cluster context
|
||||
--admin duration[=18h0m0s] export a cluster admin user credential with the given lifetime and add it to the cluster context
|
||||
--all export all clusters from the kops state store
|
||||
-h, --help help for kubecfg
|
||||
--kubeconfig string the location of the kubeconfig file to create.
|
||||
--user string add an existing user to the cluster context
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ kops update cluster [flags]
|
|||
### Options
|
||||
|
||||
```
|
||||
--admin Also export the admin user. Implies --create-kube-config
|
||||
--admin duration[=18h0m0s] Also export a cluster admin user credential with the specified lifetime and add it to the cluster context
|
||||
--allow-kops-downgrade Allow an older version of kops to update the cluster than last used
|
||||
--create-kube-config Will control automatically creating the kube config file on your local filesystem
|
||||
-h, --help help for cluster
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@ Kops will no longer automatically export the kubernetes config on `kops update c
|
|||
|
||||
Similarly, `kops export kubecfg` will also require passing either the `--admin` or `--user` flag if the context does not already exist.
|
||||
|
||||
`kops create cluster --yes` exports the admin user along with rest of the cluster config, as is existing behaviour.
|
||||
By default, the credentials of any exported admin user now have a lifetime of 18 hours. The lifetime of the exported
|
||||
credentials may be specified as a value of the `--admin` flag. To get the previous behavior, specify `--admin=87600h` to either `kops update cluster` or `kops export kubecfg`.
|
||||
|
||||
`kops create cluster --yes` exports the admin user along with rest of the cluster config, as was the previous behaviour (except for the 18-hour validity).
|
||||
|
||||
## Other significant changes
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ go_library(
|
|||
"//pkg/apis/kops:go_default_library",
|
||||
"//pkg/apis/kops/util:go_default_library",
|
||||
"//pkg/dns:go_default_library",
|
||||
"//pkg/pki:go_default_library",
|
||||
"//pkg/rbac:go_default_library",
|
||||
"//upup/pkg/fi:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
|
|
|
|||
|
|
@ -17,18 +17,24 @@ limitations under the License.
|
|||
package kubeconfig
|
||||
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/klog"
|
||||
"k8s.io/kops/pkg/apis/kops"
|
||||
"k8s.io/kops/pkg/apis/kops/util"
|
||||
"k8s.io/kops/pkg/dns"
|
||||
"k8s.io/kops/pkg/pki"
|
||||
"k8s.io/kops/pkg/rbac"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
)
|
||||
|
||||
func BuildKubecfg(cluster *kops.Cluster, keyStore fi.Keystore, secretStore fi.SecretStore, status kops.StatusStore, configAccess clientcmd.ConfigAccess, admin bool, user string) (*KubeconfigBuilder, error) {
|
||||
const DefaultKubecfgAdminLifetime = 18 * time.Hour
|
||||
|
||||
func BuildKubecfg(cluster *kops.Cluster, keyStore fi.Keystore, secretStore fi.SecretStore, status kops.StatusStore, configAccess clientcmd.ConfigAccess, admin time.Duration, user string) (*KubeconfigBuilder, error) {
|
||||
clusterName := cluster.ObjectMeta.Name
|
||||
|
||||
master := cluster.Spec.MasterPublicName
|
||||
|
|
@ -104,30 +110,29 @@ func BuildKubecfg(cluster *kops.Cluster, keyStore fi.Keystore, secretStore fi.Se
|
|||
}
|
||||
}
|
||||
|
||||
if admin {
|
||||
{
|
||||
cert, key, _, err := keyStore.FindKeypair("kubecfg")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching kubecfg keypair: %v", err)
|
||||
}
|
||||
if cert != nil {
|
||||
b.ClientCert, err = cert.AsBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("cannot find kubecfg certificate")
|
||||
}
|
||||
if key != nil {
|
||||
b.ClientKey, err = key.AsBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("cannot find kubecfg key")
|
||||
}
|
||||
if admin != 0 {
|
||||
req := pki.IssueCertRequest{
|
||||
Signer: fi.CertificateIDCA,
|
||||
Type: "client",
|
||||
Subject: pkix.Name{
|
||||
CommonName: "kubecfg",
|
||||
Organization: []string{rbac.SystemPrivilegedGroup},
|
||||
},
|
||||
Validity: admin,
|
||||
}
|
||||
cert, privateKey, _, err := pki.IssueCert(&req, keyStore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b.ClientCert, err = cert.AsBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.ClientKey, err = privateKey.AsBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
b.Server = server
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package kubeconfig
|
|||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/kops/pkg/apis/kops"
|
||||
|
|
@ -113,13 +114,19 @@ func fakePrivateKey() *pki.PrivateKey {
|
|||
}
|
||||
|
||||
func TestBuildKubecfg(t *testing.T) {
|
||||
originalPKIDefaultPrivateKeySize := pki.DefaultPrivateKeySize
|
||||
pki.DefaultPrivateKeySize = 512
|
||||
defer func() {
|
||||
pki.DefaultPrivateKeySize = originalPKIDefaultPrivateKeySize
|
||||
}()
|
||||
|
||||
type args struct {
|
||||
cluster *kops.Cluster
|
||||
keyStore fakeKeyStore
|
||||
secretStore fi.SecretStore
|
||||
status fakeStatusStore
|
||||
configAccess clientcmd.ConfigAccess
|
||||
admin bool
|
||||
admin time.Duration
|
||||
user string
|
||||
}
|
||||
|
||||
|
|
@ -148,16 +155,14 @@ func TestBuildKubecfg(t *testing.T) {
|
|||
nil,
|
||||
fakeStatusStore{},
|
||||
nil,
|
||||
true,
|
||||
DefaultKubecfgAdminLifetime,
|
||||
"",
|
||||
},
|
||||
&KubeconfigBuilder{
|
||||
Context: "testcluster",
|
||||
Server: "https://testcluster.test.com",
|
||||
CACert: []byte(certData),
|
||||
ClientCert: []byte(certData),
|
||||
ClientKey: []byte(privatekeyData),
|
||||
User: "testcluster",
|
||||
Context: "testcluster",
|
||||
Server: "https://testcluster.test.com",
|
||||
CACert: []byte(certData),
|
||||
User: "testcluster",
|
||||
},
|
||||
false,
|
||||
},
|
||||
|
|
@ -176,16 +181,14 @@ func TestBuildKubecfg(t *testing.T) {
|
|||
nil,
|
||||
fakeStatusStore{},
|
||||
nil,
|
||||
false,
|
||||
0,
|
||||
"myuser",
|
||||
},
|
||||
&KubeconfigBuilder{
|
||||
Context: "testcluster",
|
||||
Server: "https://testcluster.test.com",
|
||||
CACert: []byte(certData),
|
||||
ClientCert: nil,
|
||||
ClientKey: nil,
|
||||
User: "myuser",
|
||||
Context: "testcluster",
|
||||
Server: "https://testcluster.test.com",
|
||||
CACert: []byte(certData),
|
||||
User: "myuser",
|
||||
},
|
||||
false,
|
||||
},
|
||||
|
|
@ -204,16 +207,14 @@ func TestBuildKubecfg(t *testing.T) {
|
|||
nil,
|
||||
fakeStatusStore{},
|
||||
nil,
|
||||
true,
|
||||
0,
|
||||
"",
|
||||
},
|
||||
&KubeconfigBuilder{
|
||||
Context: "emptyMasterPublicNameCluster",
|
||||
Server: "https://api.emptyMasterPublicNameCluster",
|
||||
CACert: []byte(certData),
|
||||
ClientCert: []byte(certData),
|
||||
ClientKey: []byte(privatekeyData),
|
||||
User: "emptyMasterPublicNameCluster",
|
||||
Context: "emptyMasterPublicNameCluster",
|
||||
Server: "https://api.emptyMasterPublicNameCluster",
|
||||
CACert: []byte(certData),
|
||||
User: "emptyMasterPublicNameCluster",
|
||||
},
|
||||
false,
|
||||
},
|
||||
|
|
@ -240,16 +241,14 @@ func TestBuildKubecfg(t *testing.T) {
|
|||
},
|
||||
},
|
||||
nil,
|
||||
true,
|
||||
0,
|
||||
"",
|
||||
},
|
||||
&KubeconfigBuilder{
|
||||
Context: "testgossipcluster.k8s.local",
|
||||
Server: "https://elbHostName",
|
||||
CACert: []byte(certData),
|
||||
ClientCert: []byte(certData),
|
||||
ClientKey: []byte(privatekeyData),
|
||||
User: "testgossipcluster.k8s.local",
|
||||
Context: "testgossipcluster.k8s.local",
|
||||
Server: "https://elbHostName",
|
||||
CACert: []byte(certData),
|
||||
User: "testgossipcluster.k8s.local",
|
||||
},
|
||||
false,
|
||||
},
|
||||
|
|
@ -261,6 +260,16 @@ func TestBuildKubecfg(t *testing.T) {
|
|||
t.Errorf("BuildKubecfg() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if tt.args.admin != 0 {
|
||||
if got.ClientCert == nil {
|
||||
t.Errorf("Expected ClientCert, got nil")
|
||||
}
|
||||
if got.ClientKey == nil {
|
||||
t.Errorf("Expected ClientKey, got nil")
|
||||
}
|
||||
tt.want.ClientCert = got.ClientCert
|
||||
tt.want.ClientKey = got.ClientKey
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("BuildKubecfg() = %v, want %v", got, tt.want)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,17 +150,6 @@ func (b *PKIModelBuilder) Build(c *fi.ModelBuilderContext) error {
|
|||
c.AddTask(t)
|
||||
}
|
||||
|
||||
{
|
||||
t := &fitasks.Keypair{
|
||||
Name: fi.String("kubecfg"),
|
||||
Lifecycle: b.Lifecycle,
|
||||
Subject: "o=" + rbac.SystemPrivilegedGroup + ",cn=kubecfg",
|
||||
Type: "client",
|
||||
Signer: defaultCA,
|
||||
}
|
||||
c.AddTask(t)
|
||||
}
|
||||
|
||||
{
|
||||
aggregatorCA := &fitasks.Keypair{
|
||||
Name: fi.String("apiserver-aggregator-ca"),
|
||||
|
|
|
|||
Loading…
Reference in New Issue