From 62182f3bcf15504a20d2a8c146be10f83954ae39 Mon Sep 17 00:00:00 2001 From: Tom Proctor Date: Tue, 6 May 2025 14:52:16 +0100 Subject: [PATCH] cmd/k8s-operator,k8s-operator/api-proxy: move k8s proxy code to library (#15857) The defaultEnv and defaultBool functions are copied over temporarily to minimise diff. This lays the ground work for having both the operator and the new k8s-proxy binary implement the API proxy Updates #13358 Change-Id: Ieacc79af64df2f13b27a18135517bb31c80a5a02 Signed-off-by: Tom Proctor --- cmd/k8s-operator/depaware.txt | 5 +- cmd/k8s-operator/operator.go | 7 +-- k8s-operator/api-proxy/doc.go | 8 ++++ k8s-operator/api-proxy/env.go | 29 ++++++++++++ .../api-proxy}/proxy.go | 46 +++++++++---------- .../api-proxy}/proxy_test.go | 2 +- 6 files changed, 68 insertions(+), 29 deletions(-) create mode 100644 k8s-operator/api-proxy/doc.go create mode 100644 k8s-operator/api-proxy/env.go rename {cmd/k8s-operator => k8s-operator/api-proxy}/proxy.go (93%) rename {cmd/k8s-operator => k8s-operator/api-proxy}/proxy_test.go (99%) diff --git a/cmd/k8s-operator/depaware.txt b/cmd/k8s-operator/depaware.txt index 186c5a0c0..544fe9089 100644 --- a/cmd/k8s-operator/depaware.txt +++ b/cmd/k8s-operator/depaware.txt @@ -840,9 +840,10 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/ tailscale.com/ipn/store/kubestore from tailscale.com/cmd/k8s-operator+ tailscale.com/ipn/store/mem from tailscale.com/ipn/ipnlocal+ tailscale.com/k8s-operator from tailscale.com/cmd/k8s-operator + tailscale.com/k8s-operator/api-proxy from tailscale.com/cmd/k8s-operator tailscale.com/k8s-operator/apis from tailscale.com/k8s-operator/apis/v1alpha1 tailscale.com/k8s-operator/apis/v1alpha1 from tailscale.com/cmd/k8s-operator+ - tailscale.com/k8s-operator/sessionrecording from tailscale.com/cmd/k8s-operator + tailscale.com/k8s-operator/sessionrecording from tailscale.com/k8s-operator/api-proxy tailscale.com/k8s-operator/sessionrecording/spdy from tailscale.com/k8s-operator/sessionrecording tailscale.com/k8s-operator/sessionrecording/tsrecorder from tailscale.com/k8s-operator/sessionrecording+ tailscale.com/k8s-operator/sessionrecording/ws from tailscale.com/k8s-operator/sessionrecording @@ -945,7 +946,7 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/ tailscale.com/util/clientmetric from tailscale.com/cmd/k8s-operator+ tailscale.com/util/cloudenv from tailscale.com/hostinfo+ tailscale.com/util/cmpver from tailscale.com/clientupdate+ - tailscale.com/util/ctxkey from tailscale.com/cmd/k8s-operator+ + tailscale.com/util/ctxkey from tailscale.com/client/tailscale/apitype+ 💣 tailscale.com/util/deephash from tailscale.com/ipn/ipnlocal+ L 💣 tailscale.com/util/dirwalk from tailscale.com/metrics+ tailscale.com/util/dnsname from tailscale.com/appc+ diff --git a/cmd/k8s-operator/operator.go b/cmd/k8s-operator/operator.go index 1f637927b..9c35a7cec 100644 --- a/cmd/k8s-operator/operator.go +++ b/cmd/k8s-operator/operator.go @@ -45,6 +45,7 @@ import ( "tailscale.com/hostinfo" "tailscale.com/ipn" "tailscale.com/ipn/store/kubestore" + apiproxy "tailscale.com/k8s-operator/api-proxy" tsapi "tailscale.com/k8s-operator/apis/v1alpha1" "tailscale.com/kube/kubetypes" "tailscale.com/tsnet" @@ -102,8 +103,8 @@ func main() { // The operator can run either as a plain operator or it can // additionally act as api-server proxy // https://tailscale.com/kb/1236/kubernetes-operator/?q=kubernetes#accessing-the-kubernetes-control-plane-using-an-api-server-proxy. - mode := parseAPIProxyMode() - if mode == apiserverProxyModeDisabled { + mode := apiproxy.ParseAPIProxyMode() + if mode == apiproxy.APIServerProxyModeDisabled { hostinfo.SetApp(kubetypes.AppOperator) } else { hostinfo.SetApp(kubetypes.AppAPIServerProxy) @@ -112,7 +113,7 @@ func main() { s, tsc := initTSNet(zlog) defer s.Close() restConfig := config.GetConfigOrDie() - maybeLaunchAPIServerProxy(zlog, restConfig, s, mode) + apiproxy.MaybeLaunchAPIServerProxy(zlog, restConfig, s, mode) rOpts := reconcilerOpts{ log: zlog, tsServer: s, diff --git a/k8s-operator/api-proxy/doc.go b/k8s-operator/api-proxy/doc.go new file mode 100644 index 000000000..89d890959 --- /dev/null +++ b/k8s-operator/api-proxy/doc.go @@ -0,0 +1,8 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +//go:build !plan9 + +// Package apiproxy contains the Kubernetes API Proxy implementation used by +// k8s-operator and k8s-proxy. +package apiproxy diff --git a/k8s-operator/api-proxy/env.go b/k8s-operator/api-proxy/env.go new file mode 100644 index 000000000..c0640ab1e --- /dev/null +++ b/k8s-operator/api-proxy/env.go @@ -0,0 +1,29 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +//go:build !plan9 + +package apiproxy + +import ( + "os" + + "tailscale.com/types/opt" +) + +func defaultBool(envName string, defVal bool) bool { + vs := os.Getenv(envName) + if vs == "" { + return defVal + } + v, _ := opt.Bool(vs).Get() + return v +} + +func defaultEnv(envName, defVal string) string { + v := os.Getenv(envName) + if v == "" { + return defVal + } + return v +} diff --git a/cmd/k8s-operator/proxy.go b/k8s-operator/api-proxy/proxy.go similarity index 93% rename from cmd/k8s-operator/proxy.go rename to k8s-operator/api-proxy/proxy.go index 01383a53d..7c7260b94 100644 --- a/cmd/k8s-operator/proxy.go +++ b/k8s-operator/api-proxy/proxy.go @@ -3,7 +3,7 @@ //go:build !plan9 -package main +package apiproxy import ( "crypto/tls" @@ -37,15 +37,15 @@ var ( whoIsKey = ctxkey.New("", (*apitype.WhoIsResponse)(nil)) ) -type apiServerProxyMode int +type APIServerProxyMode int -func (a apiServerProxyMode) String() string { +func (a APIServerProxyMode) String() string { switch a { - case apiserverProxyModeDisabled: + case APIServerProxyModeDisabled: return "disabled" - case apiserverProxyModeEnabled: + case APIServerProxyModeEnabled: return "auth" - case apiserverProxyModeNoAuth: + case APIServerProxyModeNoAuth: return "noauth" default: return "unknown" @@ -53,12 +53,12 @@ func (a apiServerProxyMode) String() string { } const ( - apiserverProxyModeDisabled apiServerProxyMode = iota - apiserverProxyModeEnabled - apiserverProxyModeNoAuth + APIServerProxyModeDisabled APIServerProxyMode = iota + APIServerProxyModeEnabled + APIServerProxyModeNoAuth ) -func parseAPIProxyMode() apiServerProxyMode { +func ParseAPIProxyMode() APIServerProxyMode { haveAuthProxyEnv := os.Getenv("AUTH_PROXY") != "" haveAPIProxyEnv := os.Getenv("APISERVER_PROXY") != "" switch { @@ -67,34 +67,34 @@ func parseAPIProxyMode() apiServerProxyMode { case haveAuthProxyEnv: var authProxyEnv = defaultBool("AUTH_PROXY", false) // deprecated if authProxyEnv { - return apiserverProxyModeEnabled + return APIServerProxyModeEnabled } - return apiserverProxyModeDisabled + return APIServerProxyModeDisabled case haveAPIProxyEnv: var apiProxyEnv = defaultEnv("APISERVER_PROXY", "") // true, false or "noauth" switch apiProxyEnv { case "true": - return apiserverProxyModeEnabled + return APIServerProxyModeEnabled case "false", "": - return apiserverProxyModeDisabled + return APIServerProxyModeDisabled case "noauth": - return apiserverProxyModeNoAuth + return APIServerProxyModeNoAuth default: panic(fmt.Sprintf("unknown APISERVER_PROXY value %q", apiProxyEnv)) } } - return apiserverProxyModeDisabled + return APIServerProxyModeDisabled } // maybeLaunchAPIServerProxy launches the auth proxy, which is a small HTTP server // that authenticates requests using the Tailscale LocalAPI and then proxies // them to the kube-apiserver. -func maybeLaunchAPIServerProxy(zlog *zap.SugaredLogger, restConfig *rest.Config, s *tsnet.Server, mode apiServerProxyMode) { - if mode == apiserverProxyModeDisabled { +func MaybeLaunchAPIServerProxy(zlog *zap.SugaredLogger, restConfig *rest.Config, s *tsnet.Server, mode APIServerProxyMode) { + if mode == APIServerProxyModeDisabled { return } startlog := zlog.Named("launchAPIProxy") - if mode == apiserverProxyModeNoAuth { + if mode == APIServerProxyModeNoAuth { restConfig = rest.AnonymousClientConfig(restConfig) } cfg, err := restConfig.TransportConfig() @@ -132,8 +132,8 @@ func maybeLaunchAPIServerProxy(zlog *zap.SugaredLogger, restConfig *rest.Config, // are passed through to the Kubernetes API. // // It never returns. -func runAPIServerProxy(ts *tsnet.Server, rt http.RoundTripper, log *zap.SugaredLogger, mode apiServerProxyMode, host string) { - if mode == apiserverProxyModeDisabled { +func runAPIServerProxy(ts *tsnet.Server, rt http.RoundTripper, log *zap.SugaredLogger, mode APIServerProxyMode, host string) { + if mode == APIServerProxyModeDisabled { return } ln, err := ts.Listen("tcp", ":443") @@ -192,7 +192,7 @@ type apiserverProxy struct { lc *local.Client rp *httputil.ReverseProxy - mode apiServerProxyMode + mode APIServerProxyMode ts *tsnet.Server upstreamURL *url.URL } @@ -285,7 +285,7 @@ func (ap *apiserverProxy) execForProto(w http.ResponseWriter, r *http.Request, p func (h *apiserverProxy) addImpersonationHeadersAsRequired(r *http.Request) { r.URL.Scheme = h.upstreamURL.Scheme r.URL.Host = h.upstreamURL.Host - if h.mode == apiserverProxyModeNoAuth { + if h.mode == APIServerProxyModeNoAuth { // If we are not providing authentication, then we are just // proxying to the Kubernetes API, so we don't need to do // anything else. diff --git a/cmd/k8s-operator/proxy_test.go b/k8s-operator/api-proxy/proxy_test.go similarity index 99% rename from cmd/k8s-operator/proxy_test.go rename to k8s-operator/api-proxy/proxy_test.go index d1d5733e7..71bf65648 100644 --- a/cmd/k8s-operator/proxy_test.go +++ b/k8s-operator/api-proxy/proxy_test.go @@ -3,7 +3,7 @@ //go:build !plan9 -package main +package apiproxy import ( "net/http"