diff --git a/cmd/kops-controller/main.go b/cmd/kops-controller/main.go index c61136167b..5a129a78ac 100644 --- a/cmd/kops-controller/main.go +++ b/cmd/kops-controller/main.go @@ -245,6 +245,9 @@ func addNodeController(mgr manager.Manager, opt *config.Options) error { if opt.ConfigBase == "" { return fmt.Errorf("must specify configBase") } + if opt.SecretStore == "" { + return fmt.Errorf("must specify secretStore") + } nodeController, err := controllers.NewLegacyNodeReconciler(mgr, opt.ConfigBase, legacyIdentifier) if err != nil { diff --git a/cmd/kops-controller/pkg/config/options.go b/cmd/kops-controller/pkg/config/options.go index 6e1e81867b..069165f125 100644 --- a/cmd/kops-controller/pkg/config/options.go +++ b/cmd/kops-controller/pkg/config/options.go @@ -25,6 +25,7 @@ import ( type Options struct { Cloud string `json:"cloud,omitempty"` ConfigBase string `json:"configBase,omitempty"` + SecretStore string `json:"secretStore,omitempty"` Server *ServerOptions `json:"server,omitempty"` CacheNodeidentityInfo bool `json:"cacheNodeidentityInfo,omitempty"` diff --git a/cmd/kops-controller/pkg/server/node_config.go b/cmd/kops-controller/pkg/server/node_config.go index 53a1d26152..b1a3bb127d 100644 --- a/cmd/kops-controller/pkg/server/node_config.go +++ b/cmd/kops-controller/pkg/server/node_config.go @@ -61,5 +61,21 @@ func (s *Server) getNodeConfig(ctx context.Context, req *nodeup.BootstrapRequest nodeConfig.NodeupConfig = string(b) } + { + secretIDs := []string{ + "dockerconfig", + } + nodeConfig.NodeSecrets = make(map[string][]byte) + for _, id := range secretIDs { + secret, err := s.secretStore.FindSecret(id) + if err != nil { + return nil, fmt.Errorf("error loading secret %q: %w", id, err) + } + if secret != nil && secret.Data != nil { + nodeConfig.NodeSecrets[id] = secret.Data + } + } + } + return nodeConfig, nil } diff --git a/cmd/kops-controller/pkg/server/server.go b/cmd/kops-controller/pkg/server/server.go index 36eb9c2aa6..b029cf2c54 100644 --- a/cmd/kops-controller/pkg/server/server.go +++ b/cmd/kops-controller/pkg/server/server.go @@ -38,16 +38,18 @@ import ( "k8s.io/kops/pkg/pki" "k8s.io/kops/pkg/rbac" "k8s.io/kops/upup/pkg/fi" + "k8s.io/kops/upup/pkg/fi/secrets" "k8s.io/kops/util/pkg/vfs" ) type Server struct { - opt *config.Options - certNames sets.String - keypairIDs map[string]string - server *http.Server - verifier bootstrap.Verifier - keystore pki.Keystore + opt *config.Options + certNames sets.String + keypairIDs map[string]string + server *http.Server + verifier bootstrap.Verifier + keystore pki.Keystore + secretStore fi.SecretStore // configBase is the base of the configuration storage. configBase vfs.Path @@ -71,10 +73,16 @@ func NewServer(opt *config.Options, verifier bootstrap.Verifier) (*Server, error configBase, err := vfs.Context.BuildVfsPath(opt.ConfigBase) if err != nil { - return nil, fmt.Errorf("cannot parse ConfigBase %q: %v", opt.ConfigBase, err) + return nil, fmt.Errorf("cannot parse ConfigBase %q: %w", opt.ConfigBase, err) } s.configBase = configBase + p, err := vfs.Context.BuildVfsPath(opt.SecretStore) + if err != nil { + return nil, fmt.Errorf("cannot parse SecretStore %q: %w", opt.SecretStore, err) + } + s.secretStore = secrets.NewVFSSecretStore(nil, p) + r := http.NewServeMux() r.Handle("/bootstrap", http.HandlerFunc(s.bootstrap)) server.Handler = recovery(r) diff --git a/pkg/apis/nodeup/bootstrap.go b/pkg/apis/nodeup/bootstrap.go index b77730149b..78018e6460 100644 --- a/pkg/apis/nodeup/bootstrap.go +++ b/pkg/apis/nodeup/bootstrap.go @@ -35,7 +35,7 @@ type BootstrapRequest struct { // BootstrapResponse is a response to a BootstrapRequest. type BootstrapResponse struct { // Certs are the issued certificates. - Certs map[string]string + Certs map[string]string `json:"Certs,omitempty"` // NodeConfig contains the node configuration, if IncludeNodeConfig is set. NodeConfig *NodeConfig `json:"nodeConfig,omitempty"` @@ -48,6 +48,9 @@ type NodeConfig struct { // NodeupConfig holds the nodeup.Config for the node's instance group. NodeupConfig string `json:"nodeupConfig,omitempty"` + + // NodeSecrets holds the secrets for the node (like `dockerconfig`). + NodeSecrets map[string][]byte `json:"nodeSecrets,omitempty"` } // NodeConfigCertificate holds a certificate that the node needs to boot. diff --git a/pkg/configserver/secretstore.go b/pkg/configserver/secretstore.go index d116d978f6..a12f21b927 100644 --- a/pkg/configserver/secretstore.go +++ b/pkg/configserver/secretstore.go @@ -19,25 +19,31 @@ package configserver import ( "fmt" - "k8s.io/kops/pkg/apis/nodeup" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/util/pkg/vfs" ) // configserverSecretStore is a SecretStore backed by the config server. type configserverSecretStore struct { - nodeConfig *nodeup.NodeConfig + nodeSecrets map[string][]byte } -func NewSecretStore(nodeConfig *nodeup.NodeConfig) fi.SecretStore { +func NewSecretStore(nodeSecrets map[string][]byte) fi.SecretStore { return &configserverSecretStore{ - nodeConfig: nodeConfig, + nodeSecrets: nodeSecrets, } } // Secret implements fi.SecretStore func (s *configserverSecretStore) Secret(id string) (*fi.Secret, error) { - return nil, fmt.Errorf("Secret not supported by configserverSecretStore") + secret, err := s.FindSecret(id) + if err != nil { + return nil, err + } + if secret == nil { + return nil, fmt.Errorf("secret %q not found", id) + } + return secret, nil } // DeleteSecret implements fi.SecretStore @@ -47,7 +53,14 @@ func (s *configserverSecretStore) DeleteSecret(id string) error { // FindSecret implements fi.SecretStore func (s *configserverSecretStore) FindSecret(id string) (*fi.Secret, error) { - return nil, fmt.Errorf("FindSecret not supported by configserverSecretStore") + secretBytes, ok := s.nodeSecrets[id] + if !ok { + return nil, nil + } + secret := &fi.Secret{ + Data: secretBytes, + } + return secret, nil } // GetOrCreateSecret implements fi.SecretStore diff --git a/upup/pkg/fi/cloudup/template_functions.go b/upup/pkg/fi/cloudup/template_functions.go index 11122b96b6..31a923b74c 100644 --- a/upup/pkg/fi/cloudup/template_functions.go +++ b/upup/pkg/fi/cloudup/template_functions.go @@ -582,8 +582,9 @@ func (tf *TemplateFunctions) KopsControllerConfig() (string, error) { cluster := tf.Cluster config := &kopscontrollerconfig.Options{ - Cloud: string(cluster.Spec.GetCloudProvider()), - ConfigBase: cluster.Spec.ConfigBase, + Cloud: string(cluster.Spec.GetCloudProvider()), + ConfigBase: cluster.Spec.ConfigBase, + SecretStore: cluster.Spec.SecretStore, } if featureflag.CacheNodeidentityInfo.Enabled() { diff --git a/upup/pkg/fi/nodeup/command.go b/upup/pkg/fi/nodeup/command.go index 44c9772456..34479a01f2 100644 --- a/upup/pkg/fi/nodeup/command.go +++ b/upup/pkg/fi/nodeup/command.go @@ -235,7 +235,7 @@ func (c *NodeUpCommand) Run(out io.Writer) error { var secretStore fi.SecretStore var keyStore fi.Keystore if nodeConfig != nil { - modelContext.SecretStore = configserver.NewSecretStore(nodeConfig) + modelContext.SecretStore = configserver.NewSecretStore(nodeConfig.NodeSecrets) } else if c.cluster.Spec.SecretStore != "" { klog.Infof("Building SecretStore at %q", c.cluster.Spec.SecretStore) p, err := vfs.Context.BuildVfsPath(c.cluster.Spec.SecretStore) diff --git a/upup/pkg/fi/secrets/vfs_secretstore.go b/upup/pkg/fi/secrets/vfs_secretstore.go index e3ec5dce87..75bf8408dd 100644 --- a/upup/pkg/fi/secrets/vfs_secretstore.go +++ b/upup/pkg/fi/secrets/vfs_secretstore.go @@ -133,7 +133,7 @@ func (c *VFSSecretStore) Secret(id string) (*fi.Secret, error) { return nil, err } if s == nil { - return nil, fmt.Errorf("Secret not found: %q", id) + return nil, fmt.Errorf("secret %q not found", id) } return s, nil }