Skip to content

Commit ee8b555

Browse files
committed
Introduce certificate-authority for self-signed certificates of authorization url
1 parent 61acdc0 commit ee8b555

File tree

3 files changed

+62
-26
lines changed

3 files changed

+62
-26
lines changed

pkg/config/config.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@ type StaticConfig struct {
1919
// When true, expose only tools annotated with readOnlyHint=true
2020
ReadOnly bool `toml:"read_only,omitempty"`
2121
// When true, disable tools annotated with destructiveHint=true
22-
DisableDestructive bool `toml:"disable_destructive,omitempty"`
23-
EnabledTools []string `toml:"enabled_tools,omitempty"`
24-
DisabledTools []string `toml:"disabled_tools,omitempty"`
25-
RequireOAuth bool `toml:"require_oauth,omitempty"`
26-
AuthorizationURL string `toml:"authorization_url,omitempty"`
27-
JwksURL string `toml:"jwks_url,omitempty"`
28-
ServerURL string `toml:"server_url,omitempty"`
22+
DisableDestructive bool `toml:"disable_destructive,omitempty"`
23+
EnabledTools []string `toml:"enabled_tools,omitempty"`
24+
DisabledTools []string `toml:"disabled_tools,omitempty"`
25+
RequireOAuth bool `toml:"require_oauth,omitempty"`
26+
AuthorizationURL string `toml:"authorization_url,omitempty"`
27+
JwksURL string `toml:"jwks_url,omitempty"`
28+
CertificateAuthority string `toml:"certificate_authority,omitempty"`
29+
ServerURL string `toml:"server_url,omitempty"`
2930
}
3031

3132
type GroupVersionKind struct {

pkg/http/authorization.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func AuthorizationMiddleware(requireOAuth bool, serverURL string, oidcProvider *
6969
http.Error(w, "Unauthorized: Invalid token", http.StatusUnauthorized)
7070
return
7171
}
72-
72+
7373
if oidcProvider != nil {
7474
// If OIDC Provider is configured, this token must be validated against it.
7575
if err := validateTokenWithOIDC(r.Context(), oidcProvider, token, audience); err != nil {

pkg/kubernetes-mcp-server/cmd/root.go

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@ package cmd
22

33
import (
44
"context"
5+
"crypto/tls"
6+
"crypto/x509"
57
"errors"
68
"flag"
79
"fmt"
10+
"net/http"
811
"net/url"
12+
"os"
913
"strconv"
1014
"strings"
1115

@@ -46,21 +50,22 @@ kubernetes-mcp-server --port 8443 --sse-base-url https://example.com:8443
4650
)
4751

4852
type MCPServerOptions struct {
49-
Version bool
50-
LogLevel int
51-
Port string
52-
SSEPort int
53-
HttpPort int
54-
SSEBaseUrl string
55-
Kubeconfig string
56-
Profile string
57-
ListOutput string
58-
ReadOnly bool
59-
DisableDestructive bool
60-
RequireOAuth bool
61-
AuthorizationURL string
62-
JwksURL string
63-
ServerURL string
53+
Version bool
54+
LogLevel int
55+
Port string
56+
SSEPort int
57+
HttpPort int
58+
SSEBaseUrl string
59+
Kubeconfig string
60+
Profile string
61+
ListOutput string
62+
ReadOnly bool
63+
DisableDestructive bool
64+
RequireOAuth bool
65+
AuthorizationURL string
66+
JwksURL string
67+
CertificateAuthority string
68+
ServerURL string
6469

6570
ConfigPath string
6671
StaticConfig *config.StaticConfig
@@ -121,6 +126,9 @@ func NewMCPServer(streams genericiooptions.IOStreams) *cobra.Command {
121126
cmd.Flags().MarkHidden("jwks-url")
122127
cmd.Flags().StringVar(&o.ServerURL, "server-url", o.ServerURL, "Server URL of this application. Optional. If set, this url will be served in protected resource metadata endpoint and tokens will be validated with this audience. If not set, expected audience is kubernetes-mcp-server. Only valid if require-oauth is enabled.")
123128
cmd.Flags().MarkHidden("server-url")
129+
cmd.Flags().StringVar(&o.CertificateAuthority, "certificate-authority", o.CertificateAuthority, "Certificate authority path to verify certificates. Optional. Only valid if require-oauth is enabled.")
130+
cmd.Flags().MarkHidden("certificate-authority")
131+
124132
return cmd
125133
}
126134

@@ -183,6 +191,9 @@ func (m *MCPServerOptions) loadFlags(cmd *cobra.Command) {
183191
if cmd.Flag("server-url").Changed {
184192
m.StaticConfig.ServerURL = m.ServerURL
185193
}
194+
if cmd.Flag("certificate-authority").Changed {
195+
m.StaticConfig.CertificateAuthority = m.CertificateAuthority
196+
}
186197
}
187198

188199
func (m *MCPServerOptions) initializeLogging() {
@@ -201,8 +212,8 @@ func (m *MCPServerOptions) Validate() error {
201212
if m.Port != "" && (m.SSEPort > 0 || m.HttpPort > 0) {
202213
return fmt.Errorf("--port is mutually exclusive with deprecated --http-port and --sse-port flags")
203214
}
204-
if !m.StaticConfig.RequireOAuth && (m.StaticConfig.AuthorizationURL != "" || m.StaticConfig.ServerURL != "" || m.StaticConfig.JwksURL != "") {
205-
return fmt.Errorf("authorization-url, server-url and jwks-url are only valid if require-oauth is enabled. Missing --port may implicitly set require-oauth to false")
215+
if !m.StaticConfig.RequireOAuth && (m.StaticConfig.AuthorizationURL != "" || m.StaticConfig.ServerURL != "" || m.StaticConfig.JwksURL != "" || m.StaticConfig.CertificateAuthority != "") {
216+
return fmt.Errorf("authorization-url, server-url, certificate-authority and jwks-url are only valid if require-oauth is enabled. Missing --port may implicitly set require-oauth to false")
206217
}
207218
if m.StaticConfig.AuthorizationURL != "" {
208219
u, err := url.Parse(m.StaticConfig.AuthorizationURL)
@@ -266,7 +277,31 @@ func (m *MCPServerOptions) Run() error {
266277

267278
var oidcProvider *oidc.Provider
268279
if m.StaticConfig.AuthorizationURL != "" {
269-
provider, err := oidc.NewProvider(context.TODO(), m.StaticConfig.AuthorizationURL)
280+
ctx := context.Background()
281+
if m.StaticConfig.CertificateAuthority != "" {
282+
httpClient := &http.Client{}
283+
caCert, err := os.ReadFile(m.StaticConfig.CertificateAuthority)
284+
if err != nil {
285+
return fmt.Errorf("failed to read CA certificate from %s: %w", m.StaticConfig.CertificateAuthority, err)
286+
}
287+
caCertPool := x509.NewCertPool()
288+
if !caCertPool.AppendCertsFromPEM(caCert) {
289+
return fmt.Errorf("failed to append CA certificate from %s to pool", m.StaticConfig.CertificateAuthority)
290+
}
291+
292+
if caCertPool.Equal(x509.NewCertPool()) {
293+
caCertPool = nil
294+
}
295+
296+
transport := &http.Transport{
297+
TLSClientConfig: &tls.Config{
298+
RootCAs: caCertPool,
299+
},
300+
}
301+
httpClient.Transport = transport
302+
ctx = oidc.ClientContext(ctx, httpClient)
303+
}
304+
provider, err := oidc.NewProvider(ctx, m.StaticConfig.AuthorizationURL)
270305
if err != nil {
271306
return fmt.Errorf("unable to setup OIDC provider: %w", err)
272307
}

0 commit comments

Comments
 (0)