Skip to content

Commit b39e082

Browse files
committed
Wire in WebhookTokenAutenticator support
Signed-off-by: Simo Sorce <[email protected]>
1 parent 3addbb0 commit b39e082

File tree

12 files changed

+397
-1
lines changed

12 files changed

+397
-1
lines changed

pkg/cmd/server/apis/config/helpers.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,10 @@ func GetMasterFileReferences(config *MasterConfig) []*string {
238238
refs = append(refs, &config.AuthConfig.RequestHeader.ClientCA)
239239
}
240240

241+
for _, wta := range config.AuthConfig.WebhookTokenAuthenticators {
242+
refs = append(refs, &wta.ConfigFile)
243+
}
244+
241245
refs = append(refs, &config.AggregatorConfig.ProxyClientInfo.CertFile)
242246
refs = append(refs, &config.AggregatorConfig.ProxyClientInfo.KeyFile)
243247

pkg/cmd/server/apis/config/types.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,8 @@ type MasterConfig struct {
446446
type MasterAuthConfig struct {
447447
// RequestHeader holds options for setting up a front proxy against the the API. It is optional.
448448
RequestHeader *RequestHeaderAuthenticationOptions
449+
// WebhookTokenAuthnConfig, if present configures remote token reviewers
450+
WebhookTokenAuthenticators []WebhookTokenAuthenticator
449451
}
450452

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

833+
type WebhookTokenAuthenticator struct {
834+
// ConfigFile is a path to a Kubeconfig file with the webhook configuration
835+
ConfigFile string
836+
// CacheTTL indicates how long an authentication result should be cached.
837+
// It takes a valid time duration string (e.g. "5m").
838+
// If empty, you get a default timeout of 10 seconds.
839+
// If zero (e.g. "0m"), caching is disabled
840+
CacheTTL string
841+
}
842+
831843
type OAuthConfig struct {
832844
// MasterCA is the CA for verifying the TLS connection back to the MasterURL.
833845
// "" to use system roots, set to use custom roots, never nil (guaranteed by conversion defaults)

pkg/cmd/server/apis/config/v1/conversions.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,13 @@ func SetDefaults_MasterConfig(obj *MasterConfig) {
136136
obj.AdmissionConfig.PluginConfig[pluginName] = &AdmissionPluginConfig{}
137137
}
138138
}
139+
140+
// Set defaults Cache TTLs for external Webhook Token Reviewers
141+
for _, wta := range obj.AuthConfig.WebhookTokenAuthenticators {
142+
if len(wta.CacheTTL) == 0 {
143+
wta.CacheTTL = "10s"
144+
}
145+
}
139146
}
140147

141148
func SetDefaults_KubernetesMasterConfig(obj *KubernetesMasterConfig) {

pkg/cmd/server/apis/config/v1/testdata/master-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ auditConfig:
2828
webHookMode: ""
2929
authConfig:
3030
requestHeader: null
31+
webhookTokenAuthenticators: null
3132
controllerConfig:
3233
controllers:
3334
- '*'

pkg/cmd/server/apis/config/v1/types.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,8 @@ type MasterConfig struct {
294294
type MasterAuthConfig struct {
295295
// RequestHeader holds options for setting up a front proxy against the the API. It is optional.
296296
RequestHeader *RequestHeaderAuthenticationOptions `json:"requestHeader"`
297+
// WebhookTokenAuthnConfig, if present configures remote token reviewers
298+
WebhookTokenAuthenticators []WebhookTokenAuthenticator `json:"webhookTokenAuthenticators"`
297299
}
298300

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

716+
// WebhookTokenAuthenticators holds the necessary configuation options for
717+
// external token authenticators
718+
type WebhookTokenAuthenticator struct {
719+
// ConfigFile is a path to a Kubeconfig file with the webhook configuration
720+
ConfigFile string `json:"configFile"`
721+
// CacheTTL indicates how long an authentication result should be cached.
722+
// It takes a valid time duration string (e.g. "5m").
723+
// If empty, you get a default timeout of 10 seconds.
724+
// If zero (e.g. "0m"), caching is disabled
725+
CacheTTL string `json:"cacheTTL"`
726+
}
727+
714728
// OAuthConfig holds the necessary configuration options for OAuth authentication
715729
type OAuthConfig struct {
716730
// MasterCA is the CA for verifying the TLS connection back to the MasterURL.

pkg/cmd/server/apis/config/v1/zz_generated.deepcopy.go

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/cmd/server/apis/config/validation/master.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,24 @@ func ValidateMasterConfig(config *configapi.MasterConfig, fldPath *field.Path) V
172172
func ValidateMasterAuthConfig(config configapi.MasterAuthConfig, fldPath *field.Path) ValidationResults {
173173
validationResults := ValidationResults{}
174174

175+
for _, wta := range config.WebhookTokenAuthenticators {
176+
configFile := fldPath.Child("webhookTokenAuthenticators", "ConfigFile")
177+
if len(wta.ConfigFile) == 0 {
178+
validationResults.AddErrors(field.Required(configFile, ""))
179+
} else {
180+
validationResults.AddErrors(ValidateFile(wta.ConfigFile, configFile)...)
181+
}
182+
183+
cacheTTL := fldPath.Child("webhookTokenAuthenticators", "cacheTTL")
184+
if len(wta.CacheTTL) == 0 {
185+
validationResults.AddErrors(field.Required(cacheTTL, ""))
186+
} else if ttl, err := time.ParseDuration(wta.CacheTTL); err != nil {
187+
validationResults.AddErrors(field.Invalid(cacheTTL, wta.CacheTTL, fmt.Sprintf("%v", err)))
188+
} else if ttl < 0 {
189+
validationResults.AddErrors(field.Invalid(cacheTTL, wta.CacheTTL, "cannot be less than zero"))
190+
}
191+
}
192+
175193
if config.RequestHeader == nil {
176194
return validationResults
177195
}

pkg/cmd/server/apis/config/validation/master_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package validation
22

33
import (
4+
"io/ioutil"
5+
"os"
46
"testing"
57

68
"k8s.io/apimachinery/pkg/util/diff"
@@ -525,3 +527,79 @@ func TestValidateIngressIPNetworkCIDR(t *testing.T) {
525527
}
526528
}
527529
}
530+
531+
func TestValidateiMasterAuthConfig(t *testing.T) {
532+
testConfigFile, err := ioutil.TempFile("", "test1.cfg")
533+
if err != nil {
534+
t.Fatalf("unexpected error: %v", err)
535+
}
536+
defer os.Remove(testConfigFile.Name())
537+
538+
testCases := []struct {
539+
testName string
540+
errorCount int
541+
RequestHeader *configapi.RequestHeaderAuthenticationOptions
542+
WebhookTokenAuthenticators []configapi.WebhookTokenAuthenticator
543+
}{
544+
{
545+
testName: "No AuthConfig",
546+
},
547+
{
548+
testName: "Valid RequestHeader",
549+
RequestHeader: &configapi.RequestHeaderAuthenticationOptions{
550+
ClientCA: "ClientCA",
551+
ClientCommonNames: []string{"ClientCommonNames", "2", "3"},
552+
UsernameHeaders: []string{"Username", "Headers"},
553+
GroupHeaders: []string{"Group", "Headers"},
554+
ExtraHeaderPrefixes: []string{"Extra", "Header", "Prefixes"},
555+
},
556+
},
557+
{
558+
testName: "Only One WebhookTokenAuthenticator",
559+
WebhookTokenAuthenticators: []configapi.WebhookTokenAuthenticator{
560+
{
561+
ConfigFile: testConfigFile.Name(),
562+
CacheTTL: "2m",
563+
},
564+
},
565+
},
566+
{
567+
testName: "Unexisting config file in second WebhookTokenAuthenticator",
568+
WebhookTokenAuthenticators: []configapi.WebhookTokenAuthenticator{
569+
{
570+
ConfigFile: testConfigFile.Name(),
571+
CacheTTL: "2m",
572+
},
573+
{
574+
ConfigFile: "Unexisting",
575+
CacheTTL: "2m",
576+
},
577+
},
578+
errorCount: 1,
579+
},
580+
{
581+
testName: "Invalid and Empty CacheTTL WebhookTokenAuthenticator",
582+
WebhookTokenAuthenticators: []configapi.WebhookTokenAuthenticator{
583+
{
584+
ConfigFile: testConfigFile.Name(),
585+
CacheTTL: "-2m",
586+
},
587+
{
588+
ConfigFile: testConfigFile.Name(),
589+
},
590+
},
591+
errorCount: 2,
592+
},
593+
}
594+
for _, test := range testCases {
595+
config := configapi.MasterAuthConfig{
596+
RequestHeader: test.RequestHeader,
597+
WebhookTokenAuthenticators: test.WebhookTokenAuthenticators,
598+
}
599+
errors := ValidateMasterAuthConfig(config, nil)
600+
errorCount := len(errors.Errors)
601+
if test.errorCount != errorCount {
602+
t.Errorf("%s: expected %d errors, got %v", test.testName, test.errorCount, errors.Errors)
603+
}
604+
}
605+
}

pkg/cmd/server/apis/config/zz_generated.deepcopy.go

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/cmd/server/kubernetes/master/master_config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,7 @@ func BuildKubernetesMasterConfig(
588588

589589
func defaultOpenAPIConfig(config configapi.MasterConfig) *openapicommon.Config {
590590
securityDefinitions := spec.SecurityDefinitions{}
591-
if len(config.ServiceAccountConfig.PublicKeyFiles) > 0 {
591+
if len(config.ServiceAccountConfig.PublicKeyFiles) > 0 || len(config.AuthConfig.WebhookTokenAuthenticators) > 0 {
592592
securityDefinitions["BearerToken"] = &spec.SecurityScheme{
593593
SecuritySchemeProps: spec.SecuritySchemeProps{
594594
Type: "apiKey",

pkg/cmd/server/origin/authenticator.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
tokencache "k8s.io/apiserver/pkg/authentication/token/cache"
1717
tokenunion "k8s.io/apiserver/pkg/authentication/token/union"
1818
genericapiserver "k8s.io/apiserver/pkg/server"
19+
webhooktoken "k8s.io/apiserver/plugin/pkg/authenticator/token/webhook"
1920
kclientsetexternal "k8s.io/client-go/kubernetes"
2021
"k8s.io/client-go/rest"
2122
"k8s.io/client-go/util/cert"
@@ -108,6 +109,18 @@ func newAuthenticator(config configapi.MasterConfig, accessTokenGetter oauthclie
108109
group.NewTokenGroupAdder(oauthTokenAuthenticator, []string{bootstrappolicy.AuthenticatedOAuthGroup}))
109110
}
110111

112+
for _, wta := range config.AuthConfig.WebhookTokenAuthenticators {
113+
ttl, err := time.ParseDuration(wta.CacheTTL)
114+
if err != nil {
115+
return nil, nil, fmt.Errorf("Error converting CacheTTL='%s' to duration", wta.CacheTTL)
116+
}
117+
webhookTokenAuthenticator, err := webhooktoken.New(wta.ConfigFile, ttl)
118+
if err != nil {
119+
return nil, nil, fmt.Errorf("Failed to create webhook token authenticator for ConfigFile='%s'", wta.ConfigFile)
120+
}
121+
tokenAuthenticators = append(tokenAuthenticators, webhookTokenAuthenticator)
122+
}
123+
111124
if len(tokenAuthenticators) > 0 {
112125
// Combine all token authenticators
113126
tokenAuth := tokenunion.New(tokenAuthenticators...)

0 commit comments

Comments
 (0)