add ability to authenticators for dynamic update of certs
Kubernetes-commit: 51195dd86012c4c4b17a1707ef50a46fa046f74f
This commit is contained in:
parent
32db273e32
commit
eee025a27a
|
@ -18,7 +18,6 @@ package authenticatorfactory
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-openapi/spec"
|
"github.com/go-openapi/spec"
|
||||||
|
@ -31,10 +30,10 @@ import (
|
||||||
unionauth "k8s.io/apiserver/pkg/authentication/request/union"
|
unionauth "k8s.io/apiserver/pkg/authentication/request/union"
|
||||||
"k8s.io/apiserver/pkg/authentication/request/websocket"
|
"k8s.io/apiserver/pkg/authentication/request/websocket"
|
||||||
"k8s.io/apiserver/pkg/authentication/request/x509"
|
"k8s.io/apiserver/pkg/authentication/request/x509"
|
||||||
|
x509request "k8s.io/apiserver/pkg/authentication/request/x509"
|
||||||
"k8s.io/apiserver/pkg/authentication/token/cache"
|
"k8s.io/apiserver/pkg/authentication/token/cache"
|
||||||
webhooktoken "k8s.io/apiserver/plugin/pkg/authenticator/token/webhook"
|
webhooktoken "k8s.io/apiserver/plugin/pkg/authenticator/token/webhook"
|
||||||
authenticationclient "k8s.io/client-go/kubernetes/typed/authentication/v1beta1"
|
authenticationclient "k8s.io/client-go/kubernetes/typed/authentication/v1beta1"
|
||||||
"k8s.io/client-go/util/cert"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// DelegatingAuthenticatorConfig is the minimal configuration needed to create an authenticator
|
// DelegatingAuthenticatorConfig is the minimal configuration needed to create an authenticator
|
||||||
|
@ -48,8 +47,9 @@ type DelegatingAuthenticatorConfig struct {
|
||||||
// CacheTTL is the length of time that a token authentication answer will be cached.
|
// CacheTTL is the length of time that a token authentication answer will be cached.
|
||||||
CacheTTL time.Duration
|
CacheTTL time.Duration
|
||||||
|
|
||||||
// ClientCAFile is the CA bundle file used to authenticate client certificates
|
// ClientVerifyOptionFn are the options for verifying incoming connections using mTLS and directly assigning to users.
|
||||||
ClientCAFile string
|
// Generally this is the CA bundle file used to authenticate client certificates
|
||||||
|
ClientVerifyOptionFn x509request.VerifyOptionFunc
|
||||||
|
|
||||||
APIAudiences authenticator.Audiences
|
APIAudiences authenticator.Audiences
|
||||||
|
|
||||||
|
@ -63,8 +63,8 @@ func (c DelegatingAuthenticatorConfig) New() (authenticator.Request, *spec.Secur
|
||||||
// front-proxy first, then remote
|
// front-proxy first, then remote
|
||||||
// Add the front proxy authenticator if requested
|
// Add the front proxy authenticator if requested
|
||||||
if c.RequestHeaderConfig != nil {
|
if c.RequestHeaderConfig != nil {
|
||||||
requestHeaderAuthenticator, err := headerrequest.NewSecure(
|
requestHeaderAuthenticator, err := headerrequest.NewDynamicVerifyOptionsSecure(
|
||||||
c.RequestHeaderConfig.ClientCA,
|
c.RequestHeaderConfig.VerifyOptionFn,
|
||||||
c.RequestHeaderConfig.AllowedClientNames,
|
c.RequestHeaderConfig.AllowedClientNames,
|
||||||
c.RequestHeaderConfig.UsernameHeaders,
|
c.RequestHeaderConfig.UsernameHeaders,
|
||||||
c.RequestHeaderConfig.GroupHeaders,
|
c.RequestHeaderConfig.GroupHeaders,
|
||||||
|
@ -77,14 +77,8 @@ func (c DelegatingAuthenticatorConfig) New() (authenticator.Request, *spec.Secur
|
||||||
}
|
}
|
||||||
|
|
||||||
// x509 client cert auth
|
// x509 client cert auth
|
||||||
if len(c.ClientCAFile) > 0 {
|
if c.ClientVerifyOptionFn != nil {
|
||||||
clientCAs, err := cert.NewPool(c.ClientCAFile)
|
authenticators = append(authenticators, x509.NewDynamic(c.ClientVerifyOptionFn, x509.CommonNameUserConversion))
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("unable to load client CA file %s: %v", c.ClientCAFile, err)
|
|
||||||
}
|
|
||||||
verifyOpts := x509.DefaultVerifyOptions()
|
|
||||||
verifyOpts.Roots = clientCAs
|
|
||||||
authenticators = append(authenticators, x509.New(verifyOpts, x509.CommonNameUserConversion))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.TokenAccessReviewClient != nil {
|
if c.TokenAccessReviewClient != nil {
|
||||||
|
|
|
@ -16,6 +16,10 @@ limitations under the License.
|
||||||
|
|
||||||
package authenticatorfactory
|
package authenticatorfactory
|
||||||
|
|
||||||
|
import (
|
||||||
|
x509request "k8s.io/apiserver/pkg/authentication/request/x509"
|
||||||
|
)
|
||||||
|
|
||||||
type RequestHeaderConfig struct {
|
type RequestHeaderConfig struct {
|
||||||
// UsernameHeaders are the headers to check (in order, case-insensitively) for an identity. The first header with a value wins.
|
// UsernameHeaders are the headers to check (in order, case-insensitively) for an identity. The first header with a value wins.
|
||||||
UsernameHeaders []string
|
UsernameHeaders []string
|
||||||
|
@ -24,8 +28,9 @@ type RequestHeaderConfig struct {
|
||||||
// ExtraHeaderPrefixes are the head prefixes to check (case-insentively) for filling in
|
// ExtraHeaderPrefixes are the head prefixes to check (case-insentively) for filling in
|
||||||
// the user.Info.Extra. All values of all matching headers will be added.
|
// the user.Info.Extra. All values of all matching headers will be added.
|
||||||
ExtraHeaderPrefixes []string
|
ExtraHeaderPrefixes []string
|
||||||
// ClientCA points to CA bundle file which is used verify the identity of the front proxy
|
// VerifyOptionFn are the options for verifying incoming connections using mTLS. Generally this points to CA bundle file which is used verify the identity of the front proxy.
|
||||||
ClientCA string
|
// It may produce different options at will.
|
||||||
|
VerifyOptionFn x509request.VerifyOptionFunc
|
||||||
// AllowedClientNames is a list of common names that may be presented by the authenticating front proxy. Empty means: accept any.
|
// AllowedClientNames is a list of common names that may be presented by the authenticating front proxy. Empty means: accept any.
|
||||||
AllowedClientNames []string
|
AllowedClientNames []string
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,11 +78,6 @@ func trimHeaders(headerNames ...string) ([]string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSecure(clientCA string, proxyClientNames []string, nameHeaders []string, groupHeaders []string, extraHeaderPrefixes []string) (authenticator.Request, error) {
|
func NewSecure(clientCA string, proxyClientNames []string, nameHeaders []string, groupHeaders []string, extraHeaderPrefixes []string) (authenticator.Request, error) {
|
||||||
headerAuthenticator, err := New(nameHeaders, groupHeaders, extraHeaderPrefixes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(clientCA) == 0 {
|
if len(clientCA) == 0 {
|
||||||
return nil, fmt.Errorf("missing clientCA file")
|
return nil, fmt.Errorf("missing clientCA file")
|
||||||
}
|
}
|
||||||
|
@ -102,7 +97,17 @@ func NewSecure(clientCA string, proxyClientNames []string, nameHeaders []string,
|
||||||
opts.Roots.AddCert(cert)
|
opts.Roots.AddCert(cert)
|
||||||
}
|
}
|
||||||
|
|
||||||
return x509request.NewVerifier(opts, headerAuthenticator, sets.NewString(proxyClientNames...)), nil
|
return NewDynamicVerifyOptionsSecure(x509request.StaticVerifierFn(opts), proxyClientNames, nameHeaders, groupHeaders, extraHeaderPrefixes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO make the string slices dynamic too.
|
||||||
|
func NewDynamicVerifyOptionsSecure(verifyOptionFn x509request.VerifyOptionFunc, proxyClientNames []string, nameHeaders []string, groupHeaders []string, extraHeaderPrefixes []string) (authenticator.Request, error) {
|
||||||
|
headerAuthenticator, err := New(nameHeaders, groupHeaders, extraHeaderPrefixes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return x509request.NewDynamicCAVerifier(verifyOptionFn, headerAuthenticator, sets.NewString(proxyClientNames...)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *requestHeaderAuthRequestHandler) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) {
|
func (a *requestHeaderAuthRequestHandler) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) {
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
Copyright 2019 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 x509
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"k8s.io/client-go/util/cert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StaticVerifierFn is a VerifyOptionFunc that always returns the same value. This allows verify options that cannot change.
|
||||||
|
func StaticVerifierFn(opts x509.VerifyOptions) VerifyOptionFunc {
|
||||||
|
return func() x509.VerifyOptions {
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStaticVerifierFromFile creates a new verification func from a file. It reads the content and then fails.
|
||||||
|
// It will return a nil function if you pass an empty CA file.
|
||||||
|
func NewStaticVerifierFromFile(clientCA string) (VerifyOptionFunc, error) {
|
||||||
|
if len(clientCA) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap with an x509 verifier
|
||||||
|
var err error
|
||||||
|
opts := DefaultVerifyOptions()
|
||||||
|
opts.Roots, err = cert.NewPool(clientCA)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error loading certs from %s: %v", clientCA, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return StaticVerifierFn(opts), nil
|
||||||
|
}
|
|
@ -82,16 +82,26 @@ func (f UserConversionFunc) User(chain []*x509.Certificate) (*authenticator.Resp
|
||||||
return f(chain)
|
return f(chain)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VerifyOptionFunc is function which provides a shallow copy of the VerifyOptions to the authenticator. This allows
|
||||||
|
// for cases where the options (particularly the CAs) can change.
|
||||||
|
type VerifyOptionFunc func() x509.VerifyOptions
|
||||||
|
|
||||||
// Authenticator implements request.Authenticator by extracting user info from verified client certificates
|
// Authenticator implements request.Authenticator by extracting user info from verified client certificates
|
||||||
type Authenticator struct {
|
type Authenticator struct {
|
||||||
opts x509.VerifyOptions
|
verifyOptionsFn VerifyOptionFunc
|
||||||
user UserConversion
|
user UserConversion
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a request.Authenticator that verifies client certificates using the provided
|
// New returns a request.Authenticator that verifies client certificates using the provided
|
||||||
// VerifyOptions, and converts valid certificate chains into user.Info using the provided UserConversion
|
// VerifyOptions, and converts valid certificate chains into user.Info using the provided UserConversion
|
||||||
func New(opts x509.VerifyOptions, user UserConversion) *Authenticator {
|
func New(opts x509.VerifyOptions, user UserConversion) *Authenticator {
|
||||||
return &Authenticator{opts, user}
|
return NewDynamic(StaticVerifierFn(opts), user)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDynamic returns a request.Authenticator that verifies client certificates using the provided
|
||||||
|
// VerifyOptionFunc (which may be dynamic), and converts valid certificate chains into user.Info using the provided UserConversion
|
||||||
|
func NewDynamic(verifyOptionsFn VerifyOptionFunc, user UserConversion) *Authenticator {
|
||||||
|
return &Authenticator{verifyOptionsFn, user}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthenticateRequest authenticates the request using presented client certificates
|
// AuthenticateRequest authenticates the request using presented client certificates
|
||||||
|
@ -101,7 +111,7 @@ func (a *Authenticator) AuthenticateRequest(req *http.Request) (*authenticator.R
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use intermediates, if provided
|
// Use intermediates, if provided
|
||||||
optsCopy := a.opts
|
optsCopy := a.verifyOptionsFn()
|
||||||
if optsCopy.Intermediates == nil && len(req.TLS.PeerCertificates) > 1 {
|
if optsCopy.Intermediates == nil && len(req.TLS.PeerCertificates) > 1 {
|
||||||
optsCopy.Intermediates = x509.NewCertPool()
|
optsCopy.Intermediates = x509.NewCertPool()
|
||||||
for _, intermediate := range req.TLS.PeerCertificates[1:] {
|
for _, intermediate := range req.TLS.PeerCertificates[1:] {
|
||||||
|
@ -133,8 +143,8 @@ func (a *Authenticator) AuthenticateRequest(req *http.Request) (*authenticator.R
|
||||||
|
|
||||||
// Verifier implements request.Authenticator by verifying a client cert on the request, then delegating to the wrapped auth
|
// Verifier implements request.Authenticator by verifying a client cert on the request, then delegating to the wrapped auth
|
||||||
type Verifier struct {
|
type Verifier struct {
|
||||||
opts x509.VerifyOptions
|
verifyOptionsFn VerifyOptionFunc
|
||||||
auth authenticator.Request
|
auth authenticator.Request
|
||||||
|
|
||||||
// allowedCommonNames contains the common names which a verified certificate is allowed to have.
|
// allowedCommonNames contains the common names which a verified certificate is allowed to have.
|
||||||
// If empty, all verified certificates are allowed.
|
// If empty, all verified certificates are allowed.
|
||||||
|
@ -143,7 +153,13 @@ type Verifier struct {
|
||||||
|
|
||||||
// NewVerifier create a request.Authenticator by verifying a client cert on the request, then delegating to the wrapped auth
|
// NewVerifier create a request.Authenticator by verifying a client cert on the request, then delegating to the wrapped auth
|
||||||
func NewVerifier(opts x509.VerifyOptions, auth authenticator.Request, allowedCommonNames sets.String) authenticator.Request {
|
func NewVerifier(opts x509.VerifyOptions, auth authenticator.Request, allowedCommonNames sets.String) authenticator.Request {
|
||||||
return &Verifier{opts, auth, allowedCommonNames}
|
return NewDynamicCAVerifier(StaticVerifierFn(opts), auth, allowedCommonNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDynamicCAVerifier create a request.Authenticator by verifying a client cert on the request, then delegating to the wrapped auth
|
||||||
|
// TODO make the allowedCommonNames dynamic
|
||||||
|
func NewDynamicCAVerifier(verifyOptionsFn VerifyOptionFunc, auth authenticator.Request, allowedCommonNames sets.String) authenticator.Request {
|
||||||
|
return &Verifier{verifyOptionsFn, auth, allowedCommonNames}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthenticateRequest verifies the presented client certificate, then delegates to the wrapped auth
|
// AuthenticateRequest verifies the presented client certificate, then delegates to the wrapped auth
|
||||||
|
@ -153,7 +169,7 @@ func (a *Verifier) AuthenticateRequest(req *http.Request) (*authenticator.Respon
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use intermediates, if provided
|
// Use intermediates, if provided
|
||||||
optsCopy := a.opts
|
optsCopy := a.verifyOptionsFn()
|
||||||
if optsCopy.Intermediates == nil && len(req.TLS.PeerCertificates) > 1 {
|
if optsCopy.Intermediates == nil && len(req.TLS.PeerCertificates) > 1 {
|
||||||
optsCopy.Intermediates = x509.NewCertPool()
|
optsCopy.Intermediates = x509.NewCertPool()
|
||||||
for _, intermediate := range req.TLS.PeerCertificates[1:] {
|
for _, intermediate := range req.TLS.PeerCertificates[1:] {
|
||||||
|
|
|
@ -657,6 +657,7 @@ func TestX509(t *testing.T) {
|
||||||
req.TLS = &tls.ConnectionState{PeerCertificates: testCase.Certs}
|
req.TLS = &tls.ConnectionState{PeerCertificates: testCase.Certs}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this effectively tests the simple dynamic verify function.
|
||||||
a := New(testCase.Opts, testCase.User)
|
a := New(testCase.Opts, testCase.User)
|
||||||
|
|
||||||
resp, ok, err := a.AuthenticateRequest(req)
|
resp, ok, err := a.AuthenticateRequest(req)
|
||||||
|
|
|
@ -23,16 +23,18 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
"k8s.io/klog"
|
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apiserver/pkg/authentication/authenticatorfactory"
|
"k8s.io/apiserver/pkg/authentication/authenticatorfactory"
|
||||||
|
"k8s.io/apiserver/pkg/authentication/request/x509"
|
||||||
"k8s.io/apiserver/pkg/server"
|
"k8s.io/apiserver/pkg/server"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
|
"k8s.io/client-go/util/cert"
|
||||||
|
"k8s.io/klog"
|
||||||
openapicommon "k8s.io/kube-openapi/pkg/common"
|
openapicommon "k8s.io/kube-openapi/pkg/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -74,23 +76,48 @@ func (s *RequestHeaderAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
|
||||||
|
|
||||||
// ToAuthenticationRequestHeaderConfig returns a RequestHeaderConfig config object for these options
|
// ToAuthenticationRequestHeaderConfig returns a RequestHeaderConfig config object for these options
|
||||||
// if necessary, nil otherwise.
|
// if necessary, nil otherwise.
|
||||||
func (s *RequestHeaderAuthenticationOptions) ToAuthenticationRequestHeaderConfig() *authenticatorfactory.RequestHeaderConfig {
|
func (s *RequestHeaderAuthenticationOptions) ToAuthenticationRequestHeaderConfig() (*authenticatorfactory.RequestHeaderConfig, error) {
|
||||||
if len(s.ClientCAFile) == 0 {
|
if len(s.ClientCAFile) == 0 {
|
||||||
return nil
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyFn, err := x509.NewStaticVerifierFromFile(s.ClientCAFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &authenticatorfactory.RequestHeaderConfig{
|
return &authenticatorfactory.RequestHeaderConfig{
|
||||||
UsernameHeaders: s.UsernameHeaders,
|
UsernameHeaders: s.UsernameHeaders,
|
||||||
GroupHeaders: s.GroupHeaders,
|
GroupHeaders: s.GroupHeaders,
|
||||||
ExtraHeaderPrefixes: s.ExtraHeaderPrefixes,
|
ExtraHeaderPrefixes: s.ExtraHeaderPrefixes,
|
||||||
ClientCA: s.ClientCAFile,
|
VerifyOptionFn: verifyFn,
|
||||||
AllowedClientNames: s.AllowedNames,
|
AllowedClientNames: s.AllowedNames,
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClientCertAuthenticationOptions provides different options for client cert auth. You should use `GetClientVerifyOptionFn` to
|
||||||
|
// get the verify options for your authenticator.
|
||||||
type ClientCertAuthenticationOptions struct {
|
type ClientCertAuthenticationOptions struct {
|
||||||
// ClientCA is the certificate bundle for all the signers that you'll recognize for incoming client certificates
|
// ClientCA is the certificate bundle for all the signers that you'll recognize for incoming client certificates
|
||||||
ClientCA string
|
ClientCA string
|
||||||
|
|
||||||
|
// ClientVerifyOptionFn are the options for verifying incoming connections using mTLS and directly assigning to users.
|
||||||
|
// Generally this is the CA bundle file used to authenticate client certificates
|
||||||
|
// If non-nil, this takes priority over the ClientCA file.
|
||||||
|
ClientVerifyOptionFn x509.VerifyOptionFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetClientVerifyOptionFn provides verify options for your authenticator while respecting the preferred order of verifiers.
|
||||||
|
func (s *ClientCertAuthenticationOptions) GetClientVerifyOptionFn() (x509.VerifyOptionFunc, error) {
|
||||||
|
if s.ClientVerifyOptionFn != nil {
|
||||||
|
return s.ClientVerifyOptionFn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(s.ClientCA) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return x509.NewStaticVerifierFromFile(s.ClientCA)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ClientCertAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
|
func (s *ClientCertAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
|
||||||
|
@ -206,12 +233,18 @@ func (s *DelegatingAuthenticationOptions) ApplyTo(c *server.AuthenticationInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
// configure AuthenticationInfo config
|
// configure AuthenticationInfo config
|
||||||
cfg.ClientCAFile = s.ClientCert.ClientCA
|
cfg.ClientVerifyOptionFn, err = s.ClientCert.GetClientVerifyOptionFn()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to load client CA file: %v", err)
|
||||||
|
}
|
||||||
if err = c.ApplyClientCert(s.ClientCert.ClientCA, servingInfo); err != nil {
|
if err = c.ApplyClientCert(s.ClientCert.ClientCA, servingInfo); err != nil {
|
||||||
return fmt.Errorf("unable to load client CA file: %v", err)
|
return fmt.Errorf("unable to load client CA file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg.RequestHeaderConfig = s.RequestHeader.ToAuthenticationRequestHeaderConfig()
|
cfg.RequestHeaderConfig, err = s.RequestHeader.ToAuthenticationRequestHeaderConfig()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create request header authentication config: %v", err)
|
||||||
|
}
|
||||||
if err = c.ApplyClientCert(s.RequestHeader.ClientCAFile, servingInfo); err != nil {
|
if err = c.ApplyClientCert(s.RequestHeader.ClientCAFile, servingInfo); err != nil {
|
||||||
return fmt.Errorf("unable to load client CA file: %v", err)
|
return fmt.Errorf("unable to load client CA file: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -307,6 +340,16 @@ func inClusterClientCA(authConfigMap *v1.ConfigMap) (*ClientCertAuthenticationOp
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clientCAs, err := cert.NewPoolFromBytes([]byte(clientCA))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to load client CA from configmap: %v", err)
|
||||||
|
}
|
||||||
|
verifyOpts := x509.DefaultVerifyOptions()
|
||||||
|
verifyOpts.Roots = clientCAs
|
||||||
|
|
||||||
|
// we still need to write out the client-ca-file for now because it is used to plumb the options through the apiserver's
|
||||||
|
// configuration to hint clients.
|
||||||
|
// TODO deads2k this should eventually be made dynamic along with the authenticator. I'm just wiring them one at at time.
|
||||||
f, err := ioutil.TempFile("", "client-ca-file")
|
f, err := ioutil.TempFile("", "client-ca-file")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -314,7 +357,11 @@ func inClusterClientCA(authConfigMap *v1.ConfigMap) (*ClientCertAuthenticationOp
|
||||||
if err := ioutil.WriteFile(f.Name(), []byte(clientCA), 0600); err != nil {
|
if err := ioutil.WriteFile(f.Name(), []byte(clientCA), 0600); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &ClientCertAuthenticationOptions{ClientCA: f.Name()}, nil
|
|
||||||
|
return &ClientCertAuthenticationOptions{
|
||||||
|
ClientCA: f.Name(),
|
||||||
|
ClientVerifyOptionFn: x509.StaticVerifierFn(verifyOpts),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func inClusterRequestHeader(authConfigMap *v1.ConfigMap) (*RequestHeaderAuthenticationOptions, error) {
|
func inClusterRequestHeader(authConfigMap *v1.ConfigMap) (*RequestHeaderAuthenticationOptions, error) {
|
||||||
|
|
|
@ -46,7 +46,7 @@ func TestToAuthenticationRequestHeaderConfig(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "test when ClientCAFile is not nil",
|
name: "test when ClientCAFile is not nil",
|
||||||
testOptions: &RequestHeaderAuthenticationOptions{
|
testOptions: &RequestHeaderAuthenticationOptions{
|
||||||
ClientCAFile: "/testClientCAFile",
|
ClientCAFile: "testdata/root.pem",
|
||||||
UsernameHeaders: []string{"x-remote-user"},
|
UsernameHeaders: []string{"x-remote-user"},
|
||||||
GroupHeaders: []string{"x-remote-group"},
|
GroupHeaders: []string{"x-remote-group"},
|
||||||
ExtraHeaderPrefixes: []string{"x-remote-extra-"},
|
ExtraHeaderPrefixes: []string{"x-remote-extra-"},
|
||||||
|
@ -56,7 +56,7 @@ func TestToAuthenticationRequestHeaderConfig(t *testing.T) {
|
||||||
UsernameHeaders: []string{"x-remote-user"},
|
UsernameHeaders: []string{"x-remote-user"},
|
||||||
GroupHeaders: []string{"x-remote-group"},
|
GroupHeaders: []string{"x-remote-group"},
|
||||||
ExtraHeaderPrefixes: []string{"x-remote-extra-"},
|
ExtraHeaderPrefixes: []string{"x-remote-extra-"},
|
||||||
ClientCA: "/testClientCAFile",
|
VerifyOptionFn: nil, // this is nil because you can't compare functions
|
||||||
AllowedClientNames: []string{"kube-aggregator"},
|
AllowedClientNames: []string{"kube-aggregator"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -64,7 +64,17 @@ func TestToAuthenticationRequestHeaderConfig(t *testing.T) {
|
||||||
|
|
||||||
for _, testcase := range testCases {
|
for _, testcase := range testCases {
|
||||||
t.Run(testcase.name, func(t *testing.T) {
|
t.Run(testcase.name, func(t *testing.T) {
|
||||||
resultConfig := testcase.testOptions.ToAuthenticationRequestHeaderConfig()
|
resultConfig, err := testcase.testOptions.ToAuthenticationRequestHeaderConfig()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if resultConfig != nil {
|
||||||
|
if resultConfig.VerifyOptionFn == nil {
|
||||||
|
t.Error("missing requestheader verify")
|
||||||
|
}
|
||||||
|
resultConfig.VerifyOptionFn = nil
|
||||||
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(resultConfig, testcase.expectConfig) {
|
if !reflect.DeepEqual(resultConfig, testcase.expectConfig) {
|
||||||
t.Errorf("got RequestHeaderConfig: %#v, expected RequestHeaderConfig: %#v", resultConfig, testcase.expectConfig)
|
t.Errorf("got RequestHeaderConfig: %#v, expected RequestHeaderConfig: %#v", resultConfig, testcase.expectConfig)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBpTCCAUugAwIBAgIUPV4LAC5KK8YWY1FegyTuhkGUr3EwCgYIKoZIzj0EAwIw
|
||||||
|
GjEYMBYGA1UEAxMPSW50ZXJtZWRpYXRlLUNBMB4XDTkwMTIzMTIzNTkwMFoXDTkw
|
||||||
|
MTIzMTIzNTkwMFowFDESMBAGA1UEAxMJTXkgQ2xpZW50MFkwEwYHKoZIzj0CAQYI
|
||||||
|
KoZIzj0DAQcDQgAEyYUnseNUN87rfHgekrfZu5sj4wlt5LYr3JYZZkfSbsb+BW3/
|
||||||
|
RzX02ifjp+8w7mI4qUGg6y6J7oXHGFT3uj9kj6N1MHMwDgYDVR0PAQH/BAQDAgWg
|
||||||
|
MBMGA1UdJQQMMAoGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFKsX
|
||||||
|
EnXwDg8j2LIEM1QzmFrE6537MB8GA1UdIwQYMBaAFF+p0JcY31pz+mjNZnjv0Gum
|
||||||
|
92vZMAoGCCqGSM49BAMCA0gAMEUCIG4FBcb57oqOCoaFiJ+Yx6S0zkaash7bTv3V
|
||||||
|
CIy9JvFdAiEAy8bf2S9EkvZyURZ6ycgEMnekll57Ebze6rjlPx8+B1Y=
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,11 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBqDCCAU2gAwIBAgIUfbqeieihh/oERbfvRm38XvS/xHAwCgYIKoZIzj0EAwIw
|
||||||
|
GjEYMBYGA1UEAxMPSW50ZXJtZWRpYXRlLUNBMCAXDTE2MTAxMTA1MDYwMFoYDzIx
|
||||||
|
MTYwOTE3MDUwNjAwWjAUMRIwEAYDVQQDEwlNeSBDbGllbnQwWTATBgcqhkjOPQIB
|
||||||
|
BggqhkjOPQMBBwNCAARv6N4R/sjMR65iMFGNLN1GC/vd7WhDW6J4X/iAjkRLLnNb
|
||||||
|
KbRG/AtOUZ+7upJ3BWIRKYbOabbQGQe2BbKFiap4o3UwczAOBgNVHQ8BAf8EBAMC
|
||||||
|
BaAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU
|
||||||
|
K/pZOWpNcYai6eHFpmJEeFpeQlEwHwYDVR0jBBgwFoAUX6nQlxjfWnP6aM1meO/Q
|
||||||
|
a6b3a9kwCgYIKoZIzj0EAwIDSQAwRgIhAIWTKw/sjJITqeuNzJDAKU4xo1zL+xJ5
|
||||||
|
MnVCuBwfwDXCAiEAw/1TA+CjPq9JC5ek1ifR0FybTURjeQqYkKpve1dveps=
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"signing": {
|
||||||
|
"profiles": {
|
||||||
|
"valid": {
|
||||||
|
"expiry": "876000h",
|
||||||
|
"usages": [
|
||||||
|
"signing",
|
||||||
|
"key encipherment",
|
||||||
|
"client auth"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"expired": {
|
||||||
|
"expiry": "1h",
|
||||||
|
"not_before": "1990-12-31T23:59:00Z",
|
||||||
|
"not_after": "1990-12-31T23:59:00Z",
|
||||||
|
"usages": [
|
||||||
|
"signing",
|
||||||
|
"key encipherment",
|
||||||
|
"client auth"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"CN": "My Client"
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
cfssl gencert -initca root.csr.json | cfssljson -bare root
|
||||||
|
|
||||||
|
cfssl gencert -initca intermediate.csr.json | cfssljson -bare intermediate
|
||||||
|
cfssl sign -ca root.pem -ca-key root-key.pem -config intermediate.config.json intermediate.csr | cfssljson -bare intermediate
|
||||||
|
|
||||||
|
cfssl gencert -ca intermediate.pem -ca-key intermediate-key.pem -config client.config.json --profile=valid client.csr.json | cfssljson -bare client-valid
|
||||||
|
cfssl gencert -ca intermediate.pem -ca-key intermediate-key.pem -config client.config.json --profile=expired client.csr.json | cfssljson -bare client-expired
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"signing": {
|
||||||
|
"default": {
|
||||||
|
"usages": [
|
||||||
|
"digital signature",
|
||||||
|
"cert sign",
|
||||||
|
"crl sign",
|
||||||
|
"signing",
|
||||||
|
"key encipherment",
|
||||||
|
"client auth"
|
||||||
|
],
|
||||||
|
"expiry": "876000h",
|
||||||
|
"ca_constraint": {
|
||||||
|
"is_ca": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"CN": "Intermediate-CA",
|
||||||
|
"ca": {
|
||||||
|
"expiry": "876000h"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBqDCCAU6gAwIBAgIUfqZtjoFgczZ+oQZbEC/BDSS2J6wwCgYIKoZIzj0EAwIw
|
||||||
|
EjEQMA4GA1UEAxMHUm9vdC1DQTAgFw0xNjEwMTEwNTA2MDBaGA8yMTE2MDkxNzA1
|
||||||
|
MDYwMFowGjEYMBYGA1UEAxMPSW50ZXJtZWRpYXRlLUNBMFkwEwYHKoZIzj0CAQYI
|
||||||
|
KoZIzj0DAQcDQgAEyWHEMMCctJg8Xa5YWLqaCPbk3MjB+uvXac42JM9pj4k9jedD
|
||||||
|
kpUJRkWIPzgJI8Zk/3cSzluUTixP6JBSDKtwwaN4MHYwDgYDVR0PAQH/BAQDAgGm
|
||||||
|
MBMGA1UdJQQMMAoGCCsGAQUFBwMCMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
|
||||||
|
FF+p0JcY31pz+mjNZnjv0Gum92vZMB8GA1UdIwQYMBaAFB7P6+i4/pfNjqZgJv/b
|
||||||
|
dgA7Fe4tMAoGCCqGSM49BAMCA0gAMEUCIQCTT1YWQZaAqfQ2oBxzOkJE2BqLFxhz
|
||||||
|
3smQlrZ5gCHddwIgcvT7puhYOzAgcvMn9+SZ1JOyZ7edODjshCVCRnuHK2c=
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"CN": "Root-CA",
|
||||||
|
"ca": {
|
||||||
|
"expiry": "876000h"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBizCCATGgAwIBAgIUH4plk9qwD61FVXgiOTngFU5FeSkwCgYIKoZIzj0EAwIw
|
||||||
|
EjEQMA4GA1UEAxMHUm9vdC1DQTAgFw0xNjEwMTEwNTA2MDBaGA8yMTE2MDkxNzA1
|
||||||
|
MDYwMFowEjEQMA4GA1UEAxMHUm9vdC1DQTBZMBMGByqGSM49AgEGCCqGSM49AwEH
|
||||||
|
A0IABI2CsrAnMGT8P2VGU2MLo5pv86Z74kcV9hgkLJUkSaeNyc1s89w7X5V2wvwu
|
||||||
|
iWEJRGm5RoZJausmyZLZEoKEVXejYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB
|
||||||
|
Af8EBTADAQH/MB0GA1UdDgQWBBQez+vouP6XzY6mYCb/23YAOxXuLTAfBgNVHSME
|
||||||
|
GDAWgBQez+vouP6XzY6mYCb/23YAOxXuLTAKBggqhkjOPQQDAgNIADBFAiBGclts
|
||||||
|
vJRM+QMVoV/1L9b+hvhgLIp/OupUFsSOReefIwIhALY06hBklyh8eFwuBtyX2VcE
|
||||||
|
8xlVn4/5idUvc3Xv2h9s
|
||||||
|
-----END CERTIFICATE-----
|
Loading…
Reference in New Issue