/* Copyright 2020 VMware, Inc. SPDX-License-Identifier: Apache-2.0 */ package config import ( "fmt" "io" "io/ioutil" "os" authenticationv1beta1 "k8s.io/api/authentication/v1beta1" utilnet "k8s.io/apimachinery/pkg/util/net" "k8s.io/apiserver/pkg/authentication/authenticator" "k8s.io/apiserver/plugin/pkg/authenticator/token/webhook" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "github.com/suzerain-io/placeholder-name/pkg/config/api" ) // NewWebhook creates a webhook from the provided API server url and caBundle // used to validate TLS connections. func NewWebhook(spec api.WebhookConfigSpec) (*webhook.WebhookTokenAuthenticator, error) { kubeconfig, err := ioutil.TempFile("", "placeholder-name-webhook-kubeconfig-*") if err != nil { return nil, fmt.Errorf("create temp file: %w", err) } defer os.Remove(kubeconfig.Name()) if err := anonymousKubeconfig(spec.URL, spec.CABundle, kubeconfig); err != nil { return nil, fmt.Errorf("anonymous kubeconfig: %w", err) } // We use v1beta1 instead of v1 since v1beta1 is more prevalent in our desired // integration points. version := authenticationv1beta1.SchemeGroupVersion.Version // At the current time, we don't provide any audiences because we simply don't // have any requirements to do so. This can be changed in the future as // requirements change. var implicitAuds authenticator.Audiences // We set this to nil because we would only need this to support some of the // custom proxy stuff used by the API server. var customDial utilnet.DialFunc return webhook.New(kubeconfig.Name(), version, implicitAuds, customDial) } // anonymousKubeconfig writes a kubeconfig file to the provided io.Writer that // will "use" anonymous auth to talk to a Kube API server at the provided url // with the provided caBundle. func anonymousKubeconfig(url string, caBundle []byte, out io.Writer) error { config := clientcmdapi.NewConfig() config.Clusters["anonymous-cluster"] = &clientcmdapi.Cluster{ Server: url, CertificateAuthorityData: caBundle, } config.Contexts["anonymous"] = &clientcmdapi.Context{ Cluster: "anonymous-cluster", } config.CurrentContext = "anonymous" data, err := clientcmd.Write(*config) if err != nil { return fmt.Errorf("marshal config: %w", err) } if _, err := out.Write(data); err != nil { return fmt.Errorf("write config: %w", err) } return nil }