Skip to content

Commit a86ef13

Browse files
author
OpenShift Bot
authored
Merge pull request #12803 from deads2k/auth-01-proxy
Merged by openshift-bot
2 parents 1ae364e + f40bb8a commit a86ef13

File tree

15 files changed

+722
-70
lines changed

15 files changed

+722
-70
lines changed

pkg/cmd/server/api/helpers.go

+19
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,10 @@ func GetMasterFileReferences(config *MasterConfig) []*string {
241241
refs = append(refs, &config.KubernetesMasterConfig.ProxyClientInfo.KeyFile)
242242
}
243243

244+
if config.AuthConfig.RequestHeader != nil {
245+
refs = append(refs, &config.AuthConfig.RequestHeader.ClientCA)
246+
}
247+
244248
refs = append(refs, &config.ServiceAccountConfig.MasterCA)
245249
refs = append(refs, &config.ServiceAccountConfig.PrivateKeyFile)
246250
for i := range config.ServiceAccountConfig.PublicKeyFiles {
@@ -457,6 +461,21 @@ func GetOAuthClientCertCAs(options MasterConfig) ([]*x509.Certificate, error) {
457461
return allCerts, nil
458462
}
459463

464+
func GetRequestHeaderClientCertCAs(options MasterConfig) ([]*x509.Certificate, error) {
465+
if !UseTLS(options.ServingInfo.ServingInfo) {
466+
return nil, nil
467+
}
468+
if options.AuthConfig.RequestHeader == nil {
469+
return nil, nil
470+
}
471+
472+
certs, err := cmdutil.CertificatesFromFile(options.AuthConfig.RequestHeader.ClientCA)
473+
if err != nil {
474+
return nil, fmt.Errorf("Error reading %s: %s", options.AuthConfig.RequestHeader.ClientCA, err)
475+
}
476+
return certs, nil
477+
}
478+
460479
func getAPIClientCertCAs(options MasterConfig) ([]*x509.Certificate, error) {
461480
if !UseTLS(options.ServingInfo.ServingInfo) {
462481
return nil, nil

pkg/cmd/server/api/types.go

+27
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,10 @@ type MasterConfig struct {
254254
// ServingInfo describes how to start serving
255255
ServingInfo HTTPServingInfo
256256

257+
// AuthConfig configures authentication options in addition to the standard
258+
// oauth token and client certificate authenticators
259+
AuthConfig MasterAuthConfig
260+
257261
// CORSAllowedOrigins
258262
CORSAllowedOrigins []string
259263

@@ -344,6 +348,29 @@ type MasterConfig struct {
344348
AuditConfig AuditConfig
345349
}
346350

351+
// MasterAuthConfig configures authentication options in addition to the standard
352+
// oauth token and client certificate authenticators
353+
type MasterAuthConfig struct {
354+
// RequestHeader holds options for setting up a front proxy against the the API. It is optional.
355+
RequestHeader *RequestHeaderAuthenticationOptions
356+
}
357+
358+
// RequestHeaderAuthenticationOptions provides options for setting up a front proxy against the entire
359+
// API instead of against the /oauth endpoint.
360+
type RequestHeaderAuthenticationOptions struct {
361+
// ClientCA is a file with the trusted signer certs. It is required.
362+
ClientCA string
363+
// ClientCommonNames is a required list of common names to require a match from.
364+
ClientCommonNames []string
365+
366+
// UsernameHeaders is the list of headers to check for user information. First hit wins.
367+
UsernameHeaders []string
368+
// GroupNameHeader is the set of headers to check for group information. All are unioned.
369+
GroupHeaders []string
370+
// ExtraHeaderPrefixes is the set of request header prefixes to inspect for user extra. X-Remote-Extra- is suggested.
371+
ExtraHeaderPrefixes []string
372+
}
373+
347374
// AuditConfig holds configuration for the audit capabilities
348375
type AuditConfig struct {
349376
// If this flag is set, audit log will be printed in the logs.

pkg/cmd/server/api/v1/swagger_doc.go

+23
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,15 @@ func (LocalQuota) SwaggerDoc() map[string]string {
431431
return map_LocalQuota
432432
}
433433

434+
var map_MasterAuthConfig = map[string]string{
435+
"": "MasterAuthConfig configures authentication options in addition to the standard oauth token and client certificate authenticators",
436+
"requestHeader": "RequestHeader holds options for setting up a front proxy against the the API. It is optional.",
437+
}
438+
439+
func (MasterAuthConfig) SwaggerDoc() map[string]string {
440+
return map_MasterAuthConfig
441+
}
442+
434443
var map_MasterClients = map[string]string{
435444
"": "MasterClients holds references to `.kubeconfig` files that qualify master clients for OpenShift and Kubernetes",
436445
"openshiftLoopbackKubeConfig": "OpenShiftLoopbackKubeConfig is a .kubeconfig filename for system components to loopback to this master",
@@ -446,6 +455,7 @@ func (MasterClients) SwaggerDoc() map[string]string {
446455
var map_MasterConfig = map[string]string{
447456
"": "MasterConfig holds the necessary configuration options for the OpenShift master",
448457
"servingInfo": "ServingInfo describes how to start serving",
458+
"authConfig": "AuthConfig configures authentication options in addition to the standard oauth token and client certificate authenticators",
449459
"corsAllowedOrigins": "CORSAllowedOrigins",
450460
"apiLevels": "APILevels is a list of API levels that should be enabled on startup: v1 as examples",
451461
"masterPublicURL": "MasterPublicURL is how clients can access the OpenShift API server",
@@ -701,6 +711,19 @@ func (RemoteConnectionInfo) SwaggerDoc() map[string]string {
701711
return map_RemoteConnectionInfo
702712
}
703713

714+
var map_RequestHeaderAuthenticationOptions = map[string]string{
715+
"": "RequestHeaderAuthenticationOptions provides options for setting up a front proxy against the entire API instead of against the /oauth endpoint.",
716+
"clientCA": "ClientCA is a file with the trusted signer certs. It is required.",
717+
"clientCommonNames": "ClientCommonNames is a required list of common names to require a match from.",
718+
"usernameHeaders": "UsernameHeaders is the list of headers to check for user information. First hit wins.",
719+
"groupHeaders": "GroupNameHeader is the set of headers to check for group information. All are unioned.",
720+
"extraHeaderPrefixes": "ExtraHeaderPrefixes is the set of request header prefixes to inspect for user extra. X-Remote-Extra- is suggested.",
721+
}
722+
723+
func (RequestHeaderAuthenticationOptions) SwaggerDoc() map[string]string {
724+
return map_RequestHeaderAuthenticationOptions
725+
}
726+
704727
var map_RequestHeaderIdentityProvider = map[string]string{
705728
"": "RequestHeaderIdentityProvider provides identities for users authenticating using request header credentials",
706729
"loginURL": "LoginURL is a URL to redirect unauthenticated /authorize requests to Unauthenticated requests from OAuth clients which expect interactive logins will be redirected here ${url} is replaced with the current URL, escaped to be safe in a query parameter\n https://www.example.com/sso-login?then=${url}\n${query} is replaced with the current query string\n https://www.example.com/auth-proxy/oauth/authorize?${query}",

pkg/cmd/server/api/v1/types.go

+27
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,10 @@ type MasterConfig struct {
174174
// ServingInfo describes how to start serving
175175
ServingInfo HTTPServingInfo `json:"servingInfo"`
176176

177+
// AuthConfig configures authentication options in addition to the standard
178+
// oauth token and client certificate authenticators
179+
AuthConfig MasterAuthConfig `json:"authConfig"`
180+
177181
// CORSAllowedOrigins
178182
CORSAllowedOrigins []string `json:"corsAllowedOrigins"`
179183

@@ -264,6 +268,29 @@ type MasterConfig struct {
264268
AuditConfig AuditConfig `json:"auditConfig"`
265269
}
266270

271+
// MasterAuthConfig configures authentication options in addition to the standard
272+
// oauth token and client certificate authenticators
273+
type MasterAuthConfig struct {
274+
// RequestHeader holds options for setting up a front proxy against the the API. It is optional.
275+
RequestHeader *RequestHeaderAuthenticationOptions `json:"requestHeader"`
276+
}
277+
278+
// RequestHeaderAuthenticationOptions provides options for setting up a front proxy against the entire
279+
// API instead of against the /oauth endpoint.
280+
type RequestHeaderAuthenticationOptions struct {
281+
// ClientCA is a file with the trusted signer certs. It is required.
282+
ClientCA string `json:"clientCA"`
283+
// ClientCommonNames is a required list of common names to require a match from.
284+
ClientCommonNames []string `json:"clientCommonNames"`
285+
286+
// UsernameHeaders is the list of headers to check for user information. First hit wins.
287+
UsernameHeaders []string `json:"usernameHeaders"`
288+
// GroupNameHeader is the set of headers to check for group information. All are unioned.
289+
GroupHeaders []string `json:"groupHeaders"`
290+
// ExtraHeaderPrefixes is the set of request header prefixes to inspect for user extra. X-Remote-Extra- is suggested.
291+
ExtraHeaderPrefixes []string `json:"extraHeaderPrefixes"`
292+
}
293+
267294
// AuditConfig holds configuration for the audit capabilities
268295
type AuditConfig struct {
269296
// If this flag is set, audit log will be printed in the logs.

pkg/cmd/server/api/v1/types_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ auditConfig:
112112
maximumFileRetentionDays: 0
113113
maximumFileSizeMegabytes: 0
114114
maximumRetainedFiles: 0
115+
authConfig:
116+
requestHeader: null
115117
controllerConfig:
116118
serviceServingCert:
117119
signer: null

pkg/cmd/server/api/validation/master.go

+28
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,34 @@ func ValidateMasterConfig(config *api.MasterConfig, fldPath *field.Path) Validat
193193

194194
validationResults.Append(ValidateAuditConfig(config.AuditConfig, fldPath.Child("auditConfig")))
195195

196+
validationResults.Append(ValidateMasterAuthConfig(config.AuthConfig, fldPath.Child("authConfig")))
197+
198+
return validationResults
199+
}
200+
201+
func ValidateMasterAuthConfig(config api.MasterAuthConfig, fldPath *field.Path) ValidationResults {
202+
validationResults := ValidationResults{}
203+
204+
if config.RequestHeader == nil {
205+
return validationResults
206+
}
207+
208+
if len(config.RequestHeader.ClientCA) == 0 {
209+
validationResults.AddErrors(field.Required(fldPath.Child("requestHeader.clientCA"), "must be specified for a secure connection"))
210+
}
211+
if len(config.RequestHeader.ClientCommonNames) == 0 {
212+
validationResults.AddErrors(field.Required(fldPath.Child("requestHeader.clientCommonNames"), "must be specified for a secure connection"))
213+
}
214+
if len(config.RequestHeader.UsernameHeaders) == 0 {
215+
validationResults.AddErrors(field.Required(fldPath.Child("requestHeader.usernameHeaders"), "must be specified for a secure connection"))
216+
}
217+
if len(config.RequestHeader.GroupHeaders) == 0 {
218+
validationResults.AddErrors(field.Required(fldPath.Child("requestHeader.groupHeaders"), "must be specified for a secure connection"))
219+
}
220+
if len(config.RequestHeader.ExtraHeaderPrefixes) == 0 {
221+
validationResults.AddErrors(field.Required(fldPath.Child("requestHeader.extraHeaderPrefixes"), "must be specified for a secure connection"))
222+
}
223+
196224
return validationResults
197225
}
198226

pkg/cmd/server/kubernetes/master_config.go

+34
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"crypto/tls"
55
"errors"
66
"fmt"
7+
"io/ioutil"
78
"net"
89
"net/http"
910
"net/url"
@@ -112,6 +113,7 @@ func BuildDefaultAPIServer(options configapi.MasterConfig) (*apiserveroptions.Se
112113
server.GenericServerRunOptions.TLSCertFile = options.ServingInfo.ServerCert.CertFile
113114
server.GenericServerRunOptions.TLSPrivateKeyFile = options.ServingInfo.ServerCert.KeyFile
114115
server.GenericServerRunOptions.ClientCAFile = options.ServingInfo.ClientCA
116+
115117
server.GenericServerRunOptions.MaxRequestsInFlight = options.ServingInfo.MaxRequestsInFlight
116118
server.GenericServerRunOptions.MinRequestTimeout = options.ServingInfo.RequestTimeoutSeconds
117119
for _, nc := range options.ServingInfo.NamedCertificates {
@@ -314,6 +316,14 @@ func BuildKubernetesMasterConfig(
314316
for _, cert := range oAuthClientCertCAs {
315317
genericConfig.SecureServingInfo.ClientCA.AddCert(cert)
316318
}
319+
requestHeaderCACerts, err := configapi.GetRequestHeaderClientCertCAs(options)
320+
if err != nil {
321+
glog.Fatalf("Error setting up request header client certificates: %v", err)
322+
}
323+
for _, cert := range requestHeaderCACerts {
324+
genericConfig.SecureServingInfo.ClientCA.AddCert(cert)
325+
}
326+
317327
url, err := url.Parse(options.MasterPublicURL)
318328
if err != nil {
319329
glog.Fatalf("Error parsing master public url %q: %v", options.MasterPublicURL, err)
@@ -554,3 +564,27 @@ func getAPIResourceConfig(options configapi.MasterConfig) genericapiserver.APIRe
554564

555565
return resourceConfig
556566
}
567+
568+
// TODO remove this func in 1.6 when we get rid of the hack above
569+
func concatenateFiles(prefix, separator string, files ...string) (string, error) {
570+
data := []byte{}
571+
for _, file := range files {
572+
fileBytes, err := ioutil.ReadFile(file)
573+
if err != nil {
574+
return "", err
575+
}
576+
data = append(data, fileBytes...)
577+
data = append(data, []byte(separator)...)
578+
}
579+
tmpFile, err := ioutil.TempFile("", prefix)
580+
if err != nil {
581+
return "", err
582+
}
583+
if _, err := tmpFile.Write(data); err != nil {
584+
return "", err
585+
}
586+
if err := tmpFile.Close(); err != nil {
587+
return "", err
588+
}
589+
return tmpFile.Name(), nil
590+
}

pkg/cmd/server/origin/master_config.go

+21-8
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
"k8s.io/kubernetes/plugin/pkg/admission/namespace/lifecycle"
3434
saadmit "k8s.io/kubernetes/plugin/pkg/admission/serviceaccount"
3535
storageclassdefaultadmission "k8s.io/kubernetes/plugin/pkg/admission/storageclass/default"
36+
"k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/headerrequest"
3637

3738
"github.com/openshift/origin/pkg/auth/authenticator"
3839
"github.com/openshift/origin/pkg/auth/authenticator/anonymous"
@@ -664,16 +665,28 @@ func newAuthenticator(config configapi.MasterConfig, restOptionsGetter restoptio
664665
authenticators = append(authenticators, certauth)
665666
}
666667

667-
ret := &unionrequest.Authenticator{
668-
FailOnError: true,
669-
Handlers: []authenticator.Request{
670-
// if you change this, have a look at the impersonationFilter where we attach groups to the impersonated user
671-
group.NewGroupAdder(&unionrequest.Authenticator{FailOnError: true, Handlers: authenticators}, []string{bootstrappolicy.AuthenticatedGroup}),
672-
anonymous.NewAuthenticator(),
673-
},
668+
topLevelAuthenticators := []authenticator.Request{}
669+
// if we have a front proxy providing authentication configuration, wire it up and it should come first
670+
if config.AuthConfig.RequestHeader != nil {
671+
requestHeaderAuthenticator, err := headerrequest.NewSecure(
672+
config.AuthConfig.RequestHeader.ClientCA,
673+
config.AuthConfig.RequestHeader.ClientCommonNames,
674+
config.AuthConfig.RequestHeader.UsernameHeaders,
675+
config.AuthConfig.RequestHeader.GroupHeaders,
676+
config.AuthConfig.RequestHeader.ExtraHeaderPrefixes,
677+
)
678+
if err != nil {
679+
return nil, fmt.Errorf("Error building front proxy auth config: %v", err)
680+
}
681+
topLevelAuthenticators = append(topLevelAuthenticators, requestHeaderAuthenticator)
674682
}
683+
topLevelAuthenticators = append(topLevelAuthenticators, group.NewGroupAdder(&unionrequest.Authenticator{FailOnError: true, Handlers: authenticators}, []string{bootstrappolicy.AuthenticatedGroup}))
684+
topLevelAuthenticators = append(topLevelAuthenticators, anonymous.NewAuthenticator())
675685

676-
return ret, nil
686+
return &unionrequest.Authenticator{
687+
FailOnError: true,
688+
Handlers: topLevelAuthenticators,
689+
}, nil
677690
}
678691

679692
func newProjectAuthorizationCache(authorizer authorizer.Authorizer, kubeClient *kclientset.Clientset, informerFactory shared.InformerFactory) *projectauth.AuthorizationCache {

test/integration/auth_proxy_test.go

+3-7
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313
configapi "github.com/openshift/origin/pkg/cmd/server/api"
1414
"github.com/openshift/origin/pkg/cmd/server/origin"
1515
oauthapi "github.com/openshift/origin/pkg/oauth/api"
16-
clientregistry "github.com/openshift/origin/pkg/oauth/registry/oauthclient"
1716
testutil "github.com/openshift/origin/test/util"
1817
testserver "github.com/openshift/origin/test/util/server"
1918
)
@@ -90,6 +89,9 @@ func TestAuthProxyOnAuthorize(t *testing.T) {
9089

9190
// make our authorize request again, but this time our transport has properly set the auth info for the front proxy
9291
req, err := http.NewRequest("GET", rawAuthorizeRequest, nil)
92+
if err != nil {
93+
t.Fatalf("Unexpected error: %v", err)
94+
}
9395
_, err = httpClient.Do(req)
9496
if err != nil {
9597
t.Errorf("Unexpected error: %v", err)
@@ -108,12 +110,6 @@ func TestAuthProxyOnAuthorize(t *testing.T) {
108110
}
109111
}
110112

111-
func createClient(t *testing.T, clientRegistry clientregistry.Registry, client *oauthapi.OAuthClient) {
112-
if _, err := clientRegistry.CreateClient(kapi.NewContext(), client); err != nil {
113-
t.Errorf("Error creating client: %v due to %v\n", client, err)
114-
}
115-
}
116-
117113
type checkRedirect func(req *http.Request, via []*http.Request) error
118114

119115
func getRedirectMethod(t *testing.T, redirectRecord *[]url.URL) checkRedirect {

0 commit comments

Comments
 (0)