Skip to content

Commit 1ecc404

Browse files
committed
fix: lease based controller when using mTLS
currently when the lease based server count is enabled along with mTLS between apiserver and konnectivity server, it breaks. this fixes this by setting up the k8s clientset correctly. Signed-off-by: Imran Pochi <[email protected]>
1 parent 83b5fd9 commit 1ecc404

File tree

3 files changed

+109
-30
lines changed

3 files changed

+109
-30
lines changed

cmd/server/app/options/options.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ type ProxyRunOptions struct {
8383
AuthenticationAudience string
8484
// Path to kubeconfig (used by kubernetes client)
8585
KubeconfigPath string
86+
// Use in cluster config or not (used by kubernetes client)
87+
UseInCluster bool
8688
// Client maximum QPS.
8789
KubeconfigQPS float32
8890
// Client maximum burst for throttle.
@@ -142,6 +144,7 @@ func (o *ProxyRunOptions) Flags() *pflag.FlagSet {
142144
flags.StringVar(&o.AgentNamespace, "agent-namespace", o.AgentNamespace, "Expected agent's namespace during agent authentication (used with agent-service-account, authentication-audience, kubeconfig).")
143145
flags.StringVar(&o.AgentServiceAccount, "agent-service-account", o.AgentServiceAccount, "Expected agent's service account during agent authentication (used with agent-namespace, authentication-audience, kubeconfig).")
144146
flags.StringVar(&o.KubeconfigPath, "kubeconfig", o.KubeconfigPath, "absolute path to the kubeconfig file (used with agent-namespace, agent-service-account, authentication-audience).")
147+
flags.BoolVar(&o.UseInCluster, "use-in-cluster", o.UseInCluster, "Use in-cluster config to setup kubernetes client.")
145148
flags.Float32Var(&o.KubeconfigQPS, "kubeconfig-qps", o.KubeconfigQPS, "Maximum client QPS (proxy server uses this client to authenticate agent tokens).")
146149
flags.IntVar(&o.KubeconfigBurst, "kubeconfig-burst", o.KubeconfigBurst, "Maximum client burst (proxy server uses this client to authenticate agent tokens).")
147150
flags.StringVar(&o.APIContentType, "kube-api-content-type", o.APIContentType, "Content type of requests sent to apiserver.")
@@ -289,8 +292,8 @@ func (o *ProxyRunOptions) Validate() error {
289292
}
290293

291294
// validate agent authentication params
292-
// all 4 parameters must be empty or must have value (except KubeconfigPath that might be empty)
293-
if o.AgentNamespace != "" || o.AgentServiceAccount != "" || o.AuthenticationAudience != "" || o.KubeconfigPath != "" {
295+
// all 3 parameters must be empty or must have value
296+
if o.AgentNamespace != "" || o.AgentServiceAccount != "" || o.AuthenticationAudience != "" {
294297
if o.ClusterCaCert != "" {
295298
return fmt.Errorf("ClusterCaCert can not be used when service account authentication is enabled")
296299
}
@@ -303,13 +306,18 @@ func (o *ProxyRunOptions) Validate() error {
303306
if o.AuthenticationAudience == "" {
304307
return fmt.Errorf("AuthenticationAudience cannot be empty when agent authentication is enabled")
305308
}
306-
if o.KubeconfigPath != "" {
309+
}
310+
311+
// either use kubeconfig path or incluster config for kubernetes client
312+
if util.IsRunningInKubernetes() {
313+
if (len(o.KubeconfigPath) > 0) == o.UseInCluster {
314+
return fmt.Errorf("set only one of --use-in-cluster or --kubeconfig")
315+
} else {
307316
if _, err := os.Stat(o.KubeconfigPath); os.IsNotExist(err) {
308317
return fmt.Errorf("error checking KubeconfigPath %q, got %v", o.KubeconfigPath, err)
309318
}
310319
}
311320
}
312-
313321
// validate the proxy strategies
314322
if len(o.ProxyStrategies) == 0 {
315323
return fmt.Errorf("ProxyStrategies cannot be empty")

cmd/server/app/server.go

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ import (
4040
"google.golang.org/grpc/credentials"
4141
"google.golang.org/grpc/keepalive"
4242
"k8s.io/client-go/kubernetes"
43-
"k8s.io/client-go/tools/clientcmd"
4443
"k8s.io/klog/v2"
4544
"sigs.k8s.io/apiserver-network-proxy/cmd/server/app/options"
4645
"sigs.k8s.io/apiserver-network-proxy/konnectivity-client/proto/client"
@@ -97,42 +96,41 @@ type Proxy struct {
9796
type StopFunc func()
9897

9998
func (p *Proxy) Run(o *options.ProxyRunOptions, stopCh <-chan struct{}) error {
99+
var err error
100+
var k8sClient *kubernetes.Clientset
101+
var authOpt *server.AgentTokenAuthenticationOptions
100102
o.Print()
101-
if err := o.Validate(); err != nil {
103+
if err = o.Validate(); err != nil {
102104
return fmt.Errorf("failed to validate server options with %v", err)
103105
}
104106
ctx, cancel := context.WithCancel(context.Background())
105107
defer cancel()
106108

107-
var k8sClient *kubernetes.Clientset
108-
if o.AgentNamespace != "" {
109-
config, err := clientcmd.BuildConfigFromFlags("", o.KubeconfigPath)
110-
if err != nil {
111-
return fmt.Errorf("failed to load kubernetes client config: %v", err)
109+
if util.IsRunningInKubernetes() {
110+
clientsetcfg := &util.ClientsetConfig{
111+
Kubeconfig: o.KubeconfigPath,
112+
InClusterConfig: o.UseInCluster,
113+
ClientConfig: &util.ClientConfig{
114+
Burst: o.KubeconfigBurst,
115+
QPS: o.KubeconfigQPS,
116+
APIContentType: o.APIContentType,
117+
},
112118
}
113119

114-
if o.KubeconfigQPS != 0 {
115-
klog.V(1).Infof("Setting k8s client QPS: %v", o.KubeconfigQPS)
116-
config.QPS = o.KubeconfigQPS
117-
}
118-
if o.KubeconfigBurst != 0 {
119-
klog.V(1).Infof("Setting k8s client Burst: %v", o.KubeconfigBurst)
120-
config.Burst = o.KubeconfigBurst
121-
}
122-
config.ContentType = o.APIContentType
123-
k8sClient, err = kubernetes.NewForConfig(config)
120+
k8sClient, err = clientsetcfg.NewClientset()
124121
if err != nil {
125-
return fmt.Errorf("failed to create kubernetes clientset: %v", err)
122+
return err
126123
}
127-
}
128124

129-
authOpt := &server.AgentTokenAuthenticationOptions{
130-
Enabled: o.AgentNamespace != "",
131-
AgentNamespace: o.AgentNamespace,
132-
AgentServiceAccount: o.AgentServiceAccount,
133-
KubernetesClient: k8sClient,
134-
AuthenticationAudience: o.AuthenticationAudience,
125+
authOpt = &server.AgentTokenAuthenticationOptions{
126+
Enabled: o.AgentNamespace != "",
127+
AgentNamespace: o.AgentNamespace,
128+
AgentServiceAccount: o.AgentServiceAccount,
129+
KubernetesClient: k8sClient,
130+
AuthenticationAudience: o.AuthenticationAudience,
131+
}
135132
}
133+
136134
klog.V(1).Infoln("Starting frontend server for client connections.")
137135
ps, err := server.ParseProxyStrategies(o.ProxyStrategies)
138136
if err != nil {
@@ -160,7 +158,7 @@ func (p *Proxy) Run(o *options.ProxyRunOptions, stopCh <-chan struct{}) error {
160158
return err
161159
}
162160

163-
if o.EnableLeaseController {
161+
if util.IsRunningInKubernetes() && o.EnableLeaseController {
164162
leaseController := leases.NewController(
165163
k8sClient,
166164
o.ServerID,

pkg/util/k8s.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package util
2+
3+
import (
4+
"fmt"
5+
"os"
6+
7+
"k8s.io/client-go/kubernetes"
8+
"k8s.io/client-go/rest"
9+
"k8s.io/client-go/tools/clientcmd"
10+
"k8s.io/klog/v2"
11+
)
12+
13+
type ClientsetConfig struct {
14+
Kubeconfig string
15+
InClusterConfig bool
16+
ClientConfig *ClientConfig
17+
}
18+
19+
type ClientConfig struct {
20+
QPS float32
21+
Burst int
22+
APIContentType string
23+
}
24+
25+
func IsRunningInKubernetes() bool {
26+
if os.Getenv("KUBERNETES_SERVICE_HOST") != "" && os.Getenv("KUBERNETES_SERVICE_PORT") != "" {
27+
return true
28+
}
29+
30+
return false
31+
}
32+
33+
func (cfg *ClientsetConfig) NewClientset() (*kubernetes.Clientset, error) {
34+
var config *rest.Config
35+
var err error
36+
37+
switch {
38+
case cfg.Kubeconfig != "":
39+
config, err = clientcmd.BuildConfigFromFlags("", cfg.Kubeconfig)
40+
if err != nil {
41+
return nil, fmt.Errorf("failed to load kubernetes client config: %v", err)
42+
}
43+
case cfg.InClusterConfig:
44+
config, err = rest.InClusterConfig()
45+
default:
46+
return nil, fmt.Errorf("no valid configuration option provided; either provide kubeconfig path or set --use-in-cluster=true with necessary RBAC permissions")
47+
}
48+
49+
cfg.setClientOptions(config)
50+
51+
clientset, err := kubernetes.NewForConfig(config)
52+
if err != nil {
53+
return nil, fmt.Errorf("failed to create Kubernetes clientset: %w", err)
54+
}
55+
56+
return clientset, nil
57+
}
58+
59+
func (cfg *ClientsetConfig) setClientOptions(config *rest.Config) {
60+
61+
if cfg.ClientConfig.QPS != 0 {
62+
klog.V(1).Infof("Setting k8s client QPS: %v", cfg.ClientConfig.QPS)
63+
config.QPS = cfg.ClientConfig.QPS
64+
}
65+
if cfg.ClientConfig.Burst != 0 {
66+
klog.V(1).Infof("Setting k8s client Burst: %v", cfg.ClientConfig.Burst)
67+
config.Burst = cfg.ClientConfig.Burst
68+
}
69+
if len(cfg.ClientConfig.APIContentType) != 0 {
70+
klog.V(1).Infof("Setting k8s client Content type: %v", cfg.ClientConfig.APIContentType)
71+
config.ContentType = cfg.ClientConfig.APIContentType
72+
}
73+
}

0 commit comments

Comments
 (0)