Block bootstrap when the node already exists

We now do this across all clouds, as it has been demonstrated on
OpenStack.
This commit is contained in:
justinsb 2023-04-27 11:39:19 -04:00
parent 2875f70cb5
commit 868823bbcf
2 changed files with 44 additions and 6 deletions

View File

@ -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)

View File

@ -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)