mirror of https://github.com/k3s-io/k3s
Delete oidc
parent
dc848a5d5e
commit
7b622843ff
|
@ -34,7 +34,6 @@ import (
|
|||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/apiserver/plugin/pkg/authenticator/password/passwordfile"
|
||||
"k8s.io/apiserver/plugin/pkg/authenticator/request/basicauth"
|
||||
"k8s.io/apiserver/plugin/pkg/authenticator/token/oidc"
|
||||
"k8s.io/apiserver/plugin/pkg/authenticator/token/webhook"
|
||||
// Initialize all known client auth plugins.
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
|
@ -49,15 +48,6 @@ type Config struct {
|
|||
BasicAuthFile string
|
||||
ClientCAFile string
|
||||
TokenAuthFile string
|
||||
OIDCIssuerURL string
|
||||
OIDCClientID string
|
||||
OIDCCAFile string
|
||||
OIDCUsernameClaim string
|
||||
OIDCUsernamePrefix string
|
||||
OIDCGroupsClaim string
|
||||
OIDCGroupsPrefix string
|
||||
OIDCSigningAlgs []string
|
||||
OIDCRequiredClaims map[string]string
|
||||
ServiceAccountKeyFiles []string
|
||||
ServiceAccountLookup bool
|
||||
ServiceAccountIssuer string
|
||||
|
@ -136,30 +126,6 @@ func (config Config) New() (authenticator.Request, error) {
|
|||
}
|
||||
tokenAuthenticators = append(tokenAuthenticators, serviceAccountAuth)
|
||||
}
|
||||
// NOTE(ericchiang): Keep the OpenID Connect after Service Accounts.
|
||||
//
|
||||
// Because both plugins verify JWTs whichever comes first in the union experiences
|
||||
// cache misses for all requests using the other. While the service account plugin
|
||||
// simply returns an error, the OpenID Connect plugin may query the provider to
|
||||
// update the keys, causing performance hits.
|
||||
if len(config.OIDCIssuerURL) > 0 && len(config.OIDCClientID) > 0 {
|
||||
oidcAuth, err := newAuthenticatorFromOIDCIssuerURL(oidc.Options{
|
||||
IssuerURL: config.OIDCIssuerURL,
|
||||
ClientID: config.OIDCClientID,
|
||||
APIAudiences: config.APIAudiences,
|
||||
CAFile: config.OIDCCAFile,
|
||||
UsernameClaim: config.OIDCUsernameClaim,
|
||||
UsernamePrefix: config.OIDCUsernamePrefix,
|
||||
GroupsClaim: config.OIDCGroupsClaim,
|
||||
GroupsPrefix: config.OIDCGroupsPrefix,
|
||||
SupportedSigningAlgs: config.OIDCSigningAlgs,
|
||||
RequiredClaims: config.OIDCRequiredClaims,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tokenAuthenticators = append(tokenAuthenticators, oidcAuth)
|
||||
}
|
||||
if len(config.WebhookTokenAuthnConfigFile) > 0 {
|
||||
webhookTokenAuth, err := newWebhookTokenAuthenticator(config.WebhookTokenAuthnConfigFile, config.WebhookTokenAuthnCacheTTL, config.APIAudiences)
|
||||
if err != nil {
|
||||
|
@ -224,31 +190,6 @@ func newAuthenticatorFromTokenFile(tokenAuthFile string) (authenticator.Token, e
|
|||
return tokenAuthenticator, nil
|
||||
}
|
||||
|
||||
// newAuthenticatorFromOIDCIssuerURL returns an authenticator.Token or an error.
|
||||
func newAuthenticatorFromOIDCIssuerURL(opts oidc.Options) (authenticator.Token, error) {
|
||||
const noUsernamePrefix = "-"
|
||||
|
||||
if opts.UsernamePrefix == "" && opts.UsernameClaim != "email" {
|
||||
// Old behavior. If a usernamePrefix isn't provided, prefix all claims other than "email"
|
||||
// with the issuerURL.
|
||||
//
|
||||
// See https://github.com/kubernetes/kubernetes/issues/31380
|
||||
opts.UsernamePrefix = opts.IssuerURL + "#"
|
||||
}
|
||||
|
||||
if opts.UsernamePrefix == noUsernamePrefix {
|
||||
// Special value indicating usernames shouldn't be prefixed.
|
||||
opts.UsernamePrefix = ""
|
||||
}
|
||||
|
||||
tokenAuthenticator, err := oidc.New(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tokenAuthenticator, nil
|
||||
}
|
||||
|
||||
// newLegacyServiceAccountAuthenticator returns an authenticator.Token or an error
|
||||
func newLegacyServiceAccountAuthenticator(keyfiles []string, lookup bool, apiAudiences authenticator.Audiences, serviceAccountGetter serviceaccount.ServiceAccountTokenGetter) (authenticator.Token, error) {
|
||||
allPublicKeys := []interface{}{}
|
||||
|
|
|
@ -29,7 +29,6 @@ import (
|
|||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
genericoptions "k8s.io/apiserver/pkg/server/options"
|
||||
"k8s.io/apiserver/pkg/util/flag"
|
||||
kubeauthenticator "k8s.io/kubernetes/pkg/kubeapiserver/authenticator"
|
||||
authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
|
||||
)
|
||||
|
@ -38,7 +37,6 @@ type BuiltInAuthenticationOptions struct {
|
|||
APIAudiences []string
|
||||
Anonymous *AnonymousAuthenticationOptions
|
||||
ClientCert *genericoptions.ClientCertAuthenticationOptions
|
||||
OIDC *OIDCAuthenticationOptions
|
||||
PasswordFile *PasswordFileAuthenticationOptions
|
||||
RequestHeader *genericoptions.RequestHeaderAuthenticationOptions
|
||||
ServiceAccounts *ServiceAccountAuthenticationOptions
|
||||
|
@ -53,18 +51,6 @@ type AnonymousAuthenticationOptions struct {
|
|||
Allow bool
|
||||
}
|
||||
|
||||
type OIDCAuthenticationOptions struct {
|
||||
CAFile string
|
||||
ClientID string
|
||||
IssuerURL string
|
||||
UsernameClaim string
|
||||
UsernamePrefix string
|
||||
GroupsClaim string
|
||||
GroupsPrefix string
|
||||
SigningAlgs []string
|
||||
RequiredClaims map[string]string
|
||||
}
|
||||
|
||||
type PasswordFileAuthenticationOptions struct {
|
||||
BasicAuthFile string
|
||||
}
|
||||
|
@ -96,7 +82,6 @@ func (s *BuiltInAuthenticationOptions) WithAll() *BuiltInAuthenticationOptions {
|
|||
return s.
|
||||
WithAnonymous().
|
||||
WithClientCert().
|
||||
WithOIDC().
|
||||
WithPasswordFile().
|
||||
WithRequestHeader().
|
||||
WithServiceAccounts().
|
||||
|
@ -114,11 +99,6 @@ func (s *BuiltInAuthenticationOptions) WithClientCert() *BuiltInAuthenticationOp
|
|||
return s
|
||||
}
|
||||
|
||||
func (s *BuiltInAuthenticationOptions) WithOIDC() *BuiltInAuthenticationOptions {
|
||||
s.OIDC = &OIDCAuthenticationOptions{}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *BuiltInAuthenticationOptions) WithPasswordFile() *BuiltInAuthenticationOptions {
|
||||
s.PasswordFile = &PasswordFileAuthenticationOptions{}
|
||||
return s
|
||||
|
@ -150,10 +130,6 @@ func (s *BuiltInAuthenticationOptions) WithWebHook() *BuiltInAuthenticationOptio
|
|||
func (s *BuiltInAuthenticationOptions) Validate() []error {
|
||||
allErrors := []error{}
|
||||
|
||||
if s.OIDC != nil && (len(s.OIDC.IssuerURL) > 0) != (len(s.OIDC.ClientID) > 0) {
|
||||
allErrors = append(allErrors, fmt.Errorf("oidc-issuer-url and oidc-client-id should be specified together"))
|
||||
}
|
||||
|
||||
if s.ServiceAccounts != nil && len(s.ServiceAccounts.Issuer) > 0 && strings.Contains(s.ServiceAccounts.Issuer, ":") {
|
||||
if _, err := url.Parse(s.ServiceAccounts.Issuer); err != nil {
|
||||
allErrors = append(allErrors, fmt.Errorf("service-account-issuer contained a ':' but was not a valid URL: %v", err))
|
||||
|
@ -181,48 +157,6 @@ func (s *BuiltInAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
|
|||
s.ClientCert.AddFlags(fs)
|
||||
}
|
||||
|
||||
if s.OIDC != nil {
|
||||
fs.StringVar(&s.OIDC.IssuerURL, "oidc-issuer-url", s.OIDC.IssuerURL, ""+
|
||||
"The URL of the OpenID issuer, only HTTPS scheme will be accepted. "+
|
||||
"If set, it will be used to verify the OIDC JSON Web Token (JWT).")
|
||||
|
||||
fs.StringVar(&s.OIDC.ClientID, "oidc-client-id", s.OIDC.ClientID,
|
||||
"The client ID for the OpenID Connect client, must be set if oidc-issuer-url is set.")
|
||||
|
||||
fs.StringVar(&s.OIDC.CAFile, "oidc-ca-file", s.OIDC.CAFile, ""+
|
||||
"If set, the OpenID server's certificate will be verified by one of the authorities "+
|
||||
"in the oidc-ca-file, otherwise the host's root CA set will be used.")
|
||||
|
||||
fs.StringVar(&s.OIDC.UsernameClaim, "oidc-username-claim", "sub", ""+
|
||||
"The OpenID claim to use as the user name. Note that claims other than the default ('sub') "+
|
||||
"is not guaranteed to be unique and immutable. This flag is experimental, please see "+
|
||||
"the authentication documentation for further details.")
|
||||
|
||||
fs.StringVar(&s.OIDC.UsernamePrefix, "oidc-username-prefix", "", ""+
|
||||
"If provided, all usernames will be prefixed with this value. If not provided, "+
|
||||
"username claims other than 'email' are prefixed by the issuer URL to avoid "+
|
||||
"clashes. To skip any prefixing, provide the value '-'.")
|
||||
|
||||
fs.StringVar(&s.OIDC.GroupsClaim, "oidc-groups-claim", "", ""+
|
||||
"If provided, the name of a custom OpenID Connect claim for specifying user groups. "+
|
||||
"The claim value is expected to be a string or array of strings. This flag is experimental, "+
|
||||
"please see the authentication documentation for further details.")
|
||||
|
||||
fs.StringVar(&s.OIDC.GroupsPrefix, "oidc-groups-prefix", "", ""+
|
||||
"If provided, all groups will be prefixed with this value to prevent conflicts with "+
|
||||
"other authentication strategies.")
|
||||
|
||||
fs.StringSliceVar(&s.OIDC.SigningAlgs, "oidc-signing-algs", []string{"RS256"}, ""+
|
||||
"Comma-separated list of allowed JOSE asymmetric signing algorithms. JWTs with a "+
|
||||
"'alg' header value not in this list will be rejected. "+
|
||||
"Values are defined by RFC 7518 https://tools.ietf.org/html/rfc7518#section-3.1.")
|
||||
|
||||
fs.Var(flag.NewMapStringStringNoSplit(&s.OIDC.RequiredClaims), "oidc-required-claim", ""+
|
||||
"A key=value pair that describes a required claim in the ID Token. "+
|
||||
"If set, the claim is verified to be present in the ID Token with a matching value. "+
|
||||
"Repeat this flag to specify multiple claims.")
|
||||
}
|
||||
|
||||
if s.PasswordFile != nil {
|
||||
fs.StringVar(&s.PasswordFile.BasicAuthFile, "basic-auth-file", s.PasswordFile.BasicAuthFile, ""+
|
||||
"If set, the file that will be used to admit requests to the secure port of the API server "+
|
||||
|
@ -289,18 +223,6 @@ func (s *BuiltInAuthenticationOptions) ToAuthenticationConfig() kubeauthenticato
|
|||
ret.ClientCAFile = s.ClientCert.ClientCA
|
||||
}
|
||||
|
||||
if s.OIDC != nil {
|
||||
ret.OIDCCAFile = s.OIDC.CAFile
|
||||
ret.OIDCClientID = s.OIDC.ClientID
|
||||
ret.OIDCGroupsClaim = s.OIDC.GroupsClaim
|
||||
ret.OIDCGroupsPrefix = s.OIDC.GroupsPrefix
|
||||
ret.OIDCIssuerURL = s.OIDC.IssuerURL
|
||||
ret.OIDCUsernameClaim = s.OIDC.UsernameClaim
|
||||
ret.OIDCUsernamePrefix = s.OIDC.UsernamePrefix
|
||||
ret.OIDCSigningAlgs = s.OIDC.SigningAlgs
|
||||
ret.OIDCRequiredClaims = s.OIDC.RequiredClaims
|
||||
}
|
||||
|
||||
if s.PasswordFile != nil {
|
||||
ret.BasicAuthFile = s.PasswordFile.BasicAuthFile
|
||||
}
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "small",
|
||||
srcs = ["oidc_test.go"],
|
||||
data = glob(["testdata/**"]),
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//staging/src/k8s.io/apiserver/pkg/authentication/authenticator:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||
"//vendor/github.com/coreos/go-oidc:go_default_library",
|
||||
"//vendor/gopkg.in/square/go-jose.v2:go_default_library",
|
||||
"//vendor/k8s.io/klog:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["oidc.go"],
|
||||
importmap = "k8s.io/kubernetes/vendor/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc",
|
||||
importpath = "k8s.io/apiserver/plugin/pkg/authenticator/token/oidc",
|
||||
deps = [
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/authentication/authenticator:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/util/cert:go_default_library",
|
||||
"//vendor/github.com/coreos/go-oidc:go_default_library",
|
||||
"//vendor/k8s.io/klog:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
|
@ -1,5 +0,0 @@
|
|||
approvers:
|
||||
- ericchiang
|
||||
reviewers:
|
||||
- ericchiang
|
||||
- rithujohn191
|
|
@ -1,703 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
oidc implements the authenticator.Token interface using the OpenID Connect protocol.
|
||||
|
||||
config := oidc.Options{
|
||||
IssuerURL: "https://accounts.google.com",
|
||||
ClientID: os.Getenv("GOOGLE_CLIENT_ID"),
|
||||
UsernameClaim: "email",
|
||||
}
|
||||
tokenAuthenticator, err := oidc.New(config)
|
||||
*/
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
oidc "github.com/coreos/go-oidc"
|
||||
"k8s.io/klog"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/net"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
)
|
||||
|
||||
var (
|
||||
// synchronizeTokenIDVerifierForTest should be set to true to force a
|
||||
// wait until the token ID verifiers are ready.
|
||||
synchronizeTokenIDVerifierForTest = false
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
// IssuerURL is the URL the provider signs ID Tokens as. This will be the "iss"
|
||||
// field of all tokens produced by the provider and is used for configuration
|
||||
// discovery.
|
||||
//
|
||||
// The URL is usually the provider's URL without a path, for example
|
||||
// "https://accounts.google.com" or "https://login.salesforce.com".
|
||||
//
|
||||
// The provider must implement configuration discovery.
|
||||
// See: https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig
|
||||
IssuerURL string
|
||||
|
||||
// ClientID the JWT must be issued for, the "sub" field. This plugin only trusts a single
|
||||
// client to ensure the plugin can be used with public providers.
|
||||
//
|
||||
// The plugin supports the "authorized party" OpenID Connect claim, which allows
|
||||
// specialized providers to issue tokens to a client for a different client.
|
||||
// See: https://openid.net/specs/openid-connect-core-1_0.html#IDToken
|
||||
ClientID string
|
||||
|
||||
// APIAudiences are the audiences that the API server identitifes as. The
|
||||
// (API audiences unioned with the ClientIDs) should have a non-empty
|
||||
// intersection with the request's target audience. This preserves the
|
||||
// behavior of the OIDC authenticator pre-introduction of API audiences.
|
||||
APIAudiences authenticator.Audiences
|
||||
|
||||
// Path to a PEM encoded root certificate of the provider.
|
||||
CAFile string
|
||||
|
||||
// UsernameClaim is the JWT field to use as the user's username.
|
||||
UsernameClaim string
|
||||
|
||||
// UsernamePrefix, if specified, causes claims mapping to username to be prefix with
|
||||
// the provided value. A value "oidc:" would result in usernames like "oidc:john".
|
||||
UsernamePrefix string
|
||||
|
||||
// GroupsClaim, if specified, causes the OIDCAuthenticator to try to populate the user's
|
||||
// groups with an ID Token field. If the GroupsClaim field is present in an ID Token the value
|
||||
// must be a string or list of strings.
|
||||
GroupsClaim string
|
||||
|
||||
// GroupsPrefix, if specified, causes claims mapping to group names to be prefixed with the
|
||||
// value. A value "oidc:" would result in groups like "oidc:engineering" and "oidc:marketing".
|
||||
GroupsPrefix string
|
||||
|
||||
// SupportedSigningAlgs sets the accepted set of JOSE signing algorithms that
|
||||
// can be used by the provider to sign tokens.
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc7518#section-3.1
|
||||
//
|
||||
// This value defaults to RS256, the value recommended by the OpenID Connect
|
||||
// spec:
|
||||
//
|
||||
// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
|
||||
SupportedSigningAlgs []string
|
||||
|
||||
// RequiredClaims, if specified, causes the OIDCAuthenticator to verify that all the
|
||||
// required claims key value pairs are present in the ID Token.
|
||||
RequiredClaims map[string]string
|
||||
|
||||
// now is used for testing. It defaults to time.Now.
|
||||
now func() time.Time
|
||||
}
|
||||
|
||||
// initVerifier creates a new ID token verifier for the given configuration and issuer URL. On success, calls setVerifier with the
|
||||
// resulting verifier.
|
||||
func initVerifier(ctx context.Context, config *oidc.Config, iss string) (*oidc.IDTokenVerifier, error) {
|
||||
provider, err := oidc.NewProvider(ctx, iss)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("init verifier failed: %v", err)
|
||||
}
|
||||
return provider.Verifier(config), nil
|
||||
}
|
||||
|
||||
// asyncIDTokenVerifier is an ID token verifier that allows async initialization
|
||||
// of the issuer check. Must be passed by reference as it wraps sync.Mutex.
|
||||
type asyncIDTokenVerifier struct {
|
||||
m sync.Mutex
|
||||
|
||||
// v is the ID token verifier initialized asynchronously. It remains nil
|
||||
// up until it is eventually initialized.
|
||||
// Guarded by m
|
||||
v *oidc.IDTokenVerifier
|
||||
}
|
||||
|
||||
// newAsyncIDTokenVerifier creates a new asynchronous token verifier. The
|
||||
// verifier is available immediately, but may remain uninitialized for some time
|
||||
// after creation.
|
||||
func newAsyncIDTokenVerifier(ctx context.Context, c *oidc.Config, iss string) *asyncIDTokenVerifier {
|
||||
t := &asyncIDTokenVerifier{}
|
||||
|
||||
sync := make(chan struct{})
|
||||
// Polls indefinitely in an attempt to initialize the distributed claims
|
||||
// verifier, or until context canceled.
|
||||
initFn := func() (done bool, err error) {
|
||||
klog.V(4).Infof("oidc authenticator: attempting init: iss=%v", iss)
|
||||
v, err := initVerifier(ctx, c, iss)
|
||||
if err != nil {
|
||||
klog.Errorf("oidc authenticator: async token verifier for issuer: %q: %v", iss, err)
|
||||
return false, nil
|
||||
}
|
||||
t.m.Lock()
|
||||
defer t.m.Unlock()
|
||||
t.v = v
|
||||
close(sync)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
go func() {
|
||||
if done, _ := initFn(); !done {
|
||||
go wait.PollUntil(time.Second*10, initFn, ctx.Done())
|
||||
}
|
||||
}()
|
||||
|
||||
if synchronizeTokenIDVerifierForTest {
|
||||
<-sync
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
// verifier returns the underlying ID token verifier, or nil if one is not yet initialized.
|
||||
func (a *asyncIDTokenVerifier) verifier() *oidc.IDTokenVerifier {
|
||||
a.m.Lock()
|
||||
defer a.m.Unlock()
|
||||
return a.v
|
||||
}
|
||||
|
||||
type Authenticator struct {
|
||||
issuerURL string
|
||||
|
||||
usernameClaim string
|
||||
usernamePrefix string
|
||||
groupsClaim string
|
||||
groupsPrefix string
|
||||
requiredClaims map[string]string
|
||||
clientIDs authenticator.Audiences
|
||||
apiAudiences authenticator.Audiences
|
||||
|
||||
// Contains an *oidc.IDTokenVerifier. Do not access directly use the
|
||||
// idTokenVerifier method.
|
||||
verifier atomic.Value
|
||||
|
||||
cancel context.CancelFunc
|
||||
|
||||
// resolver is used to resolve distributed claims.
|
||||
resolver *claimResolver
|
||||
}
|
||||
|
||||
func (a *Authenticator) setVerifier(v *oidc.IDTokenVerifier) {
|
||||
a.verifier.Store(v)
|
||||
}
|
||||
|
||||
func (a *Authenticator) idTokenVerifier() (*oidc.IDTokenVerifier, bool) {
|
||||
if v := a.verifier.Load(); v != nil {
|
||||
return v.(*oidc.IDTokenVerifier), true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (a *Authenticator) Close() {
|
||||
a.cancel()
|
||||
}
|
||||
|
||||
func New(opts Options) (*Authenticator, error) {
|
||||
return newAuthenticator(opts, func(ctx context.Context, a *Authenticator, config *oidc.Config) {
|
||||
// Asynchronously attempt to initialize the authenticator. This enables
|
||||
// self-hosted providers, providers that run on top of Kubernetes itself.
|
||||
go wait.PollUntil(time.Second*10, func() (done bool, err error) {
|
||||
provider, err := oidc.NewProvider(ctx, a.issuerURL)
|
||||
if err != nil {
|
||||
klog.Errorf("oidc authenticator: initializing plugin: %v", err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
verifier := provider.Verifier(config)
|
||||
a.setVerifier(verifier)
|
||||
return true, nil
|
||||
}, ctx.Done())
|
||||
})
|
||||
}
|
||||
|
||||
// whitelist of signing algorithms to ensure users don't mistakenly pass something
|
||||
// goofy.
|
||||
var allowedSigningAlgs = map[string]bool{
|
||||
oidc.RS256: true,
|
||||
oidc.RS384: true,
|
||||
oidc.RS512: true,
|
||||
oidc.ES256: true,
|
||||
oidc.ES384: true,
|
||||
oidc.ES512: true,
|
||||
oidc.PS256: true,
|
||||
oidc.PS384: true,
|
||||
oidc.PS512: true,
|
||||
}
|
||||
|
||||
func newAuthenticator(opts Options, initVerifier func(ctx context.Context, a *Authenticator, config *oidc.Config)) (*Authenticator, error) {
|
||||
url, err := url.Parse(opts.IssuerURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if url.Scheme != "https" {
|
||||
return nil, fmt.Errorf("'oidc-issuer-url' (%q) has invalid scheme (%q), require 'https'", opts.IssuerURL, url.Scheme)
|
||||
}
|
||||
|
||||
if opts.UsernameClaim == "" {
|
||||
return nil, errors.New("no username claim provided")
|
||||
}
|
||||
|
||||
supportedSigningAlgs := opts.SupportedSigningAlgs
|
||||
if len(supportedSigningAlgs) == 0 {
|
||||
// RS256 is the default recommended by OpenID Connect and an 'alg' value
|
||||
// providers are required to implement.
|
||||
supportedSigningAlgs = []string{oidc.RS256}
|
||||
}
|
||||
for _, alg := range supportedSigningAlgs {
|
||||
if !allowedSigningAlgs[alg] {
|
||||
return nil, fmt.Errorf("oidc: unsupported signing alg: %q", alg)
|
||||
}
|
||||
}
|
||||
|
||||
var roots *x509.CertPool
|
||||
if opts.CAFile != "" {
|
||||
roots, err = certutil.NewPool(opts.CAFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to read the CA file: %v", err)
|
||||
}
|
||||
} else {
|
||||
klog.Info("OIDC: No x509 certificates provided, will use host's root CA set")
|
||||
}
|
||||
|
||||
// Copied from http.DefaultTransport.
|
||||
tr := net.SetTransportDefaults(&http.Transport{
|
||||
// According to golang's doc, if RootCAs is nil,
|
||||
// TLS uses the host's root CA set.
|
||||
TLSClientConfig: &tls.Config{RootCAs: roots},
|
||||
})
|
||||
|
||||
client := &http.Client{Transport: tr, Timeout: 30 * time.Second}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx = oidc.ClientContext(ctx, client)
|
||||
|
||||
now := opts.now
|
||||
if now == nil {
|
||||
now = time.Now
|
||||
}
|
||||
|
||||
verifierConfig := &oidc.Config{
|
||||
ClientID: opts.ClientID,
|
||||
SupportedSigningAlgs: supportedSigningAlgs,
|
||||
Now: now,
|
||||
}
|
||||
|
||||
var resolver *claimResolver
|
||||
if opts.GroupsClaim != "" {
|
||||
resolver = newClaimResolver(opts.GroupsClaim, client, verifierConfig)
|
||||
}
|
||||
|
||||
authenticator := &Authenticator{
|
||||
issuerURL: opts.IssuerURL,
|
||||
usernameClaim: opts.UsernameClaim,
|
||||
usernamePrefix: opts.UsernamePrefix,
|
||||
groupsClaim: opts.GroupsClaim,
|
||||
groupsPrefix: opts.GroupsPrefix,
|
||||
requiredClaims: opts.RequiredClaims,
|
||||
clientIDs: authenticator.Audiences{opts.ClientID},
|
||||
apiAudiences: opts.APIAudiences,
|
||||
cancel: cancel,
|
||||
resolver: resolver,
|
||||
}
|
||||
|
||||
initVerifier(ctx, authenticator, verifierConfig)
|
||||
return authenticator, nil
|
||||
}
|
||||
|
||||
// untrustedIssuer extracts an untrusted "iss" claim from the given JWT token,
|
||||
// or returns an error if the token can not be parsed. Since the JWT is not
|
||||
// verified, the returned issuer should not be trusted.
|
||||
func untrustedIssuer(token string) (string, error) {
|
||||
parts := strings.Split(token, ".")
|
||||
if len(parts) != 3 {
|
||||
return "", fmt.Errorf("malformed token")
|
||||
}
|
||||
payload, err := base64.RawURLEncoding.DecodeString(parts[1])
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error decoding token: %v", err)
|
||||
}
|
||||
claims := struct {
|
||||
// WARNING: this JWT is not verified. Do not trust these claims.
|
||||
Issuer string `json:"iss"`
|
||||
}{}
|
||||
if err := json.Unmarshal(payload, &claims); err != nil {
|
||||
return "", fmt.Errorf("while unmarshaling token: %v", err)
|
||||
}
|
||||
// Coalesce the legacy GoogleIss with the new one.
|
||||
//
|
||||
// http://openid.net/specs/openid-connect-core-1_0.html#GoogleIss
|
||||
if claims.Issuer == "accounts.google.com" {
|
||||
return "https://accounts.google.com", nil
|
||||
}
|
||||
return claims.Issuer, nil
|
||||
}
|
||||
|
||||
func hasCorrectIssuer(iss, tokenData string) bool {
|
||||
uiss, err := untrustedIssuer(tokenData)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if uiss != iss {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// endpoint represents an OIDC distributed claims endpoint.
|
||||
type endpoint struct {
|
||||
// URL to use to request the distributed claim. This URL is expected to be
|
||||
// prefixed by one of the known issuer URLs.
|
||||
URL string `json:"endpoint,omitempty"`
|
||||
// AccessToken is the bearer token to use for access. If empty, it is
|
||||
// not used. Access token is optional per the OIDC distributed claims
|
||||
// specification.
|
||||
// See: http://openid.net/specs/openid-connect-core-1_0.html#DistributedExample
|
||||
AccessToken string `json:"access_token,omitempty"`
|
||||
// JWT is the container for aggregated claims. Not supported at the moment.
|
||||
// See: http://openid.net/specs/openid-connect-core-1_0.html#AggregatedExample
|
||||
JWT string `json:"JWT,omitempty"`
|
||||
}
|
||||
|
||||
// claimResolver expands distributed claims by calling respective claim source
|
||||
// endpoints.
|
||||
type claimResolver struct {
|
||||
// claim is the distributed claim that may be resolved.
|
||||
claim string
|
||||
|
||||
// client is the to use for resolving distributed claims
|
||||
client *http.Client
|
||||
|
||||
// config is the OIDC configuration used for resolving distributed claims.
|
||||
config *oidc.Config
|
||||
|
||||
// verifierPerIssuer contains, for each issuer, the appropriate verifier to use
|
||||
// for this claim. It is assumed that there will be very few entries in
|
||||
// this map.
|
||||
// Guarded by m.
|
||||
verifierPerIssuer map[string]*asyncIDTokenVerifier
|
||||
|
||||
m sync.Mutex
|
||||
}
|
||||
|
||||
// newClaimResolver creates a new resolver for distributed claims.
|
||||
func newClaimResolver(claim string, client *http.Client, config *oidc.Config) *claimResolver {
|
||||
return &claimResolver{claim: claim, client: client, config: config, verifierPerIssuer: map[string]*asyncIDTokenVerifier{}}
|
||||
}
|
||||
|
||||
// Verifier returns either the verifier for the specified issuer, or error.
|
||||
func (r *claimResolver) Verifier(iss string) (*oidc.IDTokenVerifier, error) {
|
||||
r.m.Lock()
|
||||
av := r.verifierPerIssuer[iss]
|
||||
if av == nil {
|
||||
// This lazy init should normally be very quick.
|
||||
// TODO: Make this context cancelable.
|
||||
ctx := oidc.ClientContext(context.Background(), r.client)
|
||||
av = newAsyncIDTokenVerifier(ctx, r.config, iss)
|
||||
r.verifierPerIssuer[iss] = av
|
||||
}
|
||||
r.m.Unlock()
|
||||
|
||||
v := av.verifier()
|
||||
if v == nil {
|
||||
return nil, fmt.Errorf("verifier not initialized for issuer: %q", iss)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// expand extracts the distributed claims from claim names and claim sources.
|
||||
// The extracted claim value is pulled up into the supplied claims.
|
||||
//
|
||||
// Distributed claims are of the form as seen below, and are defined in the
|
||||
// OIDC Connect Core 1.0, section 5.6.2.
|
||||
// See: https://openid.net/specs/openid-connect-core-1_0.html#AggregatedDistributedClaims
|
||||
//
|
||||
// {
|
||||
// ... (other normal claims)...
|
||||
// "_claim_names": {
|
||||
// "groups": "src1"
|
||||
// },
|
||||
// "_claim_sources": {
|
||||
// "src1": {
|
||||
// "endpoint": "https://www.example.com",
|
||||
// "access_token": "f005ba11"
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
func (r *claimResolver) expand(c claims) error {
|
||||
const (
|
||||
// The claim containing a map of endpoint references per claim.
|
||||
// OIDC Connect Core 1.0, section 5.6.2.
|
||||
claimNamesKey = "_claim_names"
|
||||
// The claim containing endpoint specifications.
|
||||
// OIDC Connect Core 1.0, section 5.6.2.
|
||||
claimSourcesKey = "_claim_sources"
|
||||
)
|
||||
|
||||
_, ok := c[r.claim]
|
||||
if ok {
|
||||
// There already is a normal claim, skip resolving.
|
||||
return nil
|
||||
}
|
||||
names, ok := c[claimNamesKey]
|
||||
if !ok {
|
||||
// No _claim_names, no keys to look up.
|
||||
return nil
|
||||
}
|
||||
|
||||
claimToSource := map[string]string{}
|
||||
if err := json.Unmarshal([]byte(names), &claimToSource); err != nil {
|
||||
return fmt.Errorf("oidc: error parsing distributed claim names: %v", err)
|
||||
}
|
||||
|
||||
rawSources, ok := c[claimSourcesKey]
|
||||
if !ok {
|
||||
// Having _claim_names claim, but no _claim_sources is not an expected
|
||||
// state.
|
||||
return fmt.Errorf("oidc: no claim sources")
|
||||
}
|
||||
|
||||
var sources map[string]endpoint
|
||||
if err := json.Unmarshal([]byte(rawSources), &sources); err != nil {
|
||||
// The claims sources claim is malformed, this is not an expected state.
|
||||
return fmt.Errorf("oidc: could not parse claim sources: %v", err)
|
||||
}
|
||||
|
||||
src, ok := claimToSource[r.claim]
|
||||
if !ok {
|
||||
// No distributed claim present.
|
||||
return nil
|
||||
}
|
||||
ep, ok := sources[src]
|
||||
if !ok {
|
||||
return fmt.Errorf("id token _claim_names contained a source %s missing in _claims_sources", src)
|
||||
}
|
||||
if ep.URL == "" {
|
||||
// This is maybe an aggregated claim (ep.JWT != "").
|
||||
return nil
|
||||
}
|
||||
return r.resolve(ep, c)
|
||||
}
|
||||
|
||||
// resolve requests distributed claims from all endpoints passed in,
|
||||
// and inserts the lookup results into allClaims.
|
||||
func (r *claimResolver) resolve(endpoint endpoint, allClaims claims) error {
|
||||
// TODO: cache resolved claims.
|
||||
jwt, err := getClaimJWT(r.client, endpoint.URL, endpoint.AccessToken)
|
||||
if err != nil {
|
||||
return fmt.Errorf("while getting distributed claim %q: %v", r.claim, err)
|
||||
}
|
||||
untrustedIss, err := untrustedIssuer(jwt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting untrusted issuer from endpoint %v failed for claim %q: %v", endpoint.URL, r.claim, err)
|
||||
}
|
||||
v, err := r.Verifier(untrustedIss)
|
||||
if err != nil {
|
||||
return fmt.Errorf("verifying untrusted issuer %v failed: %v", untrustedIss, err)
|
||||
}
|
||||
t, err := v.Verify(context.Background(), jwt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("verify distributed claim token: %v", err)
|
||||
}
|
||||
var distClaims claims
|
||||
if err := t.Claims(&distClaims); err != nil {
|
||||
return fmt.Errorf("could not parse distributed claims for claim %v: %v", r.claim, err)
|
||||
}
|
||||
value, ok := distClaims[r.claim]
|
||||
if !ok {
|
||||
return fmt.Errorf("jwt returned by distributed claim endpoint %s did not contain claim: %v", endpoint, r.claim)
|
||||
}
|
||||
allClaims[r.claim] = value
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Authenticator) AuthenticateToken(ctx context.Context, token string) (*authenticator.Response, bool, error) {
|
||||
if reqAuds, ok := authenticator.AudiencesFrom(ctx); ok {
|
||||
if len(reqAuds.Intersect(a.clientIDs)) == 0 && len(reqAuds.Intersect(a.apiAudiences)) == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
}
|
||||
if !hasCorrectIssuer(a.issuerURL, token) {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
verifier, ok := a.idTokenVerifier()
|
||||
if !ok {
|
||||
return nil, false, fmt.Errorf("oidc: authenticator not initialized")
|
||||
}
|
||||
|
||||
idToken, err := verifier.Verify(ctx, token)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("oidc: verify token: %v", err)
|
||||
}
|
||||
|
||||
var c claims
|
||||
if err := idToken.Claims(&c); err != nil {
|
||||
return nil, false, fmt.Errorf("oidc: parse claims: %v", err)
|
||||
}
|
||||
if a.resolver != nil {
|
||||
if err := a.resolver.expand(c); err != nil {
|
||||
return nil, false, fmt.Errorf("oidc: could not expand distributed claims: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
var username string
|
||||
if err := c.unmarshalClaim(a.usernameClaim, &username); err != nil {
|
||||
return nil, false, fmt.Errorf("oidc: parse username claims %q: %v", a.usernameClaim, err)
|
||||
}
|
||||
|
||||
if a.usernameClaim == "email" {
|
||||
// If the email_verified claim is present, ensure the email is valid.
|
||||
// https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
|
||||
if hasEmailVerified := c.hasClaim("email_verified"); hasEmailVerified {
|
||||
var emailVerified bool
|
||||
if err := c.unmarshalClaim("email_verified", &emailVerified); err != nil {
|
||||
return nil, false, fmt.Errorf("oidc: parse 'email_verified' claim: %v", err)
|
||||
}
|
||||
|
||||
// If the email_verified claim is present we have to verify it is set to `true`.
|
||||
if !emailVerified {
|
||||
return nil, false, fmt.Errorf("oidc: email not verified")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if a.usernamePrefix != "" {
|
||||
username = a.usernamePrefix + username
|
||||
}
|
||||
|
||||
info := &user.DefaultInfo{Name: username}
|
||||
if a.groupsClaim != "" {
|
||||
if _, ok := c[a.groupsClaim]; ok {
|
||||
// Some admins want to use string claims like "role" as the group value.
|
||||
// Allow the group claim to be a single string instead of an array.
|
||||
//
|
||||
// See: https://github.com/kubernetes/kubernetes/issues/33290
|
||||
var groups stringOrArray
|
||||
if err := c.unmarshalClaim(a.groupsClaim, &groups); err != nil {
|
||||
return nil, false, fmt.Errorf("oidc: parse groups claim %q: %v", a.groupsClaim, err)
|
||||
}
|
||||
info.Groups = []string(groups)
|
||||
}
|
||||
}
|
||||
|
||||
if a.groupsPrefix != "" {
|
||||
for i, group := range info.Groups {
|
||||
info.Groups[i] = a.groupsPrefix + group
|
||||
}
|
||||
}
|
||||
|
||||
// check to ensure all required claims are present in the ID token and have matching values.
|
||||
for claim, value := range a.requiredClaims {
|
||||
if !c.hasClaim(claim) {
|
||||
return nil, false, fmt.Errorf("oidc: required claim %s not present in ID token", claim)
|
||||
}
|
||||
|
||||
// NOTE: Only string values are supported as valid required claim values.
|
||||
var claimValue string
|
||||
if err := c.unmarshalClaim(claim, &claimValue); err != nil {
|
||||
return nil, false, fmt.Errorf("oidc: parse claim %s: %v", claim, err)
|
||||
}
|
||||
if claimValue != value {
|
||||
return nil, false, fmt.Errorf("oidc: required claim %s value does not match. Got = %s, want = %s", claim, claimValue, value)
|
||||
}
|
||||
}
|
||||
|
||||
return &authenticator.Response{User: info}, true, nil
|
||||
}
|
||||
|
||||
// getClaimJWT gets a distributed claim JWT from url, using the supplied access
|
||||
// token as bearer token. If the access token is "", the authorization header
|
||||
// will not be set.
|
||||
// TODO: Allow passing in JSON hints to the IDP.
|
||||
func getClaimJWT(client *http.Client, url, accessToken string) (string, error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// TODO: Allow passing request body with configurable information.
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("while calling %v: %v", url, err)
|
||||
}
|
||||
if accessToken != "" {
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", accessToken))
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
response, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Report non-OK status code as an error.
|
||||
if response.StatusCode < http.StatusOK || response.StatusCode > http.StatusIMUsed {
|
||||
return "", fmt.Errorf("error while getting distributed claim JWT: %v", response.Status)
|
||||
}
|
||||
defer response.Body.Close()
|
||||
responseBytes, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not decode distributed claim response")
|
||||
}
|
||||
return string(responseBytes), nil
|
||||
}
|
||||
|
||||
type stringOrArray []string
|
||||
|
||||
func (s *stringOrArray) UnmarshalJSON(b []byte) error {
|
||||
var a []string
|
||||
if err := json.Unmarshal(b, &a); err == nil {
|
||||
*s = a
|
||||
return nil
|
||||
}
|
||||
var str string
|
||||
if err := json.Unmarshal(b, &str); err != nil {
|
||||
return err
|
||||
}
|
||||
*s = []string{str}
|
||||
return nil
|
||||
}
|
||||
|
||||
type claims map[string]json.RawMessage
|
||||
|
||||
func (c claims) unmarshalClaim(name string, v interface{}) error {
|
||||
val, ok := c[name]
|
||||
if !ok {
|
||||
return fmt.Errorf("claim not present")
|
||||
}
|
||||
return json.Unmarshal([]byte(val), v)
|
||||
}
|
||||
|
||||
func (c claims) hasClaim(name string) bool {
|
||||
if _, ok := c[name]; !ok {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +0,0 @@
|
|||
-----BEGIN EC PRIVATE KEY-----
|
||||
MIHcAgEBBEIAvxtZy9aI/lEt6LVLIhVrWLSwmlMFThU/nZUPr88nA5yKzJMyKbW/
|
||||
QA+umam9YtO78WwzriTGC9qwW6+t+liXrcCgBwYFK4EEACOhgYkDgYYABAGzIO9n
|
||||
tdTx6oVg1O59ljYP4FHY9RNUy+wHeXFnB6fo9asGg9jwLMg/iX0F+whFkllQjNLf
|
||||
kKp/9ATWQHrzSbzuqwB9UU5zfQ3ulhMwEBpxbM6aSi1HyYtc5pQn7KB6h1VXiuQK
|
||||
CIj4kVYHClZuKz0om/XAJL4vWVDwJqDBN6m9Yi9ZLQ==
|
||||
-----END EC PRIVATE KEY-----
|
|
@ -1,7 +0,0 @@
|
|||
-----BEGIN EC PRIVATE KEY-----
|
||||
MIHcAgEBBEIBtxiwrrDmi1U+NxClUKIr2cvmL6PPLxjAULjPuORt0AWbqKakphSJ
|
||||
43VmIbPBBCuLnN2PuVS9N8jLDlR1KUnnFSGgBwYFK4EEACOhgYkDgYYABAEgshGY
|
||||
Oflwnz2SQOWIkvSPmijMhS4nWmLYedR2H/Dg9c9nuiyQqL3XpqkPnQQwqOgcXjMT
|
||||
hTec2tiLcRS3Gj02yQEpe/6Do6if4K4cQ9KsNtVHsn0bibsqLtRuvI7xUu9JJAs7
|
||||
vSLNUtmxVzFo4s4spnIjLT71uz1Vag/NrKwot7cz4g==
|
||||
-----END EC PRIVATE KEY-----
|
|
@ -1,7 +0,0 @@
|
|||
-----BEGIN EC PRIVATE KEY-----
|
||||
MIHcAgEBBEIAQTHP4V93rz1w1D+jF1Jvx5QHzkQQIYxbN1LPuvQjEoplQrjZ4Qiu
|
||||
h9mKK6DBCaDlfSos+wnTOlZH1z1tpa9soPOgBwYFK4EEACOhgYkDgYYABAFn+QOY
|
||||
a937Lp+WO1S+zJU9ITnzdvjqQtD/TjtJPQsllV8rD0QNXZb/pLFQFZtDEehiZKEu
|
||||
WA0REGNs+rVMO63YZAAyDMwZTz87ulH23OR6EaoyDp9qEPx7kpxgaJqeIztla2t8
|
||||
SLVpv/FPR92E/OmguT6sFI5mP0AhV8UVlLYuHaovnw==
|
||||
-----END EC PRIVATE KEY-----
|
|
@ -1,27 +0,0 @@
|
|||
#!/usr/bin/env bash -e
|
||||
|
||||
# Copyright 2018 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.
|
||||
|
||||
rm *.pem
|
||||
|
||||
for N in `seq 1 3`; do
|
||||
ssh-keygen -t rsa -b 2048 -f rsa_$N.pem -N ''
|
||||
done
|
||||
|
||||
for N in `seq 1 3`; do
|
||||
ssh-keygen -t ecdsa -b 521 -f ecdsa_$N.pem -N ''
|
||||
done
|
||||
|
||||
rm *.pub
|
|
@ -1,27 +0,0 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEA0pXWMYjWRjBEds/fKj/u9r2E6SIDx0J+TAg+eyVeR20Ky9jZ
|
||||
mIXW5zSxE/EKpNQpiBWm1e6G9kmhMuqjr7g455S7E+3rD3OVkdTT6SU5AKBNSFoR
|
||||
XUd+G/YJEtRzrpEYNtEJHkxUxWuyfCHblHSt+wsrE6t0DccCqC87lKQiGb/QfC8u
|
||||
P6ZS99SCjKBEFp1fZvyNkYwStFc2OH5fBGPXXb6SNsquvDeKX9NeWjXkmxDkbOg2
|
||||
kSkel4s/zw5KwcW3JzERfEcLStrDQ8fRbJ1C3uC088sUk4q4APQmKI/8FTvJe431
|
||||
Vne9sOSptphiqCjlR+Knja58rc/vt4TkSPZf2wIDAQABAoIBAQDO3UgjMtOi8Wlf
|
||||
+YW1IEbjdXrp9XMWu9gLYpHWMPgzXAeeBfCDJv7b8uP8ve2By7TcrMBOKVnE+MF0
|
||||
nhCb3nFv9KftxOsDK70DG7nrrpgXaGFisK+cHU3hs8hoCfF1y6yotKGrdLpVkR0t
|
||||
Wak1ZYU/NlJjqSqBGj0e7/8sXivtc7oME8tBBRBCEa8OqPqaelCInfFF1rX5vmxX
|
||||
pQjPpZoA+vroSJy8SYE0N5oqtGwOPT+9rVuDOL10eaMbGUcssZl8ofwuvzOYPMW4
|
||||
KFSVtvdtKnACq94Qy6XQbK5hZbZXSpzxANKq8SFyG2N1wOlpu/ktdXqkyDs08AZY
|
||||
c/KkpXspAoGBAPdC73GOZn/hxzkwZ2Dl+S9rgrLT3VbeuhMp6GXSdiT+f9babMuw
|
||||
HlYw6uULmvL1gD/0GmyWrHopPFJxodBG5SlwYS5wl49slcxeKCjK26vbNfK2eCbu
|
||||
9uMtED4dN/5NlaXF4hqy/FmSyaFhQT+5hvx8n/zvLsgpuSQ+SCiDAHMfAoGBANoH
|
||||
FCZeCWzzUFhObYG9wxGJ9FBPQa0htafIYEgTwezlKPsrfXfCTnVg1lLkr6Z4IwYQ
|
||||
9VufJZNAc5V0X9H/ceyKJYxhQ+E01NEVzVpoK8fOC4yCYSYtbJnqkOUQzZJzkjFT
|
||||
mNcIa8o4UrBOWzMhMQa0AOZH4VrbtZDCZhid+hfFAoGAAbKh9kOmDIa+WXQtoYqy
|
||||
tVKlqRivUmNhH7SP9fMGAKcGtbD2QkfJTYo0crIrtDNfWBETBV/be1NBKMfC9q0l
|
||||
8azl3e3D/KYgOTEEUZNjAsEUk8AQ/yNw6opqrCKDOenKd0LulIRaGztYyxTh39Ak
|
||||
TyOD7bauuY0fylHrKOwNWr0CgYEAsVZ0o0h1rjKyNUGFfLQWyFtHZ1Mv/lye3tvy
|
||||
xG2dnMxAaxvSr+hR3NNpQH9WB7dL9ZExoNZvv7f6y6OelLaLuXQcWnR6u+E3AOIU
|
||||
5+Y3RgtoBV+/GUh1PzQ1qrviGa77SDfQ54an9hGd4F27fHkQ4XzkBmqM+FQg+J/G
|
||||
X1uPomkCgYBo4ZBEA20Wvf1k2iWOVdfsxZNeOLxwcN5x89dAvm0v6RHg2NMy+XKw
|
||||
Rj+YRuudFdxfg39J/V/Md9qsvjW+4FthD8GhgPs22dksV+7j6ApWkYTmIKG4rmh3
|
||||
RhHOr6uLg9BeShnlvMMaMJKf2eA7SaVtmuS6uBGgEUNaa3qEBq0R+Q==
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -1,27 +0,0 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAxmLv1fqjacxZu5jyfQzytwjdt9/BBDzPM6G+R92YvsB7o2FH
|
||||
ISiySbT8pyj7wGUgDvBdc9+GT3thf2pKGl/THaYJCLbh90GXVMTTak8Najqp2Qod
|
||||
kxIpjSOBgukmd0LUtUVGKJg7YTEqsxAUmn9mfFiUZ6ixHr0U2NAXu7TF4dw7C9bt
|
||||
ORi9DKDfUhEiV3Vcyrc5d/9VdmWG6fs/kq0B/6AboiSIIGAKSugNi4BgV7rNZYwZ
|
||||
37UT72CW28TQQuuAtmTgXWMkfeWZiQKD1P8Nl1byORpgivp2A+2pMNlcLuogVhGY
|
||||
TIZstbeMqoPyLW57GitWDg1tbakwJdCR23gCKQIDAQABAoIBACZmDf//1FNtD01F
|
||||
TGIx+GS/HZMyhvyX/I8E1ny4gpEhVo0IDil35BJqKqD8SMYzjKH3mk8MS8Xknrl3
|
||||
zEIQnB9X/NWn+FLQakcpFba0+GbAVhHBaHoIAOzlm3LIR/67e8peTzcaSBwG1Tn1
|
||||
eddxo1ecGZV6zFWjyX4xwPY/BjIyA/b1LewqIK6W/I4u3RwtEzqANV9ddVbFH53e
|
||||
Y+i2X1HVuLm0LETsX4jB/G/ZDP6Y101gOwPFddm+h1ixZ2jrAkyTbvYL5ukIsU8Z
|
||||
okIEZsd6nv08YN+LOXOPh0CxvgHI347RDzgfbDmHGqq8gh20+wLP/MV+dOiBBAJT
|
||||
RfnoFcUCgYEA8SpMW64CNhRkH3Nv5A5ffSOa7NqiN7OdNEswgcgkAbjR5YsTATPg
|
||||
p9iWqGcEodX1FWjnL2NLMdMvYxh4MwMCACIa8FQ2/RDEwViaVjxcOK64MIvyvnNq
|
||||
NObx8pMClUBXWF/llxxTR+/CJWRdCABBm56lQPuuX/qEi/xqybHPcAcCgYEA0pb9
|
||||
FGmGhDXl3bG3zNNIim+FuqG0xQoIjVPwdvkMI30h/ii6qs3jxwHlw6KBf8AI9wI+
|
||||
bWbzhwcYVkS6It0Yj4q/mqOVHi89frrAQygsJkMQkdl8WiWwPeiiIdsHYTUcBv5+
|
||||
i6YLs8ycnzMeFAxg8kuxrq6mm3yW6u5CuInsEE8CgYAWXqUMj/x2hbevzyZe0hJ7
|
||||
ahURyUnovsljM2JBd44XdsxJbXgK0YQSLZ3z6vJcDJuaK8vd8mjkK0GnAHsNyEak
|
||||
OoWjKzyahrapdI2EWD75pwNAxYpzrgL4+z8QECDaNUik0uhZ9u+mqY+ppkCW4Gc1
|
||||
hyau+2l2T6eB0J0bLloeewKBgQC+fZn8JuBpI6AEg8ew3cYWg37CLZgpTEQkIzO3
|
||||
StyyFXT0RL9l1cwerha6ensNphX16e+yYpgTIlXfY1fERZ776RQcu7Adl7nWsvNL
|
||||
TEFzcuLAK60SlljwB0jxuwDX64SoxviNNewL/iAG2eRxWikvw0y8qHtI1tBlPpTX
|
||||
/NqufQKBgD1jAPRgu+IN9IDXoqnMiXbSfrVsJ1ndyPCRBq4dfvawcbEDTZfkiid0
|
||||
aNrdRoOETfLCynAcG6o6uS3+2sumzXLrMNr8MDF8NEa2Rh8nN3IjZqESV4CNgaV6
|
||||
JhAlWFp+AvYv6N1iHK52nNAFiX/cfaMpWTUKqk2Z4VZCr5qhLUVs
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -1,27 +0,0 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpgIBAAKCAQEA1uO3LV0wHadHjQb8U8KGUpzlZTyBHlinL7lF5yKk2hXOyssT
|
||||
Z0UVF6ofPq/L+ZN5VkTb1FJWMEBiX1BgXlboDdYKAYTl1QaeEQrfrAM4gp2FdWS4
|
||||
fMUuagFdVXs8T2J4GGPEE83ybwz/YEM3p83Qojifvx/IjVtuHCMkUpcj92scjsCY
|
||||
EeSKRjJicVBmJ1RK0ShEorHhnYmokKYsPl41mV5VdmWZOtmPK4jV05cBfg7eC7yI
|
||||
cwwYhowLkYvd9d9H74otK5tD7KTxFG6JJ0N5zwf6XBRcXBWKstfOOZ5Qgo9z0Tkm
|
||||
n3Klp9vwun25aaA1MlSSByByiCb7qvS8jhqMkQIDAQABAoIBAQCjN58AU9GiFFai
|
||||
ZXXuYMgJo6YRON2RoSCLfRv9LTEtfHbvTUPVooEc0lidEoXJcRwuTGr0X/2a9KxD
|
||||
XRd1UGk9aR98e+bd4QLaSvoM+v1HKEIgInqGOnbAiXzM2qe6XD5/t/dMW5cShjrK
|
||||
cQOq7wbS0FN1pbx8sb92m7KREL9+wnXuOCHYtublRf7arsMkaZcpSBBaI+raMaZR
|
||||
dUC+LmalIvR8+dNegducwWsdE8/Vh+xq97ZbNFlyut3JOvfuHmaAOvUsX/4touj2
|
||||
dDkJmvzvmpTBG888t+6hv9eKWaacsTAKuPLThRBD53coTEvHK8iic9fOok65y5Bn
|
||||
nFP/irUpAoGBAPUsPoAPwcNajZX/E4XeG/2eV+IHMxYR9gJwBzpoUfwHr5mR54HK
|
||||
POia/z7/M2KVV9kMWBlbTumIHSEylEEDCCKNNIe1gHBxQ7uGuaf+vVXpVgjBigUz
|
||||
7oiCjb5RdjevfiyudX/z0B9IQSI9djCXebifEHKpUxAOmU3oP0SEMULLAoGBAOBh
|
||||
G+fDouMU7QN93NG0hssu44yc7xQhb7VLB+isrEQASVG2xWkd3iDrdRxwHAHZqxFd
|
||||
4DRzDTFC7yeR+FEbVVkWQRaeDwFJM1zmRTXsYjBkK49GNzB4QEtHvPouuxMAQ4UD
|
||||
zJ9a3LEDSs867R7XEbNF8j9NC9z0vk7w9bHTA1aTAoGBAODUUQBY8sQ1zy8lOf8B
|
||||
/sMmKMti9Mshb2su1sIOFli7p6F5tkZEcnSQZs+bccDO2T92XXfrTsNDigr+egvg
|
||||
Pt6IhQqKPB1hEM7wLmLLbU9Sag4fhXVd+TmAF4HW7EUGjvtkhOXwbQOy2+ANYswO
|
||||
rJXMcGXltwE7kgRqnVI0s4PfAoGBALUrM5Dq0bZwyv6qvYVFMiEUdv6uKAwlA0Fq
|
||||
l7Qy19UANjMYVEUPrK7/7stLahHEYu/e0I0I6HoCBX/5yHoUi9Emut88N/ld1W8J
|
||||
LpDfkFhqSRGiLCWisqcWAWwwFzS8XcgkzS9N+iui8OBqP9NK7CvIKlUaLJ33r0Gm
|
||||
JXuzWVqpAoGBAIQ8+YuvFfyhwXuWwQbzxVpWRURH0FRu8KfFIkFFbdyht6leYXuj
|
||||
uxcrcHWzkEPSLO22BoJX8ECHq4LadAIjkkpr5GCikKCF+r/bq50RnECqvfoJ629J
|
||||
gA87C8cLU3dXmSYd+vSg6icZyncTmXyyEV0dqoUGJ2M33kE6hYAbc/ic
|
||||
-----END RSA PRIVATE KEY-----
|
Loading…
Reference in New Issue