mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-13 06:07:34 +00:00
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 <tomhjp@users.noreply.github.com>
This commit is contained in:
@@ -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/kubestore from tailscale.com/cmd/k8s-operator+
|
||||||
tailscale.com/ipn/store/mem from tailscale.com/ipn/ipnlocal+
|
tailscale.com/ipn/store/mem from tailscale.com/ipn/ipnlocal+
|
||||||
tailscale.com/k8s-operator from tailscale.com/cmd/k8s-operator
|
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 from tailscale.com/k8s-operator/apis/v1alpha1
|
||||||
tailscale.com/k8s-operator/apis/v1alpha1 from tailscale.com/cmd/k8s-operator+
|
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/spdy from tailscale.com/k8s-operator/sessionrecording
|
||||||
tailscale.com/k8s-operator/sessionrecording/tsrecorder 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
|
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/clientmetric from tailscale.com/cmd/k8s-operator+
|
||||||
tailscale.com/util/cloudenv from tailscale.com/hostinfo+
|
tailscale.com/util/cloudenv from tailscale.com/hostinfo+
|
||||||
tailscale.com/util/cmpver from tailscale.com/clientupdate+
|
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+
|
💣 tailscale.com/util/deephash from tailscale.com/ipn/ipnlocal+
|
||||||
L 💣 tailscale.com/util/dirwalk from tailscale.com/metrics+
|
L 💣 tailscale.com/util/dirwalk from tailscale.com/metrics+
|
||||||
tailscale.com/util/dnsname from tailscale.com/appc+
|
tailscale.com/util/dnsname from tailscale.com/appc+
|
||||||
|
@@ -45,6 +45,7 @@ import (
|
|||||||
"tailscale.com/hostinfo"
|
"tailscale.com/hostinfo"
|
||||||
"tailscale.com/ipn"
|
"tailscale.com/ipn"
|
||||||
"tailscale.com/ipn/store/kubestore"
|
"tailscale.com/ipn/store/kubestore"
|
||||||
|
apiproxy "tailscale.com/k8s-operator/api-proxy"
|
||||||
tsapi "tailscale.com/k8s-operator/apis/v1alpha1"
|
tsapi "tailscale.com/k8s-operator/apis/v1alpha1"
|
||||||
"tailscale.com/kube/kubetypes"
|
"tailscale.com/kube/kubetypes"
|
||||||
"tailscale.com/tsnet"
|
"tailscale.com/tsnet"
|
||||||
@@ -102,8 +103,8 @@ func main() {
|
|||||||
// The operator can run either as a plain operator or it can
|
// The operator can run either as a plain operator or it can
|
||||||
// additionally act as api-server proxy
|
// 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.
|
// https://tailscale.com/kb/1236/kubernetes-operator/?q=kubernetes#accessing-the-kubernetes-control-plane-using-an-api-server-proxy.
|
||||||
mode := parseAPIProxyMode()
|
mode := apiproxy.ParseAPIProxyMode()
|
||||||
if mode == apiserverProxyModeDisabled {
|
if mode == apiproxy.APIServerProxyModeDisabled {
|
||||||
hostinfo.SetApp(kubetypes.AppOperator)
|
hostinfo.SetApp(kubetypes.AppOperator)
|
||||||
} else {
|
} else {
|
||||||
hostinfo.SetApp(kubetypes.AppAPIServerProxy)
|
hostinfo.SetApp(kubetypes.AppAPIServerProxy)
|
||||||
@@ -112,7 +113,7 @@ func main() {
|
|||||||
s, tsc := initTSNet(zlog)
|
s, tsc := initTSNet(zlog)
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
restConfig := config.GetConfigOrDie()
|
restConfig := config.GetConfigOrDie()
|
||||||
maybeLaunchAPIServerProxy(zlog, restConfig, s, mode)
|
apiproxy.MaybeLaunchAPIServerProxy(zlog, restConfig, s, mode)
|
||||||
rOpts := reconcilerOpts{
|
rOpts := reconcilerOpts{
|
||||||
log: zlog,
|
log: zlog,
|
||||||
tsServer: s,
|
tsServer: s,
|
||||||
|
8
k8s-operator/api-proxy/doc.go
Normal file
8
k8s-operator/api-proxy/doc.go
Normal 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
|
29
k8s-operator/api-proxy/env.go
Normal file
29
k8s-operator/api-proxy/env.go
Normal file
@@ -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
|
||||||
|
}
|
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
//go:build !plan9
|
//go:build !plan9
|
||||||
|
|
||||||
package main
|
package apiproxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
@@ -37,15 +37,15 @@ var (
|
|||||||
whoIsKey = ctxkey.New("", (*apitype.WhoIsResponse)(nil))
|
whoIsKey = ctxkey.New("", (*apitype.WhoIsResponse)(nil))
|
||||||
)
|
)
|
||||||
|
|
||||||
type apiServerProxyMode int
|
type APIServerProxyMode int
|
||||||
|
|
||||||
func (a apiServerProxyMode) String() string {
|
func (a APIServerProxyMode) String() string {
|
||||||
switch a {
|
switch a {
|
||||||
case apiserverProxyModeDisabled:
|
case APIServerProxyModeDisabled:
|
||||||
return "disabled"
|
return "disabled"
|
||||||
case apiserverProxyModeEnabled:
|
case APIServerProxyModeEnabled:
|
||||||
return "auth"
|
return "auth"
|
||||||
case apiserverProxyModeNoAuth:
|
case APIServerProxyModeNoAuth:
|
||||||
return "noauth"
|
return "noauth"
|
||||||
default:
|
default:
|
||||||
return "unknown"
|
return "unknown"
|
||||||
@@ -53,12 +53,12 @@ func (a apiServerProxyMode) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
apiserverProxyModeDisabled apiServerProxyMode = iota
|
APIServerProxyModeDisabled APIServerProxyMode = iota
|
||||||
apiserverProxyModeEnabled
|
APIServerProxyModeEnabled
|
||||||
apiserverProxyModeNoAuth
|
APIServerProxyModeNoAuth
|
||||||
)
|
)
|
||||||
|
|
||||||
func parseAPIProxyMode() apiServerProxyMode {
|
func ParseAPIProxyMode() APIServerProxyMode {
|
||||||
haveAuthProxyEnv := os.Getenv("AUTH_PROXY") != ""
|
haveAuthProxyEnv := os.Getenv("AUTH_PROXY") != ""
|
||||||
haveAPIProxyEnv := os.Getenv("APISERVER_PROXY") != ""
|
haveAPIProxyEnv := os.Getenv("APISERVER_PROXY") != ""
|
||||||
switch {
|
switch {
|
||||||
@@ -67,34 +67,34 @@ func parseAPIProxyMode() apiServerProxyMode {
|
|||||||
case haveAuthProxyEnv:
|
case haveAuthProxyEnv:
|
||||||
var authProxyEnv = defaultBool("AUTH_PROXY", false) // deprecated
|
var authProxyEnv = defaultBool("AUTH_PROXY", false) // deprecated
|
||||||
if authProxyEnv {
|
if authProxyEnv {
|
||||||
return apiserverProxyModeEnabled
|
return APIServerProxyModeEnabled
|
||||||
}
|
}
|
||||||
return apiserverProxyModeDisabled
|
return APIServerProxyModeDisabled
|
||||||
case haveAPIProxyEnv:
|
case haveAPIProxyEnv:
|
||||||
var apiProxyEnv = defaultEnv("APISERVER_PROXY", "") // true, false or "noauth"
|
var apiProxyEnv = defaultEnv("APISERVER_PROXY", "") // true, false or "noauth"
|
||||||
switch apiProxyEnv {
|
switch apiProxyEnv {
|
||||||
case "true":
|
case "true":
|
||||||
return apiserverProxyModeEnabled
|
return APIServerProxyModeEnabled
|
||||||
case "false", "":
|
case "false", "":
|
||||||
return apiserverProxyModeDisabled
|
return APIServerProxyModeDisabled
|
||||||
case "noauth":
|
case "noauth":
|
||||||
return apiserverProxyModeNoAuth
|
return APIServerProxyModeNoAuth
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unknown APISERVER_PROXY value %q", apiProxyEnv))
|
panic(fmt.Sprintf("unknown APISERVER_PROXY value %q", apiProxyEnv))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return apiserverProxyModeDisabled
|
return APIServerProxyModeDisabled
|
||||||
}
|
}
|
||||||
|
|
||||||
// maybeLaunchAPIServerProxy launches the auth proxy, which is a small HTTP server
|
// maybeLaunchAPIServerProxy launches the auth proxy, which is a small HTTP server
|
||||||
// that authenticates requests using the Tailscale LocalAPI and then proxies
|
// that authenticates requests using the Tailscale LocalAPI and then proxies
|
||||||
// them to the kube-apiserver.
|
// them to the kube-apiserver.
|
||||||
func maybeLaunchAPIServerProxy(zlog *zap.SugaredLogger, restConfig *rest.Config, s *tsnet.Server, mode apiServerProxyMode) {
|
func MaybeLaunchAPIServerProxy(zlog *zap.SugaredLogger, restConfig *rest.Config, s *tsnet.Server, mode APIServerProxyMode) {
|
||||||
if mode == apiserverProxyModeDisabled {
|
if mode == APIServerProxyModeDisabled {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
startlog := zlog.Named("launchAPIProxy")
|
startlog := zlog.Named("launchAPIProxy")
|
||||||
if mode == apiserverProxyModeNoAuth {
|
if mode == APIServerProxyModeNoAuth {
|
||||||
restConfig = rest.AnonymousClientConfig(restConfig)
|
restConfig = rest.AnonymousClientConfig(restConfig)
|
||||||
}
|
}
|
||||||
cfg, err := restConfig.TransportConfig()
|
cfg, err := restConfig.TransportConfig()
|
||||||
@@ -132,8 +132,8 @@ func maybeLaunchAPIServerProxy(zlog *zap.SugaredLogger, restConfig *rest.Config,
|
|||||||
// are passed through to the Kubernetes API.
|
// are passed through to the Kubernetes API.
|
||||||
//
|
//
|
||||||
// It never returns.
|
// It never returns.
|
||||||
func runAPIServerProxy(ts *tsnet.Server, rt http.RoundTripper, log *zap.SugaredLogger, mode apiServerProxyMode, host string) {
|
func runAPIServerProxy(ts *tsnet.Server, rt http.RoundTripper, log *zap.SugaredLogger, mode APIServerProxyMode, host string) {
|
||||||
if mode == apiserverProxyModeDisabled {
|
if mode == APIServerProxyModeDisabled {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ln, err := ts.Listen("tcp", ":443")
|
ln, err := ts.Listen("tcp", ":443")
|
||||||
@@ -192,7 +192,7 @@ type apiserverProxy struct {
|
|||||||
lc *local.Client
|
lc *local.Client
|
||||||
rp *httputil.ReverseProxy
|
rp *httputil.ReverseProxy
|
||||||
|
|
||||||
mode apiServerProxyMode
|
mode APIServerProxyMode
|
||||||
ts *tsnet.Server
|
ts *tsnet.Server
|
||||||
upstreamURL *url.URL
|
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) {
|
func (h *apiserverProxy) addImpersonationHeadersAsRequired(r *http.Request) {
|
||||||
r.URL.Scheme = h.upstreamURL.Scheme
|
r.URL.Scheme = h.upstreamURL.Scheme
|
||||||
r.URL.Host = h.upstreamURL.Host
|
r.URL.Host = h.upstreamURL.Host
|
||||||
if h.mode == apiserverProxyModeNoAuth {
|
if h.mode == APIServerProxyModeNoAuth {
|
||||||
// If we are not providing authentication, then we are just
|
// If we are not providing authentication, then we are just
|
||||||
// proxying to the Kubernetes API, so we don't need to do
|
// proxying to the Kubernetes API, so we don't need to do
|
||||||
// anything else.
|
// anything else.
|
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
//go:build !plan9
|
//go:build !plan9
|
||||||
|
|
||||||
package main
|
package apiproxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
Reference in New Issue
Block a user