From 868823bbcf89d7f4880f31f0737807ca112094e8 Mon Sep 17 00:00:00 2001 From: justinsb Date: Thu, 27 Apr 2023 11:39:19 -0400 Subject: [PATCH] Block bootstrap when the node already exists We now do this across all clouds, as it has been demonstrated on OpenStack. --- cmd/kops-controller/main.go | 12 +++++++- cmd/kops-controller/pkg/server/server.go | 38 ++++++++++++++++++++---- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/cmd/kops-controller/main.go b/cmd/kops-controller/main.go index b0224743e8..b321a72009 100644 --- a/cmd/kops-controller/main.go +++ b/cmd/kops-controller/main.go @@ -45,6 +45,7 @@ import ( "k8s.io/kops/upup/pkg/fi/cloudup/hetzner" "k8s.io/kops/upup/pkg/fi/cloudup/openstack" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/yaml" // +kubebuilder:scaffold:imports @@ -138,7 +139,16 @@ func main() { klog.Fatalf("server cloud provider config not provided") } - srv, err := server.NewServer(&opt, verifier) + uncachedClient, err := client.New(mgr.GetConfig(), client.Options{ + Scheme: mgr.GetScheme(), + Mapper: mgr.GetRESTMapper(), + }) + if err != nil { + setupLog.Error(err, "error creating uncached client") + os.Exit(1) + } + + srv, err := server.NewServer(&opt, verifier, uncachedClient) if err != nil { setupLog.Error(err, "unable to create server") os.Exit(1) diff --git a/cmd/kops-controller/pkg/server/server.go b/cmd/kops-controller/pkg/server/server.go index 392f1314c3..56598d6922 100644 --- a/cmd/kops-controller/pkg/server/server.go +++ b/cmd/kops-controller/pkg/server/server.go @@ -30,6 +30,9 @@ import ( "runtime/debug" "time" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/klog/v2" "k8s.io/kops/cmd/kops-controller/pkg/config" @@ -40,6 +43,7 @@ import ( "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/secrets" "k8s.io/kops/util/pkg/vfs" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/manager" ) @@ -54,11 +58,14 @@ type Server struct { // configBase is the base of the configuration storage. configBase vfs.Path + + // uncachedClient is an uncached client for the kube apiserver + uncachedClient client.Client } var _ manager.LeaderElectionRunnable = &Server{} -func NewServer(opt *config.Options, verifier bootstrap.Verifier) (*Server, error) { +func NewServer(opt *config.Options, verifier bootstrap.Verifier, uncachedClient client.Client) (*Server, error) { server := &http.Server{ Addr: opt.Server.Listen, TLSConfig: &tls.Config{ @@ -68,10 +75,11 @@ func NewServer(opt *config.Options, verifier bootstrap.Verifier) (*Server, error } s := &Server{ - opt: opt, - certNames: sets.NewString(opt.Server.CertNames...), - server: server, - verifier: verifier, + opt: opt, + certNames: sets.NewString(opt.Server.CertNames...), + server: server, + verifier: verifier, + uncachedClient: uncachedClient, } configBase, err := vfs.Context.BuildVfsPath(opt.ConfigBase) @@ -155,6 +163,26 @@ func (s *Server) bootstrap(w http.ResponseWriter, r *http.Request) { return } + // Once the node is registered, we don't allow further registrations, this protects against a pod or escaped workload attempting to impersonate the node. + { + node := &unstructured.Unstructured{} + node.SetAPIVersion("v1") + node.SetKind("node") + err := s.uncachedClient.Get(ctx, types.NamespacedName{Name: id.NodeName}, node) + if err == nil { + klog.Infof("bootstrap %s node %q already exists; denying to avoid node-impersonation attacks", r.RemoteAddr, id.NodeName) + w.WriteHeader(http.StatusForbidden) + _, _ = w.Write([]byte("node already registered")) + return + } + if err != nil && !errors.IsNotFound(err) { + klog.Infof("bootstrap %s error querying for node %q: %v", r.RemoteAddr, id.NodeName, err) + w.WriteHeader(http.StatusInternalServerError) + _, _ = w.Write([]byte("internal error")) + return + } + } + req := &nodeup.BootstrapRequest{} if err := json.Unmarshal(body, req); err != nil { klog.Infof("bootstrap %s decode err: %v", r.RemoteAddr, err)