Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename config file name to authz config filename as it is specific to authz. #347

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 46 additions & 17 deletions cmd/kube-rbac-proxy/app/kube-rbac-proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,23 +205,7 @@ func Run(cfg *server.KubeRBACProxyConfig) error {
return fmt.Errorf("failed to setup an authorizer: %v", err)
}

proxy := httputil.NewSingleHostReverseProxy(cfg.KubeRBACProxyInfo.UpstreamURL)
proxy.Transport = cfg.KubeRBACProxyInfo.UpstreamTransport

if cfg.KubeRBACProxyInfo.UpstreamForceH2C {
// Force http/2 for connections to the upstream i.e. do not start with HTTP1.1 UPGRADE req to
// initialize http/2 session.
// See https://github.com/golang/go/issues/14141#issuecomment-219212895 for more context
proxy.Transport = &http2.Transport{
// Allow http schema. This doesn't automatically disable TLS
AllowHTTP: true,
// Do disable TLS.
// In combination with the schema check above. We could enforce h2c against the upstream server
DialTLS: func(netw, addr string, cfg *tls.Config) (net.Conn, error) {
return net.Dial(netw, addr)
},
}
}
proxy := setupProxyHandler(cfg.KubeRBACProxyInfo)

handler := identityheaders.WithAuthHeaders(proxy, cfg.KubeRBACProxyInfo.UpstreamHeaders)
handler = kubefilters.WithAuthorization(handler, authz, scheme.Codecs)
Expand Down Expand Up @@ -347,3 +331,48 @@ func setupAuthorizer(krbInfo *server.KubeRBACProxyInfo, delegatedAuthz *serverco

return rewritingAuthorizer, nil
}

func setupProxyHandler(cfg *server.KubeRBACProxyInfo) http.Handler {
proxy := &httputil.ReverseProxy{
Rewrite: func(pr *httputil.ProxyRequest) {
target := cfg.UpstreamURL
pr.SetURL(target)
pr.Out.Host = target.Host
copyHeaderIfSet(pr.In, pr.Out, "X-Forwarded-For")
pr.SetXForwarded()
},
}
proxy.Transport = cfg.UpstreamTransport

if cfg.UpstreamForceH2C {
// Force http/2 for connections to the upstream i.e. do not start with HTTP1.1 UPGRADE req to
// initialize http/2 session.
// See https://github.com/golang/go/issues/14141#issuecomment-219212895 for more context
proxy.Transport = &http2.Transport{
// Allow http schema. This doesn't automatically disable TLS
AllowHTTP: true,
// Do disable TLS.
// In combination with the schema check above. We could enforce h2c against the upstream server
DialTLS: func(netw, addr string, cfg *tls.Config) (net.Conn, error) {
return net.Dial(netw, addr)
},
}
}

return proxy
}

func copyHeaderIfSet(inReq *http.Request, outReq *http.Request, headerKey string) {
src := inReq.Header.Values(headerKey)
if src == nil {
return
}

if outReq.Header == nil {
outReq.Header = http.Header{}
}

for _, v := range src {
outReq.Header.Add(headerKey, v)
}
}
162 changes: 162 additions & 0 deletions cmd/kube-rbac-proxy/app/kube-rbac-proxy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
Copyright 2022 the kube-rbac-proxy maintainers. All rights reserved.

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 app

import (
"net/http"
"net/http/httptest"
"net/url"
"reflect"
"testing"
"time"

"github.com/brancz/kube-rbac-proxy/pkg/server"
"github.com/google/go-cmp/cmp"
)

func Test_copyHeaderIfSet(t *testing.T) {
tests := []struct {
name string
headerKey string
inHeader http.Header
outHeader http.Header
expectedValues []string
}{
{
name: "src exists, dist does not",
headerKey: "NONCanon",
inHeader: http.Header{
"Noncanon": []string{"here"},
},
expectedValues: []string{"here"},
},
{
name: "src exists, dist does too",
headerKey: "NONCanon",
inHeader: http.Header{
"Noncanon": []string{"here"},
},
outHeader: http.Header{
"Noncanon": []string{"there"},
},
expectedValues: []string{"there", "here"},
},
{
name: "src does not exist, dist does",
headerKey: "nonCanon",
outHeader: http.Header{
"Noncanon": []string{"there"},
},
expectedValues: []string{"there"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
inReq := http.Request{
Header: tt.inHeader,
}
outReq := http.Request{
Header: tt.outHeader,
}

copyHeaderIfSet(&inReq, &outReq, tt.headerKey)
if gotVals := outReq.Header.Values(tt.headerKey); !reflect.DeepEqual(tt.expectedValues, gotVals) {
t.Errorf("expected values: %v, got: %v", tt.expectedValues, gotVals)
}
})
}
}

func TestProxyHandler(t *testing.T) {
reqChan := make(chan http.Header, 1)
testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
reqChan <- req.Header
w.WriteHeader(http.StatusOK)
}))
t.Cleanup(testServer.Close)

testServerURL, err := url.Parse(testServer.URL)
if err != nil {
t.Fatalf("failed to parse testserver URL")
}

config := &server.KubeRBACProxyInfo{
UpstreamURL: testServerURL,
}
testHandler := setupProxyHandler(config)

// the Golang implementation of an HTTP server passes the remote address of an
// incoming connection into the HTTP request, we'll emulate this in the tests
const (
testRemoteIP = "10.0.0.1"
testRemoteAddr = testRemoteIP + ":10354"
)

tests := []struct {
name string
header http.Header
wantHeader http.Header
}{
{
name: "no extra headers",
header: make(http.Header),
wantHeader: http.Header{
"X-Forwarded-For": []string{testRemoteIP},
"X-Forwarded-Host": []string{testServerURL.Host},
"X-Forwarded-Proto": []string{"http"},
},
},
{
name: "X-Forwarded-For is set",
header: http.Header{
"X-Forwarded-For": []string{"10.0.0.2"},
},
wantHeader: http.Header{
"X-Forwarded-For": []string{"10.0.0.2, " + testRemoteIP},
"X-Forwarded-Host": []string{testServerURL.Host},
"X-Forwarded-Proto": []string{"http"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
testWriter := httptest.NewRecorder()
req, err := http.NewRequest(http.MethodGet, testServer.URL, nil)
if err != nil {
t.Fatalf("failed to create an http request: %v", err)
}
req.Header = tt.header
req.RemoteAddr = testRemoteAddr
testHandler.ServeHTTP(testWriter, req)

var gotHeaders http.Header
select {
case gotHeaders = <-reqChan:
case <-time.After(5 * time.Second):
t.Fatal("timeout: did not receive any response")
}

gotHeaders.Del("Content-Length")
gotHeaders.Del("Accept-Encoding")
gotHeaders.Del("Date")

if !reflect.DeepEqual(gotHeaders, tt.wantHeader) {
t.Errorf("got different headers than expected: %s", cmp.Diff(tt.wantHeader, gotHeaders))
}
})
}
}
10 changes: 5 additions & 5 deletions cmd/kube-rbac-proxy/app/options/proxyoptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ type ProxyOptions struct {

UpstreamHeader *identityheaders.AuthnHeaderConfig

ConfigFileName string
AllowPaths []string
IgnorePaths []string
AuthzConfigFileName string
AllowPaths []string
IgnorePaths []string

ProxyEndpointsPort int

Expand All @@ -70,7 +70,7 @@ func (o *ProxyOptions) AddFlags(flagset *pflag.FlagSet) {
flagset.StringVar(&o.UpstreamClientCertFile, "upstream-client-cert-file", "", "If set, the client will be used to authenticate the proxy to upstream. Requires --upstream-client-key-file to be set, too.")
flagset.StringVar(&o.UpstreamClientKeyFile, "upstream-client-key-file", "", "The key matching the certificate from --upstream-client-cert-file. If set, requires --upstream-client-cert-file to be set, too.")

flagset.StringVar(&o.ConfigFileName, "config-file", "", "Configuration file to configure static and rewrites authorization of the kube-rbac-proxy.")
flagset.StringVar(&o.AuthzConfigFileName, "config-file", "", "Configuration file to configure static and rewrites authorization of the kube-rbac-proxy.")
flagset.StringSliceVar(&o.AllowPaths, "allow-paths", nil, "Comma-separated list of paths against which kube-rbac-proxy pattern-matches the incoming request. If the request doesn't match, kube-rbac-proxy responds with a 404 status code. If omitted, the incoming request path isn't checked. Cannot be used with --ignore-paths.")
flagset.StringSliceVar(&o.IgnorePaths, "ignore-paths", nil, "Comma-separated list of paths against which kube-rbac-proxy pattern-matches the incoming request. If the requst matches, it will proxy the request without performing an authentication or authorization check. Cannot be used with --allow-paths.")

Expand Down Expand Up @@ -133,7 +133,7 @@ func (o *ProxyOptions) ApplyTo(krpInfo *server.KubeRBACProxyInfo, authInfo *serv
return fmt.Errorf("failed to setup transport for upstream: %w", err)
}

if configFileName := o.ConfigFileName; len(configFileName) > 0 {
if configFileName := o.AuthzConfigFileName; len(configFileName) > 0 {
krpInfo.Authorization, err = parseAuthorizationConfigFile(configFileName)
if err != nil {
return fmt.Errorf("failed to read the config file: %w", err)
Expand Down
Loading