Skip to content

Commit

Permalink
Add '--allow-paths-regex' CLI flag
Browse files Browse the repository at this point in the history
Signed-off-by: Simon Pasquier <spasquie@redhat.com>
  • Loading branch information
simonpasquier committed Aug 11, 2020
1 parent 281be3a commit 7ba3ba8
Show file tree
Hide file tree
Showing 10 changed files with 196 additions and 1 deletion.
14 changes: 13 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"net/url"
"os"
"os/signal"
"regexp"
"strings"
"syscall"
"time"
Expand Down Expand Up @@ -60,6 +61,7 @@ type config struct {
auth proxy.Config
tls tlsConfig
kubeconfigLocation string
allowPathsRegex string
}

type tlsConfig struct {
Expand Down Expand Up @@ -115,6 +117,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.StringVar(&cfg.allowPathsRegex, "allow-paths-regex", ".*", "Regular expression against which kube-rbac-proxy matches the incoming request path. If the request doesn't match, kube-rbac-proxy responds with a 404 status code.")

// 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 +151,12 @@ 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)
}

allowPathsRe, err := regexp.Compile(cfg.allowPathsRegex)
if err != nil {
klog.Fatalf("Failed to compile allow path regex: %v", err)
}

if configFileName != "" {
Expand Down Expand Up @@ -215,6 +223,10 @@ func main() {
proxy.Transport = upstreamTransport
mux := http.NewServeMux()
mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if !allowPathsRe.MatchString(req.URL.Path) {
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-regex=/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 {
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"; 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(
ClientFails(
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": testAllowPath(suite),
}

for name, tc := range tests {
Expand Down

0 comments on commit 7ba3ba8

Please sign in to comment.