Skip to content

Commit a4a4307

Browse files
committed
Allow passing token proxy options from options
1 parent a4063dd commit a4a4307

File tree

7 files changed

+511
-318
lines changed

7 files changed

+511
-318
lines changed

sdk/azidentity/go.work.sum

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
2+
github.com/keybase/dbus v0.0.0-20220506165403-5aa21ea2c23a/go.mod h1:YPNKjjE7Ubp9dTbnWvsP3HT+hYnY6TfXzubYTBeUxc8=
3+
github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
4+
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
5+
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
6+
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
7+
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
8+
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package customtokenproxy
5+
6+
import (
7+
"errors"
8+
"fmt"
9+
"net/url"
10+
"os"
11+
12+
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
13+
)
14+
15+
const (
16+
AzureKubernetesCAData = "AZURE_KUBERNETES_CA_DATA"
17+
AzureKubernetesCAFile = "AZURE_KUBERNETES_CA_FILE"
18+
AzureKubernetesSNIName = "AZURE_KUBERNETES_SNI_NAME"
19+
20+
AzureKubernetesTokenProxy = "AZURE_KUBERNETES_TOKEN_PROXY"
21+
)
22+
23+
// Options contains optional parameters for custom token proxy configuration.
24+
type Options struct {
25+
// AzureKubernetesCAData specifies the CA certificate data for the Kubernetes cluster.
26+
// Corresponds to the AZURE_KUBERNETES_CA_DATA environment variable.
27+
// At most one of AzureKubernetesCAData or AzureKubernetesCAFile should be set.
28+
AzureKubernetesCAData string
29+
30+
// AzureKubernetesCAFile specifies the path to the CA certificate file for the Kubernetes cluster.
31+
// This field corresponds to the AZURE_KUBERNETES_CA_FILE environment variable.
32+
// At most one of AzureKubernetesCAData or AzureKubernetesCAFile should be set.
33+
AzureKubernetesCAFile string
34+
35+
// AzureKubernetesSNIName specifies the name of the SNI for Kubernetes cluster.
36+
// This field corresponds to the AZURE_KUBERNETES_SNI_NAME environment variable.
37+
AzureKubernetesSNIName string
38+
39+
// AzureKubernetesTokenProxy specifies the URL of the custom token proxy for the Kubernetes cluster.
40+
// This field corresponds to the AZURE_KUBERNETES_TOKEN_PROXY environment variable.
41+
AzureKubernetesTokenProxy string
42+
}
43+
44+
func parseTokenProxyURL(endpoint string) (*url.URL, error) {
45+
tokenProxy, err := url.Parse(endpoint)
46+
if err != nil {
47+
return nil, fmt.Errorf("failed to parse custom token proxy URL %q: %s", endpoint, err)
48+
}
49+
if tokenProxy.Scheme != "https" {
50+
return nil, fmt.Errorf("custom token endpoint must use https scheme, got %q", tokenProxy.Scheme)
51+
}
52+
if tokenProxy.User != nil {
53+
return nil, fmt.Errorf("custom token endpoint URL %q must not contain user info", tokenProxy)
54+
}
55+
if tokenProxy.RawQuery != "" {
56+
return nil, fmt.Errorf("custom token endpoint URL %q must not contain a query", tokenProxy)
57+
}
58+
if tokenProxy.EscapedFragment() != "" {
59+
return nil, fmt.Errorf("custom token endpoint URL %q must not contain a fragment", tokenProxy)
60+
}
61+
if tokenProxy.EscapedPath() == "" {
62+
// if the path is empty, set it to "/" to avoid stripping the path from req.URL
63+
tokenProxy.Path = "/"
64+
}
65+
return tokenProxy, nil
66+
}
67+
68+
func (o *Options) defaults() {
69+
if o.AzureKubernetesTokenProxy == "" {
70+
o.AzureKubernetesTokenProxy = os.Getenv(AzureKubernetesTokenProxy)
71+
}
72+
if o.AzureKubernetesSNIName == "" {
73+
o.AzureKubernetesSNIName = os.Getenv(AzureKubernetesSNIName)
74+
}
75+
if o.AzureKubernetesCAFile == "" {
76+
o.AzureKubernetesCAFile = os.Getenv(AzureKubernetesCAFile)
77+
}
78+
if o.AzureKubernetesCAData == "" {
79+
o.AzureKubernetesCAData = os.Getenv(AzureKubernetesCAData)
80+
}
81+
}
82+
83+
var (
84+
errCustomEndpointSetWithoutTokenProxy = errors.New(
85+
"AZURE_KUBERNETES_TOKEN_PROXY is not set but other custom endpoint-related settings are present",
86+
)
87+
errCustomEndpointMultipleCASourcesSet = errors.New(
88+
"only one of AzureKubernetesCAFile or AzureKubernetesCAData can be specified",
89+
)
90+
)
91+
92+
func noopConfigure(*policy.ClientOptions) {
93+
// no-op
94+
}
95+
96+
// Apply returns a function that configures the client options to use the custom token proxy.
97+
func Apply(opts *Options) (func(*policy.ClientOptions), error) {
98+
if opts == nil {
99+
return noopConfigure, nil
100+
}
101+
102+
opts.defaults()
103+
104+
if opts.AzureKubernetesTokenProxy == "" {
105+
// custom token proxy is not set, while other Kubernetes-related environment variables are present,
106+
// this is likely a configuration issue so erroring out to avoid misconfiguration
107+
if opts.AzureKubernetesSNIName != "" || opts.AzureKubernetesCAFile != "" || opts.AzureKubernetesCAData != "" {
108+
return nil, errCustomEndpointSetWithoutTokenProxy
109+
}
110+
111+
return noopConfigure, nil
112+
}
113+
114+
tokenProxy, err := parseTokenProxyURL(opts.AzureKubernetesTokenProxy)
115+
if err != nil {
116+
return nil, err
117+
}
118+
119+
// CAFile and CAData are mutually exclusive, at most one can be set.
120+
// If none of CAFile or CAData are set, the default system CA pool will be used.
121+
if opts.AzureKubernetesCAFile != "" && opts.AzureKubernetesCAData != "" {
122+
return nil, errCustomEndpointMultipleCASourcesSet
123+
}
124+
125+
// preload the transport
126+
t := &transport{
127+
caFile: opts.AzureKubernetesCAFile,
128+
caData: []byte(opts.AzureKubernetesCAData),
129+
sniName: opts.AzureKubernetesSNIName,
130+
tokenProxy: tokenProxy,
131+
}
132+
if _, err := t.getTokenTransporter(); err != nil {
133+
return nil, err
134+
}
135+
136+
return func(clientOptions *policy.ClientOptions) {
137+
clientOptions.Transport = t
138+
}, nil
139+
}

0 commit comments

Comments
 (0)