cmd/k8s-operator,k8s-operator/api-proxy: move k8s proxy code to library

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 <tomhjp@users.noreply.github.com>
This commit is contained in:
Tom Proctor 2025-05-01 18:13:54 +01:00
parent 1f1c323eeb
commit 15a8d9ab56
5 changed files with 60 additions and 27 deletions

View File

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

View File

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

View File

@ -0,0 +1,24 @@
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
}

View File

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

View File

@ -3,7 +3,7 @@
//go:build !plan9
package main
package apiproxy
import (
"net/http"