mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-07-09 21:17:09 +08:00
go.mod: update k8s deps to v0.26.2 (remove "replace" rule)
Replace rules are not inherited by consumers of buildx as a module, and as such would default to use the v0.26.2 version. Removing the replace rules also removes various (indirect) dependencies (although brings in some new packages from k8s itself). The "azure" and "gcp" authentication packages in k8s.io/go-client are now no longer functional, so removing those imports. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
56
vendor/k8s.io/client-go/plugin/pkg/client/auth/azure/README.md
generated
vendored
56
vendor/k8s.io/client-go/plugin/pkg/client/auth/azure/README.md
generated
vendored
@ -1,56 +0,0 @@
|
||||
# Azure Active Directory plugin for client authentication
|
||||
|
||||
This plugin provides an integration with Azure Active Directory device flow. If no tokens are present in the kubectl configuration, it will prompt a device code which can be used to login in a browser. After login it will automatically fetch the tokens and store them in the kubectl configuration. In addition it will refresh and update the tokens in the configuration when expired.
|
||||
|
||||
## Usage
|
||||
|
||||
1. Create an Azure Active Directory *Web App / API* application for `apiserver` following these [instructions](https://docs.microsoft.com/en-us/azure/active-directory/active-directory-app-registration). The callback URL does not matter (just cannot be empty).
|
||||
|
||||
2. Create a second Azure Active Directory native application for `kubectl`. The callback URL does not matter (just cannot be empty).
|
||||
|
||||
3. On `kubectl` application's configuration page in Azure portal grant permissions to `apiserver` application by clicking on *Required Permissions*, click the *Add* button and search for the apiserver application created in step 1. Select "Access apiserver" under the *DELEGATED PERMISSIONS*. Once added click the *Grant Permissions* button to apply the changes.
|
||||
|
||||
4. Configure the `apiserver` to use the Azure Active Directory as an OIDC provider with following options
|
||||
|
||||
```
|
||||
--oidc-client-id="spn:APISERVER_APPLICATION_ID" \
|
||||
--oidc-issuer-url="https://sts.windows.net/TENANT_ID/"
|
||||
--oidc-username-claim="sub"
|
||||
```
|
||||
|
||||
* Replace the `APISERVER_APPLICATION_ID` with the application ID of `apiserver` application
|
||||
* Replace `TENANT_ID` with your tenant ID.
|
||||
* For a list of alternative username claims that are supported by the OIDC issuer check the JSON response at `https://sts.windows.net/TENANT_ID/.well-known/openid-configuration`.
|
||||
|
||||
5. Configure `kubectl` to use the `azure` authentication provider
|
||||
|
||||
```
|
||||
kubectl config set-credentials "USER_NAME" --auth-provider=azure \
|
||||
--auth-provider-arg=environment=AzurePublicCloud \
|
||||
--auth-provider-arg=client-id=APPLICATION_ID \
|
||||
--auth-provider-arg=tenant-id=TENANT_ID \
|
||||
--auth-provider-arg=apiserver-id=APISERVER_APPLICATION_ID
|
||||
```
|
||||
|
||||
* Supported environments: `AzurePublicCloud`, `AzureUSGovernmentCloud`, `AzureChinaCloud`, `AzureGermanCloud`
|
||||
* Replace `USER_NAME` and `TENANT_ID` with your user name and tenant ID
|
||||
* Replace `APPLICATION_ID` with the application ID of your`kubectl` application ID
|
||||
* Replace `APISERVER_APPLICATION_ID` with the application ID of your `apiserver` application ID
|
||||
* Be sure to also (create and) select a context that uses above user
|
||||
|
||||
6. (Optionally) the AAD token has `aud` claim with `spn:` prefix. To omit that, add following auth configuration:
|
||||
|
||||
```
|
||||
--auth-provider-arg=config-mode="1"
|
||||
```
|
||||
|
||||
7. The access token is acquired when first `kubectl` command is executed
|
||||
|
||||
```
|
||||
kubectl get pods
|
||||
|
||||
To sign in, use a web browser to open the page https://aka.ms/devicelogin and enter the code DEC7D48GA to authenticate.
|
||||
```
|
||||
|
||||
* After signing in a web browser, the token is stored in the configuration, and it will be reused when executing further commands.
|
||||
* The resulting username in Kubernetes depends on your [configuration of the `--oidc-username-claim` and `--oidc-username-prefix` flags on the API server](https://kubernetes.io/docs/admin/authentication/#configuring-the-api-server). If you are using any authorization method you need to give permissions to that user, e.g. by binding the user to a role in the case of RBAC.
|
477
vendor/k8s.io/client-go/plugin/pkg/client/auth/azure/azure.go
generated
vendored
477
vendor/k8s.io/client-go/plugin/pkg/client/auth/azure/azure.go
generated
vendored
@ -1,477 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package azure
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
"github.com/Azure/go-autorest/autorest/adal"
|
||||
"github.com/Azure/go-autorest/autorest/azure"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/net"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
type configMode int
|
||||
|
||||
const (
|
||||
azureTokenKey = "azureTokenKey"
|
||||
tokenType = "Bearer"
|
||||
authHeader = "Authorization"
|
||||
|
||||
cfgClientID = "client-id"
|
||||
cfgTenantID = "tenant-id"
|
||||
cfgAccessToken = "access-token"
|
||||
cfgRefreshToken = "refresh-token"
|
||||
cfgExpiresIn = "expires-in"
|
||||
cfgExpiresOn = "expires-on"
|
||||
cfgEnvironment = "environment"
|
||||
cfgApiserverID = "apiserver-id"
|
||||
cfgConfigMode = "config-mode"
|
||||
|
||||
configModeDefault configMode = 0
|
||||
configModeOmitSPNPrefix configMode = 1
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := restclient.RegisterAuthProviderPlugin("azure", newAzureAuthProvider); err != nil {
|
||||
klog.Fatalf("Failed to register azure auth plugin: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
var cache = newAzureTokenCache()
|
||||
|
||||
type azureTokenCache struct {
|
||||
lock sync.Mutex
|
||||
cache map[string]*azureToken
|
||||
}
|
||||
|
||||
func newAzureTokenCache() *azureTokenCache {
|
||||
return &azureTokenCache{cache: make(map[string]*azureToken)}
|
||||
}
|
||||
|
||||
func (c *azureTokenCache) getToken(tokenKey string) *azureToken {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
return c.cache[tokenKey]
|
||||
}
|
||||
|
||||
func (c *azureTokenCache) setToken(tokenKey string, token *azureToken) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
c.cache[tokenKey] = token
|
||||
}
|
||||
|
||||
var warnOnce sync.Once
|
||||
|
||||
func newAzureAuthProvider(_ string, cfg map[string]string, persister restclient.AuthProviderConfigPersister) (restclient.AuthProvider, error) {
|
||||
// deprecated in v1.22, remove in v1.25
|
||||
warnOnce.Do(func() {
|
||||
klog.Warningf(`WARNING: the azure auth plugin is deprecated in v1.22+, unavailable in v1.26+; use https://github.com/Azure/kubelogin instead.
|
||||
To learn more, consult https://kubernetes.io/docs/reference/access-authn-authz/authentication/#client-go-credential-plugins`)
|
||||
})
|
||||
|
||||
var (
|
||||
ts tokenSource
|
||||
environment azure.Environment
|
||||
err error
|
||||
mode configMode
|
||||
)
|
||||
|
||||
environment, err = azure.EnvironmentFromName(cfg[cfgEnvironment])
|
||||
if err != nil {
|
||||
environment = azure.PublicCloud
|
||||
}
|
||||
|
||||
mode = configModeDefault
|
||||
if cfg[cfgConfigMode] != "" {
|
||||
configModeInt, err := strconv.Atoi(cfg[cfgConfigMode])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse %s, error: %s", cfgConfigMode, err)
|
||||
}
|
||||
mode = configMode(configModeInt)
|
||||
switch mode {
|
||||
case configModeOmitSPNPrefix:
|
||||
case configModeDefault:
|
||||
default:
|
||||
return nil, fmt.Errorf("%s:%s is not a valid mode", cfgConfigMode, cfg[cfgConfigMode])
|
||||
}
|
||||
}
|
||||
ts, err = newAzureTokenSourceDeviceCode(environment, cfg[cfgClientID], cfg[cfgTenantID], cfg[cfgApiserverID], mode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating a new azure token source for device code authentication: %v", err)
|
||||
}
|
||||
cacheSource := newAzureTokenSource(ts, cache, cfg, mode, persister)
|
||||
|
||||
return &azureAuthProvider{
|
||||
tokenSource: cacheSource,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type azureAuthProvider struct {
|
||||
tokenSource tokenSource
|
||||
}
|
||||
|
||||
func (p *azureAuthProvider) Login() error {
|
||||
return errors.New("not yet implemented")
|
||||
}
|
||||
|
||||
func (p *azureAuthProvider) WrapTransport(rt http.RoundTripper) http.RoundTripper {
|
||||
return &azureRoundTripper{
|
||||
tokenSource: p.tokenSource,
|
||||
roundTripper: rt,
|
||||
}
|
||||
}
|
||||
|
||||
type azureRoundTripper struct {
|
||||
tokenSource tokenSource
|
||||
roundTripper http.RoundTripper
|
||||
}
|
||||
|
||||
var _ net.RoundTripperWrapper = &azureRoundTripper{}
|
||||
|
||||
func (r *azureRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
if len(req.Header.Get(authHeader)) != 0 {
|
||||
return r.roundTripper.RoundTrip(req)
|
||||
}
|
||||
|
||||
token, err := r.tokenSource.Token()
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to acquire a token: %v", err)
|
||||
return nil, fmt.Errorf("acquiring a token for authorization header: %v", err)
|
||||
}
|
||||
|
||||
// clone the request in order to avoid modifying the headers of the original request
|
||||
req2 := new(http.Request)
|
||||
*req2 = *req
|
||||
req2.Header = make(http.Header, len(req.Header))
|
||||
for k, s := range req.Header {
|
||||
req2.Header[k] = append([]string(nil), s...)
|
||||
}
|
||||
|
||||
req2.Header.Set(authHeader, fmt.Sprintf("%s %s", tokenType, token.token.AccessToken))
|
||||
|
||||
return r.roundTripper.RoundTrip(req2)
|
||||
}
|
||||
|
||||
func (r *azureRoundTripper) WrappedRoundTripper() http.RoundTripper { return r.roundTripper }
|
||||
|
||||
type azureToken struct {
|
||||
token adal.Token
|
||||
environment string
|
||||
clientID string
|
||||
tenantID string
|
||||
apiserverID string
|
||||
}
|
||||
|
||||
type tokenSource interface {
|
||||
Token() (*azureToken, error)
|
||||
Refresh(*azureToken) (*azureToken, error)
|
||||
}
|
||||
|
||||
type azureTokenSource struct {
|
||||
source tokenSource
|
||||
cache *azureTokenCache
|
||||
lock sync.Mutex
|
||||
configMode configMode
|
||||
cfg map[string]string
|
||||
persister restclient.AuthProviderConfigPersister
|
||||
}
|
||||
|
||||
func newAzureTokenSource(source tokenSource, cache *azureTokenCache, cfg map[string]string, configMode configMode, persister restclient.AuthProviderConfigPersister) tokenSource {
|
||||
return &azureTokenSource{
|
||||
source: source,
|
||||
cache: cache,
|
||||
cfg: cfg,
|
||||
persister: persister,
|
||||
configMode: configMode,
|
||||
}
|
||||
}
|
||||
|
||||
// Token fetches a token from the cache of configuration if present otherwise
|
||||
// acquires a new token from the configured source. Automatically refreshes
|
||||
// the token if expired.
|
||||
func (ts *azureTokenSource) Token() (*azureToken, error) {
|
||||
ts.lock.Lock()
|
||||
defer ts.lock.Unlock()
|
||||
|
||||
var err error
|
||||
token := ts.cache.getToken(azureTokenKey)
|
||||
|
||||
if token != nil && !token.token.IsExpired() {
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// retrieve from config if no cache
|
||||
if token == nil {
|
||||
tokenFromCfg, err := ts.retrieveTokenFromCfg()
|
||||
|
||||
if err == nil {
|
||||
token = tokenFromCfg
|
||||
}
|
||||
}
|
||||
|
||||
if token != nil {
|
||||
// cache and return if the token is as good
|
||||
// avoids frequent persistor calls
|
||||
if !token.token.IsExpired() {
|
||||
ts.cache.setToken(azureTokenKey, token)
|
||||
return token, nil
|
||||
}
|
||||
|
||||
klog.V(4).Info("Refreshing token.")
|
||||
tokenFromRefresh, err := ts.Refresh(token)
|
||||
switch {
|
||||
case err == nil:
|
||||
token = tokenFromRefresh
|
||||
case autorest.IsTokenRefreshError(err):
|
||||
klog.V(4).Infof("Failed to refresh expired token, proceed to auth: %v", err)
|
||||
// reset token to nil so that the token source will be used to acquire new
|
||||
token = nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected error when refreshing token: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if token == nil {
|
||||
tokenFromSource, err := ts.source.Token()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed acquiring new token: %v", err)
|
||||
}
|
||||
token = tokenFromSource
|
||||
}
|
||||
|
||||
// sanity check
|
||||
if token == nil {
|
||||
return nil, fmt.Errorf("unable to acquire token")
|
||||
}
|
||||
|
||||
// corner condition, newly got token is valid but expired
|
||||
if token.token.IsExpired() {
|
||||
return nil, fmt.Errorf("newly acquired token is expired")
|
||||
}
|
||||
|
||||
err = ts.storeTokenInCfg(token)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("storing the refreshed token in configuration: %v", err)
|
||||
}
|
||||
ts.cache.setToken(azureTokenKey, token)
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (ts *azureTokenSource) retrieveTokenFromCfg() (*azureToken, error) {
|
||||
accessToken := ts.cfg[cfgAccessToken]
|
||||
if accessToken == "" {
|
||||
return nil, fmt.Errorf("no access token in cfg: %s", cfgAccessToken)
|
||||
}
|
||||
refreshToken := ts.cfg[cfgRefreshToken]
|
||||
if refreshToken == "" {
|
||||
return nil, fmt.Errorf("no refresh token in cfg: %s", cfgRefreshToken)
|
||||
}
|
||||
environment := ts.cfg[cfgEnvironment]
|
||||
if environment == "" {
|
||||
return nil, fmt.Errorf("no environment in cfg: %s", cfgEnvironment)
|
||||
}
|
||||
clientID := ts.cfg[cfgClientID]
|
||||
if clientID == "" {
|
||||
return nil, fmt.Errorf("no client ID in cfg: %s", cfgClientID)
|
||||
}
|
||||
tenantID := ts.cfg[cfgTenantID]
|
||||
if tenantID == "" {
|
||||
return nil, fmt.Errorf("no tenant ID in cfg: %s", cfgTenantID)
|
||||
}
|
||||
resourceID := ts.cfg[cfgApiserverID]
|
||||
if resourceID == "" {
|
||||
return nil, fmt.Errorf("no apiserver ID in cfg: %s", cfgApiserverID)
|
||||
}
|
||||
expiresIn := ts.cfg[cfgExpiresIn]
|
||||
if expiresIn == "" {
|
||||
return nil, fmt.Errorf("no expiresIn in cfg: %s", cfgExpiresIn)
|
||||
}
|
||||
expiresOn := ts.cfg[cfgExpiresOn]
|
||||
if expiresOn == "" {
|
||||
return nil, fmt.Errorf("no expiresOn in cfg: %s", cfgExpiresOn)
|
||||
}
|
||||
tokenAudience := resourceID
|
||||
if ts.configMode == configModeDefault {
|
||||
tokenAudience = fmt.Sprintf("spn:%s", resourceID)
|
||||
}
|
||||
|
||||
return &azureToken{
|
||||
token: adal.Token{
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: refreshToken,
|
||||
ExpiresIn: json.Number(expiresIn),
|
||||
ExpiresOn: json.Number(expiresOn),
|
||||
NotBefore: json.Number(expiresOn),
|
||||
Resource: tokenAudience,
|
||||
Type: tokenType,
|
||||
},
|
||||
environment: environment,
|
||||
clientID: clientID,
|
||||
tenantID: tenantID,
|
||||
apiserverID: resourceID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ts *azureTokenSource) storeTokenInCfg(token *azureToken) error {
|
||||
newCfg := make(map[string]string)
|
||||
newCfg[cfgAccessToken] = token.token.AccessToken
|
||||
newCfg[cfgRefreshToken] = token.token.RefreshToken
|
||||
newCfg[cfgEnvironment] = token.environment
|
||||
newCfg[cfgClientID] = token.clientID
|
||||
newCfg[cfgTenantID] = token.tenantID
|
||||
newCfg[cfgApiserverID] = token.apiserverID
|
||||
newCfg[cfgExpiresIn] = string(token.token.ExpiresIn)
|
||||
newCfg[cfgExpiresOn] = string(token.token.ExpiresOn)
|
||||
newCfg[cfgConfigMode] = strconv.Itoa(int(ts.configMode))
|
||||
|
||||
err := ts.persister.Persist(newCfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("persisting the configuration: %v", err)
|
||||
}
|
||||
ts.cfg = newCfg
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ts *azureTokenSource) Refresh(token *azureToken) (*azureToken, error) {
|
||||
return ts.source.Refresh(token)
|
||||
}
|
||||
|
||||
// refresh outdated token with adal.
|
||||
func (ts *azureTokenSourceDeviceCode) Refresh(token *azureToken) (*azureToken, error) {
|
||||
env, err := azure.EnvironmentFromName(token.environment)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var oauthConfig *adal.OAuthConfig
|
||||
if ts.configMode == configModeOmitSPNPrefix {
|
||||
oauthConfig, err = adal.NewOAuthConfigWithAPIVersion(env.ActiveDirectoryEndpoint, token.tenantID, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("building the OAuth configuration without api-version for token refresh: %v", err)
|
||||
}
|
||||
} else {
|
||||
oauthConfig, err = adal.NewOAuthConfig(env.ActiveDirectoryEndpoint, token.tenantID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("building the OAuth configuration for token refresh: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
callback := func(t adal.Token) error {
|
||||
return nil
|
||||
}
|
||||
spt, err := adal.NewServicePrincipalTokenFromManualToken(
|
||||
*oauthConfig,
|
||||
token.clientID,
|
||||
token.apiserverID,
|
||||
token.token,
|
||||
callback)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating new service principal for token refresh: %v", err)
|
||||
}
|
||||
|
||||
if err := spt.Refresh(); err != nil {
|
||||
// Caller expects IsTokenRefreshError(err) to trigger prompt.
|
||||
return nil, fmt.Errorf("refreshing token: %w", err)
|
||||
}
|
||||
|
||||
return &azureToken{
|
||||
token: spt.Token(),
|
||||
environment: token.environment,
|
||||
clientID: token.clientID,
|
||||
tenantID: token.tenantID,
|
||||
apiserverID: token.apiserverID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type azureTokenSourceDeviceCode struct {
|
||||
environment azure.Environment
|
||||
clientID string
|
||||
tenantID string
|
||||
apiserverID string
|
||||
configMode configMode
|
||||
}
|
||||
|
||||
func newAzureTokenSourceDeviceCode(environment azure.Environment, clientID string, tenantID string, apiserverID string, configMode configMode) (tokenSource, error) {
|
||||
if clientID == "" {
|
||||
return nil, errors.New("client-id is empty")
|
||||
}
|
||||
if tenantID == "" {
|
||||
return nil, errors.New("tenant-id is empty")
|
||||
}
|
||||
if apiserverID == "" {
|
||||
return nil, errors.New("apiserver-id is empty")
|
||||
}
|
||||
return &azureTokenSourceDeviceCode{
|
||||
environment: environment,
|
||||
clientID: clientID,
|
||||
tenantID: tenantID,
|
||||
apiserverID: apiserverID,
|
||||
configMode: configMode,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ts *azureTokenSourceDeviceCode) Token() (*azureToken, error) {
|
||||
var (
|
||||
oauthConfig *adal.OAuthConfig
|
||||
err error
|
||||
)
|
||||
if ts.configMode == configModeOmitSPNPrefix {
|
||||
oauthConfig, err = adal.NewOAuthConfigWithAPIVersion(ts.environment.ActiveDirectoryEndpoint, ts.tenantID, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("building the OAuth configuration without api-version for device code authentication: %v", err)
|
||||
}
|
||||
} else {
|
||||
oauthConfig, err = adal.NewOAuthConfig(ts.environment.ActiveDirectoryEndpoint, ts.tenantID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("building the OAuth configuration for device code authentication: %v", err)
|
||||
}
|
||||
}
|
||||
client := &autorest.Client{}
|
||||
deviceCode, err := adal.InitiateDeviceAuth(client, *oauthConfig, ts.clientID, ts.apiserverID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("initialing the device code authentication: %v", err)
|
||||
}
|
||||
|
||||
_, err = fmt.Fprintln(os.Stderr, *deviceCode.Message)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("prompting the device code message: %v", err)
|
||||
}
|
||||
|
||||
token, err := adal.WaitForUserCompletion(client, deviceCode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("waiting for device code authentication to complete: %v", err)
|
||||
}
|
||||
|
||||
return &azureToken{
|
||||
token: *token,
|
||||
environment: ts.environment.Name,
|
||||
clientID: ts.clientID,
|
||||
tenantID: ts.tenantID,
|
||||
apiserverID: ts.apiserverID,
|
||||
}, nil
|
||||
}
|
13
vendor/k8s.io/client-go/plugin/pkg/client/auth/exec/exec.go
generated
vendored
13
vendor/k8s.io/client-go/plugin/pkg/client/auth/exec/exec.go
generated
vendored
@ -308,17 +308,18 @@ func (a *Authenticator) UpdateTransportConfig(c *transport.Config) error {
|
||||
if c.HasCertCallback() {
|
||||
return errors.New("can't add TLS certificate callback: transport.Config.TLS.GetCert already set")
|
||||
}
|
||||
c.TLS.GetCert = a.getCert.GetCert
|
||||
c.TLS.GetCertHolder = a.getCert // comparable for TLS config caching
|
||||
|
||||
if c.Dial != nil {
|
||||
if c.DialHolder != nil {
|
||||
if c.DialHolder.Dial == nil {
|
||||
return errors.New("invalid transport.Config.DialHolder: wrapped Dial function is nil")
|
||||
}
|
||||
|
||||
// if c has a custom dialer, we have to wrap it
|
||||
// TLS config caching is not supported for this config
|
||||
d := connrotation.NewDialerWithTracker(c.Dial, a.connTracker)
|
||||
c.Dial = d.DialContext
|
||||
c.DialHolder = nil
|
||||
d := connrotation.NewDialerWithTracker(c.DialHolder.Dial, a.connTracker)
|
||||
c.DialHolder = &transport.DialHolder{Dial: d.DialContext}
|
||||
} else {
|
||||
c.Dial = a.dial.Dial
|
||||
c.DialHolder = a.dial // comparable for TLS config caching
|
||||
}
|
||||
|
||||
|
8
vendor/k8s.io/client-go/plugin/pkg/client/auth/gcp/OWNERS
generated
vendored
8
vendor/k8s.io/client-go/plugin/pkg/client/auth/gcp/OWNERS
generated
vendored
@ -1,8 +0,0 @@
|
||||
# See the OWNERS docs at https://go.k8s.io/owners
|
||||
|
||||
approvers:
|
||||
- cjcullen
|
||||
reviewers:
|
||||
- cjcullen
|
||||
emeritus_approvers:
|
||||
- jlowdermilk
|
389
vendor/k8s.io/client-go/plugin/pkg/client/auth/gcp/gcp.go
generated
vendored
389
vendor/k8s.io/client-go/plugin/pkg/client/auth/gcp/gcp.go
generated
vendored
@ -1,389 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gcp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
"k8s.io/apimachinery/pkg/util/net"
|
||||
"k8s.io/apimachinery/pkg/util/yaml"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/util/jsonpath"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := restclient.RegisterAuthProviderPlugin("gcp", newGCPAuthProvider); err != nil {
|
||||
klog.Fatalf("Failed to register gcp auth plugin: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
// Stubbable for testing
|
||||
execCommand = exec.Command
|
||||
|
||||
// defaultScopes:
|
||||
// - cloud-platform is the base scope to authenticate to GCP.
|
||||
// - userinfo.email is used to authenticate to GKE APIs with gserviceaccount
|
||||
// email instead of numeric uniqueID.
|
||||
defaultScopes = []string{
|
||||
"https://www.googleapis.com/auth/cloud-platform",
|
||||
"https://www.googleapis.com/auth/userinfo.email"}
|
||||
)
|
||||
|
||||
// gcpAuthProvider is an auth provider plugin that uses GCP credentials to provide
|
||||
// tokens for kubectl to authenticate itself to the apiserver. A sample json config
|
||||
// is provided below with all recognized options described.
|
||||
//
|
||||
// {
|
||||
// 'auth-provider': {
|
||||
// # Required
|
||||
// "name": "gcp",
|
||||
//
|
||||
// 'config': {
|
||||
// # Authentication options
|
||||
// # These options are used while getting a token.
|
||||
//
|
||||
// # comma-separated list of GCP API scopes. default value of this field
|
||||
// # is "https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/userinfo.email".
|
||||
// # to override the API scopes, specify this field explicitly.
|
||||
// "scopes": "https://www.googleapis.com/auth/cloud-platform"
|
||||
//
|
||||
// # Caching options
|
||||
//
|
||||
// # Raw string data representing cached access token.
|
||||
// "access-token": "ya29.CjWdA4GiBPTt",
|
||||
// # RFC3339Nano expiration timestamp for cached access token.
|
||||
// "expiry": "2016-10-31 22:31:9.123",
|
||||
//
|
||||
// # Command execution options
|
||||
// # These options direct the plugin to execute a specified command and parse
|
||||
// # token and expiry time from the output of the command.
|
||||
//
|
||||
// # Command to execute for access token. Command output will be parsed as JSON.
|
||||
// # If "cmd-args" is not present, this value will be split on whitespace, with
|
||||
// # the first element interpreted as the command, remaining elements as args.
|
||||
// "cmd-path": "/usr/bin/gcloud",
|
||||
//
|
||||
// # Arguments to pass to command to execute for access token.
|
||||
// "cmd-args": "config config-helper --output=json"
|
||||
//
|
||||
// # JSONPath to the string field that represents the access token in
|
||||
// # command output. If omitted, defaults to "{.access_token}".
|
||||
// "token-key": "{.credential.access_token}",
|
||||
//
|
||||
// # JSONPath to the string field that represents expiration timestamp
|
||||
// # of the access token in the command output. If omitted, defaults to
|
||||
// # "{.token_expiry}"
|
||||
// "expiry-key": ""{.credential.token_expiry}",
|
||||
//
|
||||
// # golang reference time in the format that the expiration timestamp uses.
|
||||
// # If omitted, defaults to time.RFC3339Nano
|
||||
// "time-fmt": "2006-01-02 15:04:05.999999999"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
type gcpAuthProvider struct {
|
||||
tokenSource oauth2.TokenSource
|
||||
persister restclient.AuthProviderConfigPersister
|
||||
}
|
||||
|
||||
var warnOnce sync.Once
|
||||
|
||||
func newGCPAuthProvider(_ string, gcpConfig map[string]string, persister restclient.AuthProviderConfigPersister) (restclient.AuthProvider, error) {
|
||||
warnOnce.Do(func() {
|
||||
klog.Warningf(`WARNING: the gcp auth plugin is deprecated in v1.22+, unavailable in v1.26+; use gcloud instead.
|
||||
To learn more, consult https://cloud.google.com/blog/products/containers-kubernetes/kubectl-auth-changes-in-gke`)
|
||||
})
|
||||
|
||||
ts, err := tokenSource(isCmdTokenSource(gcpConfig), gcpConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cts, err := newCachedTokenSource(gcpConfig["access-token"], gcpConfig["expiry"], persister, ts, gcpConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &gcpAuthProvider{cts, persister}, nil
|
||||
}
|
||||
|
||||
func isCmdTokenSource(gcpConfig map[string]string) bool {
|
||||
_, ok := gcpConfig["cmd-path"]
|
||||
return ok
|
||||
}
|
||||
|
||||
func tokenSource(isCmd bool, gcpConfig map[string]string) (oauth2.TokenSource, error) {
|
||||
// Command-based token source
|
||||
if isCmd {
|
||||
cmd := gcpConfig["cmd-path"]
|
||||
if len(cmd) == 0 {
|
||||
return nil, fmt.Errorf("missing access token cmd")
|
||||
}
|
||||
if gcpConfig["scopes"] != "" {
|
||||
return nil, fmt.Errorf("scopes can only be used when kubectl is using a gcp service account key")
|
||||
}
|
||||
var args []string
|
||||
if cmdArgs, ok := gcpConfig["cmd-args"]; ok {
|
||||
args = strings.Fields(cmdArgs)
|
||||
} else {
|
||||
fields := strings.Fields(cmd)
|
||||
cmd = fields[0]
|
||||
args = fields[1:]
|
||||
}
|
||||
return newCmdTokenSource(cmd, args, gcpConfig["token-key"], gcpConfig["expiry-key"], gcpConfig["time-fmt"]), nil
|
||||
}
|
||||
|
||||
// Google Application Credentials-based token source
|
||||
scopes := parseScopes(gcpConfig)
|
||||
ts, err := google.DefaultTokenSource(context.Background(), scopes...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot construct google default token source: %v", err)
|
||||
}
|
||||
return ts, nil
|
||||
}
|
||||
|
||||
// parseScopes constructs a list of scopes that should be included in token source
|
||||
// from the config map.
|
||||
func parseScopes(gcpConfig map[string]string) []string {
|
||||
scopes, ok := gcpConfig["scopes"]
|
||||
if !ok {
|
||||
return defaultScopes
|
||||
}
|
||||
if scopes == "" {
|
||||
return []string{}
|
||||
}
|
||||
return strings.Split(gcpConfig["scopes"], ",")
|
||||
}
|
||||
|
||||
func (g *gcpAuthProvider) WrapTransport(rt http.RoundTripper) http.RoundTripper {
|
||||
var resetCache map[string]string
|
||||
if cts, ok := g.tokenSource.(*cachedTokenSource); ok {
|
||||
resetCache = cts.baseCache()
|
||||
} else {
|
||||
resetCache = make(map[string]string)
|
||||
}
|
||||
return &conditionalTransport{&oauth2.Transport{Source: g.tokenSource, Base: rt}, g.persister, resetCache}
|
||||
}
|
||||
|
||||
func (g *gcpAuthProvider) Login() error { return nil }
|
||||
|
||||
type cachedTokenSource struct {
|
||||
lk sync.Mutex
|
||||
source oauth2.TokenSource
|
||||
accessToken string `datapolicy:"token"`
|
||||
expiry time.Time
|
||||
persister restclient.AuthProviderConfigPersister
|
||||
cache map[string]string
|
||||
}
|
||||
|
||||
func newCachedTokenSource(accessToken, expiry string, persister restclient.AuthProviderConfigPersister, ts oauth2.TokenSource, cache map[string]string) (*cachedTokenSource, error) {
|
||||
var expiryTime time.Time
|
||||
if parsedTime, err := time.Parse(time.RFC3339Nano, expiry); err == nil {
|
||||
expiryTime = parsedTime
|
||||
}
|
||||
if cache == nil {
|
||||
cache = make(map[string]string)
|
||||
}
|
||||
return &cachedTokenSource{
|
||||
source: ts,
|
||||
accessToken: accessToken,
|
||||
expiry: expiryTime,
|
||||
persister: persister,
|
||||
cache: cache,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *cachedTokenSource) Token() (*oauth2.Token, error) {
|
||||
tok := t.cachedToken()
|
||||
if tok.Valid() && !tok.Expiry.IsZero() {
|
||||
return tok, nil
|
||||
}
|
||||
tok, err := t.source.Token()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cache := t.update(tok)
|
||||
if t.persister != nil {
|
||||
if err := t.persister.Persist(cache); err != nil {
|
||||
klog.V(4).Infof("Failed to persist token: %v", err)
|
||||
}
|
||||
}
|
||||
return tok, nil
|
||||
}
|
||||
|
||||
func (t *cachedTokenSource) cachedToken() *oauth2.Token {
|
||||
t.lk.Lock()
|
||||
defer t.lk.Unlock()
|
||||
return &oauth2.Token{
|
||||
AccessToken: t.accessToken,
|
||||
TokenType: "Bearer",
|
||||
Expiry: t.expiry,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *cachedTokenSource) update(tok *oauth2.Token) map[string]string {
|
||||
t.lk.Lock()
|
||||
defer t.lk.Unlock()
|
||||
t.accessToken = tok.AccessToken
|
||||
t.expiry = tok.Expiry
|
||||
ret := map[string]string{}
|
||||
for k, v := range t.cache {
|
||||
ret[k] = v
|
||||
}
|
||||
ret["access-token"] = t.accessToken
|
||||
ret["expiry"] = t.expiry.Format(time.RFC3339Nano)
|
||||
return ret
|
||||
}
|
||||
|
||||
// baseCache is the base configuration value for this TokenSource, without any cached ephemeral tokens.
|
||||
func (t *cachedTokenSource) baseCache() map[string]string {
|
||||
t.lk.Lock()
|
||||
defer t.lk.Unlock()
|
||||
ret := map[string]string{}
|
||||
for k, v := range t.cache {
|
||||
ret[k] = v
|
||||
}
|
||||
delete(ret, "access-token")
|
||||
delete(ret, "expiry")
|
||||
return ret
|
||||
}
|
||||
|
||||
type commandTokenSource struct {
|
||||
cmd string
|
||||
args []string
|
||||
tokenKey string `datapolicy:"token"`
|
||||
expiryKey string `datapolicy:"secret-key"`
|
||||
timeFmt string
|
||||
}
|
||||
|
||||
func newCmdTokenSource(cmd string, args []string, tokenKey, expiryKey, timeFmt string) *commandTokenSource {
|
||||
if len(timeFmt) == 0 {
|
||||
timeFmt = time.RFC3339Nano
|
||||
}
|
||||
if len(tokenKey) == 0 {
|
||||
tokenKey = "{.access_token}"
|
||||
}
|
||||
if len(expiryKey) == 0 {
|
||||
expiryKey = "{.token_expiry}"
|
||||
}
|
||||
return &commandTokenSource{
|
||||
cmd: cmd,
|
||||
args: args,
|
||||
tokenKey: tokenKey,
|
||||
expiryKey: expiryKey,
|
||||
timeFmt: timeFmt,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *commandTokenSource) Token() (*oauth2.Token, error) {
|
||||
fullCmd := strings.Join(append([]string{c.cmd}, c.args...), " ")
|
||||
cmd := execCommand(c.cmd, c.args...)
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stderr = &stderr
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error executing access token command %q: err=%v output=%s stderr=%s", fullCmd, err, output, string(stderr.Bytes()))
|
||||
}
|
||||
token, err := c.parseTokenCmdOutput(output)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing output for access token command %q: %v", fullCmd, err)
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (c *commandTokenSource) parseTokenCmdOutput(output []byte) (*oauth2.Token, error) {
|
||||
output, err := yaml.ToJSON(output)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var data interface{}
|
||||
if err := json.Unmarshal(output, &data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
accessToken, err := parseJSONPath(data, "token-key", c.tokenKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing token-key %q from %q: %v", c.tokenKey, string(output), err)
|
||||
}
|
||||
expiryStr, err := parseJSONPath(data, "expiry-key", c.expiryKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing expiry-key %q from %q: %v", c.expiryKey, string(output), err)
|
||||
}
|
||||
var expiry time.Time
|
||||
if t, err := time.Parse(c.timeFmt, expiryStr); err != nil {
|
||||
klog.V(4).Infof("Failed to parse token expiry from %s (fmt=%s): %v", expiryStr, c.timeFmt, err)
|
||||
} else {
|
||||
expiry = t
|
||||
}
|
||||
|
||||
return &oauth2.Token{
|
||||
AccessToken: accessToken,
|
||||
TokenType: "Bearer",
|
||||
Expiry: expiry,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseJSONPath(input interface{}, name, template string) (string, error) {
|
||||
j := jsonpath.New(name)
|
||||
buf := new(bytes.Buffer)
|
||||
if err := j.Parse(template); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := j.Execute(buf, input); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
type conditionalTransport struct {
|
||||
oauthTransport *oauth2.Transport
|
||||
persister restclient.AuthProviderConfigPersister
|
||||
resetCache map[string]string
|
||||
}
|
||||
|
||||
var _ net.RoundTripperWrapper = &conditionalTransport{}
|
||||
|
||||
func (t *conditionalTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
if len(req.Header.Get("Authorization")) != 0 {
|
||||
return t.oauthTransport.Base.RoundTrip(req)
|
||||
}
|
||||
|
||||
res, err := t.oauthTransport.RoundTrip(req)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if res.StatusCode == 401 {
|
||||
klog.V(4).Infof("The credentials that were supplied are invalid for the target cluster")
|
||||
t.persister.Persist(t.resetCache)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (t *conditionalTransport) WrappedRoundTripper() http.RoundTripper { return t.oauthTransport.Base }
|
4
vendor/k8s.io/client-go/plugin/pkg/client/auth/oidc/oidc.go
generated
vendored
4
vendor/k8s.io/client-go/plugin/pkg/client/auth/oidc/oidc.go
generated
vendored
@ -22,7 +22,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
@ -301,7 +301,7 @@ func tokenEndpoint(client *http.Client, issuer string) (string, error) {
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
Reference in New Issue
Block a user