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

*: Support reviewing audiences of token #56

Merged
merged 4 commits into from
Feb 17, 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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 2 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ jobs:
include:
- stage: Integration Tests
before_script:
- curl -Lo kind https://github.com/kubernetes-sigs/kind/releases/download/0.2.1/kind-linux-amd64 && chmod +x kind && sudo mv kind /usr/local/bin/
- kind create cluster
- export KUBECONFIG="$(kind get kubeconfig-path)"
- curl -Lo kind https://github.com/kubernetes-sigs/kind/releases/download/v0.7.0/kind-linux-amd64 && chmod +x kind && sudo mv kind /usr/local/bin/
- kind create cluster --config test/e2e/kind-config/kind-config.yaml
script:
- VERSION=local make container
- kind load docker-image quay.io/brancz/kube-rbac-proxy:local
Expand Down
5 changes: 3 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
FROM golang:1.12-alpine AS build
FROM golang:1.13-alpine AS build
ENV GOFLAGS="-mod=vendor"
RUN apk add --update make && apk add --no-cache git
WORKDIR /go/src/github.com/brancz/kube-rbac-proxy
COPY . .
RUN make build && cp /go/src/github.com/brancz/kube-rbac-proxy/_output/linux/$(go env GOARCH)/kube-rbac-proxy /usr/local/bin

FROM alpine:3.8
FROM alpine:3.10
RUN apk add -U --no-cache ca-certificates && rm -rf /var/cache/apk/*
COPY --from=build /usr/local/bin/kube-rbac-proxy .
ENTRYPOINT ["./kube-rbac-proxy"]
Expand Down
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ GOOS?=$(shell uname -s | tr A-Z a-z)
GOARCH?=$(shell go env GOARCH)
OUT_DIR=_output
BIN?=kube-rbac-proxy
VERSION?=$(shell cat VERSION)
VERSION?=$(shell cat VERSION)-$(shell git rev-parse --short HEAD)
PKGS=$(shell go list ./... | grep -v /vendor/)
DOCKER_REPO?=quay.io/brancz/kube-rbac-proxy
KUBECONFIG?=$(HOME)/.kube/config

check-license:
@echo ">> checking license headers"
Expand Down Expand Up @@ -50,7 +51,7 @@ test:
test-e2e:
go test -timeout 55m -v ./test/e2e/ $(TEST_RUN_ARGS) --kubeconfig=$(KUBECONFIG)

generate: embedmd
generate: build embedmd
@echo ">> generating examples"
@./scripts/generate-examples.sh
@echo ">> generating docs"
Expand Down
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,22 @@ All command line flags:
```txt
$ kube-rbac-proxy -h
Usage of _output/linux/amd64/kube-rbac-proxy:
--add_dir_header If true, adds the file directory to the header
Copy link
Collaborator

Choose a reason for hiding this comment

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

I am wondering if this is used at all?

Copy link
Owner Author

Choose a reason for hiding this comment

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

It’s from klog

--alsologtostderr log to standard error as well as files
--auth-header-fields-enabled When set to true, kube-rbac-proxy adds auth-related fields to the headers of http requests sent to the upstream
--auth-header-groups-field-name string The name of the field inside a http(2) request header to tell the upstream server about the user's groups (default "x-remote-groups")
--auth-header-groups-field-separator string The separator string used for concatenating multiple group names in a groups header field's value (default "|")
--auth-header-user-field-name string The name of the field inside a http(2) request header to tell the upstream server about the user's name (default "x-remote-user")
--auth-token-audiences strings Comma-separated list of token audiences to accept. By default a token does not have to have any specific audience. It is recommended to set a specific audience.
--client-ca-file string If set, any request presenting a client certificate signed by one of the authorities in the client-ca-file is authenticated with an identity corresponding to the CommonName of the client certificate.
--config-file string Configuration file to configure kube-rbac-proxy.
--insecure-listen-address string The address the kube-rbac-proxy HTTP server should listen on.
--kubeconfig string Path to a kubeconfig file, specifying how to connect to the API server. If unset, in-cluster configuration will be used
--log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0)
--log_dir string If non-empty, write log files in this directory
--logtostderr log to standard error instead of files
--log_file string If non-empty, use this log file
--log_file_max_size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800)
--logtostderr log to standard error instead of files (default true)
--oidc-ca-file string If set, the OpenID server's certificate will be verified by one of the authorities in the oidc-ca-file, otherwise the host's root CA set will be used.
--oidc-clientID string The client ID for the OpenID Connect client, must be set if oidc-issuer-url is set.
--oidc-groups-claim string Identifier of groups in JWT claim, by default set to 'groups' (default "groups")
Expand All @@ -48,15 +52,18 @@ Usage of _output/linux/amd64/kube-rbac-proxy:
--oidc-sign-alg stringArray Supported signing algorithms, default RS256 (default [RS256])
--oidc-username-claim string Identifier of the user in JWT claim, by default set to 'email' (default "email")
--secure-listen-address string The address the kube-rbac-proxy HTTPs server should listen on.
--skip_headers If true, avoid header prefixes in the log messages
--skip_log_headers If true, avoid headers when opening log files
--stderrthreshold severity logs at or above this threshold go to stderr (default 2)
--tls-cert-file string File containing the default x509 Certificate for HTTPS. (CA cert, if any, concatenated after server cert)
--tls-cipher-suites strings Comma-separated list of cipher suites for the server. Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants). If omitted, the default Go cipher suites will be used
--tls-min-version string Minimum TLS version supported. Value must match version names from https://golang.org/pkg/crypto/tls/#pkg-constants. (default "VersionTLS12")
--tls-private-key-file string File containing the default x509 private key matching --tls-cert-file.
--tls-reload-interval duration The interval at which to watch for TLS certificate changes, by default set to 1 minute. (default 1m0s)
Copy link
Collaborator

Choose a reason for hiding this comment

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

good catch 👍

Copy link
Owner Author

Choose a reason for hiding this comment

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

This actually came in as I regenerated the help text

--upstream string The upstream URL to proxy to once requests have successfully been authenticated and authorized.
--upstream-ca-file string The CA the upstream uses for TLS connection. This is required when the upstream uses TLS and its own CA certificate
--upstream-force-h2c 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
-v, --v Level log level for V logs
-v, --v Level number for the log level verbosity
--vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging
```

Expand Down
292 changes: 292 additions & 0 deletions examples/non-resource-url-token-request/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
# non-resource-url-token-request example

> Token audience reviews can be combined with any of the other features of kube-rbac-proxy, such as reviewing permission for specific resources, this example merely chooses to show the functionality using a non-resource-url permission.

This examples is in essence similar to the [non-resource-url](../non-resource-url/) example, with the key difference, that this example requires the ServiceAccount token sent by a client must be scoped to the `kube-rbac-proxy.default.svc` audience. In this example the scoped ServiceAccount token is obtained via a projected volume and mounted into the client container from where it can be consumed. The reasoning here is that scoped tokens cannot be used to impersonate an entity by re-using the token to perform a request against the Kubernetes API itself.

The audience a token must be scoped to is configured within the kube-rbac-proxy using the `--auth-token-audiences` flag.
```bash
$ kubectl create -f deployment.yaml
```

The content of this manifest is:

[embedmd]:# (./deployment.yaml)
```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: kube-rbac-proxy
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: kube-rbac-proxy
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: kube-rbac-proxy
subjects:
- kind: ServiceAccount
name: kube-rbac-proxy
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kube-rbac-proxy
rules:
- apiGroups: ["authentication.k8s.io"]
resources:
- tokenreviews
verbs: ["create"]
- apiGroups: ["authorization.k8s.io"]
resources:
- subjectaccessreviews
verbs: ["create"]
---
apiVersion: v1
kind: Service
metadata:
labels:
app: kube-rbac-proxy
name: kube-rbac-proxy
spec:
ports:
- name: https
port: 8443
targetPort: https
selector:
app: kube-rbac-proxy
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: kube-rbac-proxy
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:v0.4.1
args:
- "--secure-listen-address=0.0.0.0:8443"
- "--upstream=http://127.0.0.1:8081/"
- "--auth-token-audiences=kube-rbac-proxy.default.svc"
- "--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"
```

Once the prometheus-example-app is up and running, we can test it. In order to test it, we deploy a Job, that performs a `curl` against the above deployment. Because it has the `/metrics` path in its `nonResourceURLs` it is allowed to access the endpoint.

The Dockerfile of this container can be found [here](../example-client/Dockerfile).

```bash
$ kubectl create -f client-rbac.yaml client.yaml
```

The content of this manifest is:

[embedmd]:# (./client-rbac.yaml)
```yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: metrics
rules:
- nonResourceURLs: ["/metrics"]
verbs: ["get"]
---
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
```

[embedmd]:# (./client.yaml)
```yaml
apiVersion: batch/v1
kind: Job
metadata:
name: krp-curl
spec:
template:
metadata:
name: krp-curl
spec:
restartPolicy: Never
containers:
- name: krp-curl
image: quay.io/brancz/krp-curl:v0.0.1
command:
- /bin/sh
- -c
- 'curl -v -s -k -H "Authorization: Bearer `cat /service-account/token`" https://kube-rbac-proxy.default.svc:8443/metrics'
volumeMounts:
- name: token-vol
mountPath: "/service-account"
readOnly: true
volumes:
- name: token-vol
projected:
sources:
- serviceAccountToken:
audience: kube-rbac-proxy.default.svc
expirationSeconds: 3600
path: token
backoffLimit: 4
```

We can look at the logs and we should get something similar to:

```
* Trying 10.106.147.107...
* TCP_NODELAY set
* Connected to kube-rbac-proxy.default.svc (10.106.147.107) port 8443 (#0)
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
} [242 bytes data]
* TLSv1.2 (IN), TLS handshake, Server hello (2):
{ [49 bytes data]
* TLSv1.2 (IN), TLS handshake, Certificate (11):
{ [1649 bytes data]
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
{ [300 bytes data]
* TLSv1.2 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
} [37 bytes data]
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
} [1 bytes data]
* TLSv1.2 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
{ [1 bytes data]
* TLSv1.2 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server did not agree to a protocol
* Server certificate:
* subject: CN=kube-rbac-proxy-695b54f7ff-z54b7@1579943520
* start date: Jan 25 08:12:00 2020 GMT
* expire date: Jan 24 08:12:00 2021 GMT
* issuer: CN=kube-rbac-proxy-695b54f7ff-z54b7-ca@1579943520
* SSL certificate verify result: self signed certificate in certificate chain (19), continuing anyway.
> GET /metrics HTTP/1.1
> Host: kube-rbac-proxy.default.svc:8443
> User-Agent: curl/7.61.0
> Accept: */*
> Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6InBtUmFFQnJIUzJXS1RuR3FmcmRkRDd3TEM2NUx3STZmb29DczRNRGwzLXcifQ.eyJhdWQiOlsia3ViZS1yYmFjLXByb3h5LmRlZmF1bHQuc3ZjIl0sImV4cCI6MTU3OTk0NzYxMiwiaWF0IjoxNTc5OTQ0MDEyLCJpc3MiOiJrdWJlcm5ldGVzLmRlZmF1bHQuc3ZjIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0IiwicG9kIjp7Im5hbWUiOiJrcnAtY3VybC1nMjVweCIsInVpZCI6IjA3MWYxYzM1LWNmZWYtNGRhNy05ZjMxLWJiMmJkNmJmY2VmNyJ9LCJzZXJ2aWNlYWNjb3VudCI6eyJuYW1lIjoiZGVmYXVsdCIsInVpZCI6IjRlYzFiOWU4LTdkZmItNDhiNi1hMjU0LWFiYTg4MGJhZGY5ZiJ9fSwibmJmIjoxNTc5OTQ0MDEyLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDpkZWZhdWx0In0.fM05SSlLzb5zMpTjYhn_H8Y0KFwX000VYqE8CMA3sP5lMAbXBInGSudbm_r_ppAMUYar6cmyAiSmIAD1bOR-DlzmJdX-LA5kgA3J9GAsrWBxXMlmASo1OfTjtOuQ98Y8tM6P-BCCe5rOAcx-ppmbPE_8Pu_7IkHjABURtClr6VUspsLfqsZ9GcN5pDBSR9iFt2Cl6m8YsbBXIDJ1kp9CknFt36s5Dg7OcTQR-WWkA21iZiOqayWGphW-DqpjEdm16XpjOqqDOf2qyFisjPhxNN-rivPJaCeoRb3GUIQVbvVShEgygzdM_8OqmZT3THeHBCdC_685Ffv3hFC4G6ijAQ
>
< HTTP/1.1 200 OK
< Content-Type: text/plain; version=0.0.4
< Date: Sat, 25 Jan 2020 09:20:12 GMT
< Content-Length: 102
<
{ [102 bytes data]
* Connection #0 to host kube-rbac-proxy.default.svc left intact
# HELP version Version information about this binary
# TYPE version gauge
version{version="v0.1.0"} 0
```

Whereas if we didn't use a token that was created for the correct audience, for example the default ServiceAccount token mounted into containers, then we should not be able to authenticate with that token. This can be verified with:


[embedmd]:# (./wrong-client.yaml)
```yaml
apiVersion: batch/v1
kind: Job
metadata:
name: krp-wrong-token-curl
spec:
template:
metadata:
name: krp-wrong-token-curl
spec:
restartPolicy: Never
containers:
- name: krp-curl
image: quay.io/brancz/krp-curl:v0.0.1
```

Then the log output should look something along the lines of:

```
* Trying 10.106.147.107...
* TCP_NODELAY set
* Connected to kube-rbac-proxy.default.svc (10.106.147.107) port 8443 (#0)
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
} [242 bytes data]
* TLSv1.2 (IN), TLS handshake, Server hello (2):
{ [49 bytes data]
* TLSv1.2 (IN), TLS handshake, Certificate (11):
{ [1649 bytes data]
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
{ [300 bytes data]
* TLSv1.2 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
} [37 bytes data]
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
} [1 bytes data]
* TLSv1.2 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
{ [1 bytes data]
* TLSv1.2 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server did not agree to a protocol
* Server certificate:
* subject: CN=kube-rbac-proxy-695b54f7ff-z54b7@1579943520
* start date: Jan 25 08:12:00 2020 GMT
* expire date: Jan 24 08:12:00 2021 GMT
* issuer: CN=kube-rbac-proxy-695b54f7ff-z54b7-ca@1579943520
* SSL certificate verify result: self signed certificate in certificate chain (19), continuing anyway.
> GET /metrics HTTP/1.1
> Host: kube-rbac-proxy.default.svc:8443
> User-Agent: curl/7.61.0
> Accept: */*
> Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6InBtUmFFQnJIUzJXS1RuR3FmcmRkRDd3TEM2NUx3STZmb29DczRNRGwzLXcifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZmF1bHQtdG9rZW4tbWtienYiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVmYXVsdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjRlYzFiOWU4LTdkZmItNDhiNi1hMjU0LWFiYTg4MGJhZGY5ZiIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.dWmmPOUTpIXve3svadwPd3a1PswkL8YC5JnPvul4EqQjcf-SLNSgp-TT4I2SqUTiNqwrehmdjjZdD925Erpdb8ZnrTTcaicmO6G95IvpwfMz3EvJY7A0anjjS_IOJpwoBN3RpgftGQcuFIaOc10xa5DC9TcS1-HouoyR-FdciqEOr3ZaOhr_em3W3MLqr6IMBTALz__rObKrb7kAUPNiBfy5fUhznbp2VgQeJYQRIxGQDOnn-_5bfFjsWjQAz098SknNAwOKtdy9BpRPwyrVybQ17i15DJcAP92aSIMP7dhYvDpuSvHBg5GhHNT3y5abd_o4ZXpWpSwqSpxAHqGE5g
>
< HTTP/1.1 401 Unauthorized
< Content-Type: text/plain; charset=utf-8
< X-Content-Type-Options: nosniff
< Date: Sat, 25 Jan 2020 09:21:16 GMT
< Content-Length: 13
<
{ [13 bytes data]
* Connection #0 to host kube-rbac-proxy.default.svc left intact
Unauthorized
```
Loading