Skip to content

Commit

Permalink
Wire in WebhookTokenAutenticator support
Browse files Browse the repository at this point in the history
Signed-off-by: Simo Sorce <[email protected]>
  • Loading branch information
simo5 committed Mar 8, 2018
1 parent 3addbb0 commit b39e082
Show file tree
Hide file tree
Showing 12 changed files with 397 additions and 1 deletion.
4 changes: 4 additions & 0 deletions pkg/cmd/server/apis/config/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,10 @@ func GetMasterFileReferences(config *MasterConfig) []*string {
refs = append(refs, &config.AuthConfig.RequestHeader.ClientCA)
}

for _, wta := range config.AuthConfig.WebhookTokenAuthenticators {
refs = append(refs, &wta.ConfigFile)
}

refs = append(refs, &config.AggregatorConfig.ProxyClientInfo.CertFile)
refs = append(refs, &config.AggregatorConfig.ProxyClientInfo.KeyFile)

Expand Down
12 changes: 12 additions & 0 deletions pkg/cmd/server/apis/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,8 @@ type MasterConfig struct {
type MasterAuthConfig struct {
// RequestHeader holds options for setting up a front proxy against the the API. It is optional.
RequestHeader *RequestHeaderAuthenticationOptions
// WebhookTokenAuthnConfig, if present configures remote token reviewers
WebhookTokenAuthenticators []WebhookTokenAuthenticator
}

// RequestHeaderAuthenticationOptions provides options for setting up a front proxy against the entire
Expand Down Expand Up @@ -828,6 +830,16 @@ type DNSConfig struct {
AllowRecursiveQueries bool
}

type WebhookTokenAuthenticator struct {
// ConfigFile is a path to a Kubeconfig file with the webhook configuration
ConfigFile string
// CacheTTL indicates how long an authentication result should be cached.
// It takes a valid time duration string (e.g. "5m").
// If empty, you get a default timeout of 10 seconds.
// If zero (e.g. "0m"), caching is disabled
CacheTTL string
}

type OAuthConfig struct {
// MasterCA is the CA for verifying the TLS connection back to the MasterURL.
// "" to use system roots, set to use custom roots, never nil (guaranteed by conversion defaults)
Expand Down
7 changes: 7 additions & 0 deletions pkg/cmd/server/apis/config/v1/conversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,13 @@ func SetDefaults_MasterConfig(obj *MasterConfig) {
obj.AdmissionConfig.PluginConfig[pluginName] = &AdmissionPluginConfig{}
}
}

// Set defaults Cache TTLs for external Webhook Token Reviewers
for _, wta := range obj.AuthConfig.WebhookTokenAuthenticators {
if len(wta.CacheTTL) == 0 {
wta.CacheTTL = "10s"
}
}
}

func SetDefaults_KubernetesMasterConfig(obj *KubernetesMasterConfig) {
Expand Down
1 change: 1 addition & 0 deletions pkg/cmd/server/apis/config/v1/testdata/master-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ auditConfig:
webHookMode: ""
authConfig:
requestHeader: null
webhookTokenAuthenticators: null
controllerConfig:
controllers:
- '*'
Expand Down
14 changes: 14 additions & 0 deletions pkg/cmd/server/apis/config/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,8 @@ type MasterConfig struct {
type MasterAuthConfig struct {
// RequestHeader holds options for setting up a front proxy against the the API. It is optional.
RequestHeader *RequestHeaderAuthenticationOptions `json:"requestHeader"`
// WebhookTokenAuthnConfig, if present configures remote token reviewers
WebhookTokenAuthenticators []WebhookTokenAuthenticator `json:"webhookTokenAuthenticators"`
}

// RequestHeaderAuthenticationOptions provides options for setting up a front proxy against the entire
Expand Down Expand Up @@ -711,6 +713,18 @@ type DNSConfig struct {
AllowRecursiveQueries bool `json:"allowRecursiveQueries"`
}

// WebhookTokenAuthenticators holds the necessary configuation options for
// external token authenticators
type WebhookTokenAuthenticator struct {
// ConfigFile is a path to a Kubeconfig file with the webhook configuration
ConfigFile string `json:"configFile"`
// CacheTTL indicates how long an authentication result should be cached.
// It takes a valid time duration string (e.g. "5m").
// If empty, you get a default timeout of 10 seconds.
// If zero (e.g. "0m"), caching is disabled
CacheTTL string `json:"cacheTTL"`
}

// OAuthConfig holds the necessary configuration options for OAuth authentication
type OAuthConfig struct {
// MasterCA is the CA for verifying the TLS connection back to the MasterURL.
Expand Down
21 changes: 21 additions & 0 deletions pkg/cmd/server/apis/config/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions pkg/cmd/server/apis/config/validation/master.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,24 @@ func ValidateMasterConfig(config *configapi.MasterConfig, fldPath *field.Path) V
func ValidateMasterAuthConfig(config configapi.MasterAuthConfig, fldPath *field.Path) ValidationResults {
validationResults := ValidationResults{}

for _, wta := range config.WebhookTokenAuthenticators {
configFile := fldPath.Child("webhookTokenAuthenticators", "ConfigFile")
if len(wta.ConfigFile) == 0 {
validationResults.AddErrors(field.Required(configFile, ""))
} else {
validationResults.AddErrors(ValidateFile(wta.ConfigFile, configFile)...)
}

cacheTTL := fldPath.Child("webhookTokenAuthenticators", "cacheTTL")
if len(wta.CacheTTL) == 0 {
validationResults.AddErrors(field.Required(cacheTTL, ""))
} else if ttl, err := time.ParseDuration(wta.CacheTTL); err != nil {
validationResults.AddErrors(field.Invalid(cacheTTL, wta.CacheTTL, fmt.Sprintf("%v", err)))
} else if ttl < 0 {
validationResults.AddErrors(field.Invalid(cacheTTL, wta.CacheTTL, "cannot be less than zero"))
}
}

if config.RequestHeader == nil {
return validationResults
}
Expand Down
78 changes: 78 additions & 0 deletions pkg/cmd/server/apis/config/validation/master_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package validation

import (
"io/ioutil"
"os"
"testing"

"k8s.io/apimachinery/pkg/util/diff"
Expand Down Expand Up @@ -525,3 +527,79 @@ func TestValidateIngressIPNetworkCIDR(t *testing.T) {
}
}
}

func TestValidateiMasterAuthConfig(t *testing.T) {
testConfigFile, err := ioutil.TempFile("", "test1.cfg")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
defer os.Remove(testConfigFile.Name())

testCases := []struct {
testName string
errorCount int
RequestHeader *configapi.RequestHeaderAuthenticationOptions
WebhookTokenAuthenticators []configapi.WebhookTokenAuthenticator
}{
{
testName: "No AuthConfig",
},
{
testName: "Valid RequestHeader",
RequestHeader: &configapi.RequestHeaderAuthenticationOptions{
ClientCA: "ClientCA",
ClientCommonNames: []string{"ClientCommonNames", "2", "3"},
UsernameHeaders: []string{"Username", "Headers"},
GroupHeaders: []string{"Group", "Headers"},
ExtraHeaderPrefixes: []string{"Extra", "Header", "Prefixes"},
},
},
{
testName: "Only One WebhookTokenAuthenticator",
WebhookTokenAuthenticators: []configapi.WebhookTokenAuthenticator{
{
ConfigFile: testConfigFile.Name(),
CacheTTL: "2m",
},
},
},
{
testName: "Unexisting config file in second WebhookTokenAuthenticator",
WebhookTokenAuthenticators: []configapi.WebhookTokenAuthenticator{
{
ConfigFile: testConfigFile.Name(),
CacheTTL: "2m",
},
{
ConfigFile: "Unexisting",
CacheTTL: "2m",
},
},
errorCount: 1,
},
{
testName: "Invalid and Empty CacheTTL WebhookTokenAuthenticator",
WebhookTokenAuthenticators: []configapi.WebhookTokenAuthenticator{
{
ConfigFile: testConfigFile.Name(),
CacheTTL: "-2m",
},
{
ConfigFile: testConfigFile.Name(),
},
},
errorCount: 2,
},
}
for _, test := range testCases {
config := configapi.MasterAuthConfig{
RequestHeader: test.RequestHeader,
WebhookTokenAuthenticators: test.WebhookTokenAuthenticators,
}
errors := ValidateMasterAuthConfig(config, nil)
errorCount := len(errors.Errors)
if test.errorCount != errorCount {
t.Errorf("%s: expected %d errors, got %v", test.testName, test.errorCount, errors.Errors)
}
}
}
21 changes: 21 additions & 0 deletions pkg/cmd/server/apis/config/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pkg/cmd/server/kubernetes/master/master_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,7 @@ func BuildKubernetesMasterConfig(

func defaultOpenAPIConfig(config configapi.MasterConfig) *openapicommon.Config {
securityDefinitions := spec.SecurityDefinitions{}
if len(config.ServiceAccountConfig.PublicKeyFiles) > 0 {
if len(config.ServiceAccountConfig.PublicKeyFiles) > 0 || len(config.AuthConfig.WebhookTokenAuthenticators) > 0 {
securityDefinitions["BearerToken"] = &spec.SecurityScheme{
SecuritySchemeProps: spec.SecuritySchemeProps{
Type: "apiKey",
Expand Down
13 changes: 13 additions & 0 deletions pkg/cmd/server/origin/authenticator.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
tokencache "k8s.io/apiserver/pkg/authentication/token/cache"
tokenunion "k8s.io/apiserver/pkg/authentication/token/union"
genericapiserver "k8s.io/apiserver/pkg/server"
webhooktoken "k8s.io/apiserver/plugin/pkg/authenticator/token/webhook"
kclientsetexternal "k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/util/cert"
Expand Down Expand Up @@ -108,6 +109,18 @@ func newAuthenticator(config configapi.MasterConfig, accessTokenGetter oauthclie
group.NewTokenGroupAdder(oauthTokenAuthenticator, []string{bootstrappolicy.AuthenticatedOAuthGroup}))
}

for _, wta := range config.AuthConfig.WebhookTokenAuthenticators {
ttl, err := time.ParseDuration(wta.CacheTTL)
if err != nil {
return nil, nil, fmt.Errorf("Error converting CacheTTL='%s' to duration", wta.CacheTTL)
}
webhookTokenAuthenticator, err := webhooktoken.New(wta.ConfigFile, ttl)
if err != nil {
return nil, nil, fmt.Errorf("Failed to create webhook token authenticator for ConfigFile='%s'", wta.ConfigFile)
}
tokenAuthenticators = append(tokenAuthenticators, webhookTokenAuthenticator)
}

if len(tokenAuthenticators) > 0 {
// Combine all token authenticators
tokenAuth := tokenunion.New(tokenAuthenticators...)
Expand Down
Loading

0 comments on commit b39e082

Please sign in to comment.