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

Add '--allow-paths' CLI flag #83

Merged
merged 1 commit into from
Aug 13, 2020
Merged
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
15 changes: 14 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ type config struct {
auth proxy.Config
tls tlsConfig
kubeconfigLocation string
allowPaths []string
}

type tlsConfig struct {
Expand Down Expand Up @@ -115,6 +116,7 @@ func main() {
flagset.BoolVar(&cfg.upstreamForceH2C, "upstream-force-h2c", false, "Force h2c to communiate with the upstream. This is required when the upstream speaks h2c(http/2 cleartext - insecure variant of http/2) only. For example, go-grpc server in the insecure mode, such as helm's tiller w/o TLS, speaks h2c only")
flagset.StringVar(&cfg.upstreamCAFile, "upstream-ca-file", "", "The CA the upstream uses for TLS connection. This is required when the upstream uses TLS and its own CA certificate")
flagset.StringVar(&configFileName, "config-file", "", "Configuration file to configure kube-rbac-proxy.")
flagset.StringSliceVar(&cfg.allowPaths, "allow-paths", nil, "Comma-separated list of paths against which kube-rbac-proxy 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.")

// TLS flags
flagset.StringVar(&cfg.tls.certFile, "tls-cert-file", "", "File containing the default x509 Certificate for HTTPS. (CA cert, if any, concatenated after server cert)")
Expand Down Expand Up @@ -148,7 +150,7 @@ func main() {

upstreamURL, err := url.Parse(cfg.upstream)
if err != nil {
klog.Fatalf("Failed to build parse upstream URL: %v", err)
klog.Fatalf("Failed to parse upstream URL: %v", err)
}

if configFileName != "" {
Expand Down Expand Up @@ -215,6 +217,17 @@ func main() {
proxy.Transport = upstreamTransport
mux := http.NewServeMux()
mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
found := len(cfg.allowPaths) == 0
for _, path := range cfg.allowPaths {
if req.URL.Path == path {
found = true
break
}
}
if !found {
http.NotFound(w, req)
return
}
ok := auth.Handle(w, req)
if !ok {
return
Expand Down
7 changes: 7 additions & 0 deletions test/e2e/allowpaths/clusterRole-client.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: metrics
rules:
- nonResourceURLs: ["/metrics"]
verbs: ["get"]
14 changes: 14 additions & 0 deletions test/e2e/allowpaths/clusterRole.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kube-rbac-proxy
namespace: default
rules:
- apiGroups: ["authentication.k8s.io"]
resources:
- tokenreviews
verbs: ["create"]
- apiGroups: ["authorization.k8s.io"]
resources:
- subjectaccessreviews
verbs: ["create"]
12 changes: 12 additions & 0 deletions test/e2e/allowpaths/clusterRoleBinding-client.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: metrics
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: metrics
subjects:
- kind: ServiceAccount
name: default
namespace: default
13 changes: 13 additions & 0 deletions test/e2e/allowpaths/clusterRoleBinding.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: kube-rbac-proxy
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: kube-rbac-proxy
subjects:
- kind: ServiceAccount
name: kube-rbac-proxy
namespace: default
32 changes: 32 additions & 0 deletions test/e2e/allowpaths/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: kube-rbac-proxy
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: kube-rbac-proxy
template:
metadata:
labels:
app: kube-rbac-proxy
spec:
serviceAccountName: kube-rbac-proxy
containers:
- name: kube-rbac-proxy
image: quay.io/brancz/kube-rbac-proxy:local
args:
- "--secure-listen-address=0.0.0.0:8443"
- "--upstream=http://127.0.0.1:8081/"
- "--allow-paths=/metrics"
- "--logtostderr=true"
- "--v=10"
ports:
- containerPort: 8443
name: https
- name: prometheus-example-app
image: quay.io/brancz/prometheus-example-app:v0.1.0
args:
- "--bind=127.0.0.1:8081"
14 changes: 14 additions & 0 deletions test/e2e/allowpaths/service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
labels:
app: kube-rbac-proxy
name: kube-rbac-proxy
namespace: default
spec:
ports:
- name: https
port: 8443
targetPort: https
selector:
app: kube-rbac-proxy
5 changes: 5 additions & 0 deletions test/e2e/allowpaths/serviceAccount.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: kube-rbac-proxy
namespace: default
85 changes: 85 additions & 0 deletions test/e2e/basics.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package e2e

import (
"fmt"
"testing"

"github.com/brancz/kube-rbac-proxy/test/kubetest"
Expand Down Expand Up @@ -190,6 +191,90 @@ func testTokenAudience(s *kubetest.Suite) kubetest.TestSuite {
}
}

func testAllowPathsRegexp(s *kubetest.Suite) kubetest.TestSuite {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@simonpasquier maybe as a small follow-up: s/testAllowPathsRegexp/testAllowPaths ?

return func(t *testing.T) {
command := `STATUS_CODE=$(curl --connect-timeout 5 -o /dev/null -v -s -k --write-out "%%{http_code}" -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" https://kube-rbac-proxy.default.svc.cluster.local:8443%s); if [[ "$STATUS_CODE" != %d ]]; then echo "expecting %d status code, got $STATUS_CODE instead" > /proc/self/fd/2; exit 1; fi`

kubetest.Scenario{
Name: "WithPathhNotAllowed",
Description: `
As a client with the correct RBAC rules,
I get a 404 response when requesting a path which isn't allowed by kube-rbac-proxy
`,

Given: kubetest.Setups(
kubetest.CreatedManifests(
s.KubeClient,
"allowpaths/clusterRole.yaml",
"allowpaths/clusterRoleBinding.yaml",
"allowpaths/deployment.yaml",
"allowpaths/service.yaml",
"allowpaths/serviceAccount.yaml",
"allowpaths/clusterRole-client.yaml",
"allowpaths/clusterRoleBinding-client.yaml",
),
),
When: kubetest.Conditions(
kubetest.PodsAreReady(
s.KubeClient,
1,
"app=kube-rbac-proxy",
),
kubetest.ServiceIsReady(
s.KubeClient,
"kube-rbac-proxy",
),
),
Then: kubetest.Checks(
ClientSucceeds(
s.KubeClient,
fmt.Sprintf(command, "/", 404, 404),
nil,
),
),
}.Run(t)

kubetest.Scenario{
Name: "WithPathAllowed",
Description: `
As a client with the correct RBAC rules,
I succeed with my request for a path that is allowed
`,

Given: kubetest.Setups(
kubetest.CreatedManifests(
s.KubeClient,
"allowpaths/clusterRole.yaml",
"allowpaths/clusterRoleBinding.yaml",
"allowpaths/deployment.yaml",
"allowpaths/service.yaml",
"allowpaths/serviceAccount.yaml",
"allowpaths/clusterRole-client.yaml",
"allowpaths/clusterRoleBinding-client.yaml",
),
),
When: kubetest.Conditions(
kubetest.PodsAreReady(
s.KubeClient,
1,
"app=kube-rbac-proxy",
),
kubetest.ServiceIsReady(
s.KubeClient,
"kube-rbac-proxy",
),
),
Then: kubetest.Checks(
ClientSucceeds(
s.KubeClient,
fmt.Sprintf(command, "/metrics", 200, 200),
nil,
),
),
}.Run(t)
}
}

func ClientSucceeds(client kubernetes.Interface, command string, opts *kubetest.RunOptions) kubetest.Check {
return func(ctx *kubetest.ScenarioContext) error {
return kubetest.RunSucceeds(
Expand Down
1 change: 1 addition & 0 deletions test/e2e/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func Test(t *testing.T) {
tests := map[string]kubetest.TestSuite{
"Basics": testBasics(suite),
"TokenAudience": testTokenAudience(suite),
"AllowPath": testAllowPathsRegexp(suite),
}

for name, tc := range tests {
Expand Down