Skip to content

Commit 963b4b5

Browse files
committed
Wire in WebhookTokenAutenticator support
Signed-off-by: Simo Sorce <[email protected]>
1 parent 9d97317 commit 963b4b5

File tree

13 files changed

+446
-1
lines changed

13 files changed

+446
-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 k := range config.AuthConfig.WebhookTokenAuthenticators {
242+
refs = append(refs, &config.AuthConfig.WebhookTokenAuthenticators[k].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/serialization_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,12 @@ func fuzzInternalObject(t *testing.T, forVersion schema.GroupVersion, item runti
215215
}
216216
}
217217

218+
for i := range obj.AuthConfig.WebhookTokenAuthenticators {
219+
if len(obj.AuthConfig.WebhookTokenAuthenticators[i].CacheTTL) == 0 {
220+
obj.AuthConfig.WebhookTokenAuthenticators[i].CacheTTL = "2m"
221+
}
222+
}
223+
218224
obj.AuditConfig.InternalAuditFilePath = ""
219225

220226
// this field isn't serialized

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
@@ -831,6 +833,16 @@ type DNSConfig struct {
831833
AllowRecursiveQueries bool
832834
}
833835

836+
type WebhookTokenAuthenticator struct {
837+
// ConfigFile is a path to a Kubeconfig file with the webhook configuration
838+
ConfigFile string
839+
// CacheTTL indicates how long an authentication result should be cached.
840+
// It takes a valid time duration string (e.g. "5m").
841+
// If empty, you get a default timeout of 2 minutes.
842+
// If zero (e.g. "0m"), caching is disabled
843+
CacheTTL string
844+
}
845+
834846
type OAuthConfig struct {
835847
// MasterCA is the CA for verifying the TLS connection back to the MasterURL.
836848
// "" 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 i := range obj.AuthConfig.WebhookTokenAuthenticators {
142+
if len(obj.AuthConfig.WebhookTokenAuthenticators[i].CacheTTL) == 0 {
143+
obj.AuthConfig.WebhookTokenAuthenticators[i].CacheTTL = "2m"
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 2 minutes.
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
@@ -173,6 +173,24 @@ func ValidateMasterConfig(config *configapi.MasterConfig, fldPath *field.Path) V
173173
func ValidateMasterAuthConfig(config configapi.MasterAuthConfig, fldPath *field.Path) ValidationResults {
174174
validationResults := ValidationResults{}
175175

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

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

Lines changed: 86 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,87 @@ func TestValidateIngressIPNetworkCIDR(t *testing.T) {
525527
}
526528
}
527529
}
530+
531+
func TestValidateMasterAuthConfig(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+
RequestHeader *configapi.RequestHeaderAuthenticationOptions
541+
WebhookTokenAuthenticators []configapi.WebhookTokenAuthenticator
542+
expectedErrors []string
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+
expectedErrors: []string{"webhookTokenAuthenticators.ConfigFile: Invalid value: \"Unexisting\": could not read file: stat Unexisting: no such file or directory"},
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+
expectedErrors: []string{
592+
"webhookTokenAuthenticators.cacheTTL: Invalid value: \"-2m\": cannot be less than zero",
593+
"webhookTokenAuthenticators.cacheTTL: Required value",
594+
},
595+
},
596+
}
597+
for _, test := range testCases {
598+
config := configapi.MasterAuthConfig{
599+
RequestHeader: test.RequestHeader,
600+
WebhookTokenAuthenticators: test.WebhookTokenAuthenticators,
601+
}
602+
errors := ValidateMasterAuthConfig(config, nil)
603+
if len(test.expectedErrors) != len(errors.Errors) {
604+
t.Errorf("%s: expected %v errors, got %v", test.testName, test.expectedErrors, errors.Errors)
605+
continue
606+
}
607+
for i := range test.expectedErrors {
608+
if errors.Errors[i].Error() != test.expectedErrors[i] {
609+
t.Errorf("%s: expected error '%s', got '%s'", test.testName, test.expectedErrors[i], errors.Errors[i])
610+
}
611+
}
612+
}
613+
}

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 parsing CacheTTL=%q: %v", wta.CacheTTL, err)
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=%q: %v", wta.ConfigFile, err)
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)