├── .VERSION ├── .gitignore ├── .version ├── Jenkinsfile ├── Makefile ├── README.md ├── assets └── web │ └── proxyinjector-round-100px.png ├── build └── package │ ├── Dockerfile.build │ └── Dockerfile.run ├── deployments └── kubernetes │ ├── chart │ └── proxyinjector │ │ ├── Chart.yaml │ │ ├── templates │ │ ├── _helpers.tpl │ │ ├── configmap.yaml │ │ ├── deployment.yaml │ │ ├── rbac.yaml │ │ └── secrets.yaml │ │ └── values.yaml │ ├── manifests │ ├── configmap.yaml │ ├── deployment.yaml │ ├── manifest.yaml │ ├── rbac.yaml │ └── secrets.yaml │ ├── proxyinjector.yaml │ └── templates │ └── chart │ ├── Chart.yaml.tmpl │ └── values.yaml.tmpl ├── docs ├── ProxyInjectorFlow.md └── images │ └── proxyinjector-flow.png ├── go.mod ├── go.sum ├── internal └── pkg │ ├── app │ └── app.go │ ├── callbacks │ └── resourceActionFuncs.go │ ├── cmd │ └── proxyinjector.go │ ├── config │ └── config.go │ ├── constants │ └── annotations.go │ ├── controller │ └── controller.go │ └── handler │ ├── create.go │ └── handler.go ├── main.go ├── pkg └── kube │ ├── client.go │ └── resourcemapper.go └── stk.yaml /.VERSION: -------------------------------------------------------------------------------- 1 | version: v0.0.21 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | _dist/ 3 | .idea 4 | golib 5 | release 6 | out/ 7 | _gopath/ 8 | .DS_Store 9 | .vscode 10 | vendor -------------------------------------------------------------------------------- /.version: -------------------------------------------------------------------------------- 1 | v0.0.23 2 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/groovy 2 | @Library('github.com/stakater/stakater-pipeline-library@v2.16.24') _ 3 | 4 | goBuildAndRelease { 5 | publicChartRepositoryURL = 'https://stakater.github.io/stakater-charts' 6 | publicChartGitURL = 'git@github.com:stakater/stakater-charts.git' 7 | toolsImage = 'stakater/pipeline-tools:v2.0.18' 8 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # note: call scripts from /scripts 2 | 3 | .PHONY: default build builder-image binary-image test stop clean-images clean push apply deploy 4 | 5 | BUILDER ?= proxyinjector-builder 6 | BINARY ?= ProxyInjector 7 | DOCKER_IMAGE ?= stakater/proxyinjector 8 | # Default value "dev" 9 | DOCKER_TAG ?= 0.0.1 10 | REPOSITORY = ${DOCKER_IMAGE}:${DOCKER_TAG} 11 | 12 | VERSION=$(shell cat .version) 13 | BUILD= 14 | 15 | GOCMD = go 16 | GOFLAGS ?= $(GOFLAGS:) 17 | LDFLAGS = 18 | 19 | default: build test 20 | 21 | install: 22 | "$(GOCMD)" mod download 23 | 24 | build: 25 | "$(GOCMD)" build ${GOFLAGS} ${LDFLAGS} -o "${BINARY}" 26 | 27 | builder-image: 28 | @docker build --network host -t "${BUILDER}" -f build/package/Dockerfile.build . 29 | 30 | binary-image: builder-image 31 | @docker run --network host --rm "${BUILDER}" | docker build --network host -t "${REPOSITORY}" -f Dockerfile.run - 32 | 33 | test: 34 | "$(GOCMD)" test -timeout 1800s -v ./... 35 | 36 | stop: 37 | @docker stop "${BINARY}" 38 | 39 | clean-images: stop 40 | @docker rmi "${BUILDER}" "${BINARY}" 41 | 42 | clean: 43 | "$(GOCMD)" clean -i 44 | 45 | push: ## push the latest Docker image to DockerHub 46 | docker push $(REPOSITORY) 47 | 48 | apply: 49 | kubectl apply -f deployments/manifests/ -n temp-proxyinjector 50 | 51 | deploy: binary-image push apply 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![](assets/web/proxyinjector-round-100px.png) Proxy Injector 2 | A Kubernetes controller to inject an authentication proxy container to relevant pods 3 | 4 | [![Get started with Stakater](https://stakater.github.io/README/stakater-github-banner.png)](http://stakater.com/?utm_source=ProxyInjector&utm_medium=github) 5 | 6 | ## Problem Statement 7 | 8 | We want to automatically inject an authentication proxy container in a pod, for any deployment that requires to connect 9 | to our SSO provider, instead of manually adding a sidecar container with each deployment 10 | 11 | ## Solution 12 | 13 | This controller will continuously watch deployments in specific or all namespaces, and automatically add a sidecar container 14 | for the authentication proxy. Configuration for the proxy is managed through annotations of the respective deployment 15 | or with ConfigMap of the ProxyInjector. 16 | 17 | ### Supported proxies 18 | 19 | For now the ProxyInjector only supports [Keycloak Gatekeeper](https://github.com/keycloak/keycloak-gatekeeper) 20 | as the authentication proxy, to work with [Keycloak Server](https://github.com/keycloak/keycloak) 21 | 22 | 23 | ## Usage 24 | 25 | The following quickstart let's you set up ProxyInjector: 26 | 27 | 1. Add configuration to the ProxyInjector 28 | The following arguments can either be added to the proxy injector `config.yaml` in the ConfigMap/Secret for centralized configuration, 29 | or as annotations on the individual target deployments with a `authproxy.stakater.com/` prefix. In case of both, 30 | the deployment annotation values will override the central configuration. 31 | 32 | | Key | Description | 33 | |------------------|---------------------------------------------------------------------------| 34 | | listen | the interface address and port the proxy should be listening on | 35 | | upstream-url | url for the upstream endpoint you wish to proxy | 36 | | resources | list of resources to proxy uri, methods, roles | 37 | | client-id | client id used to authenticate to the oauth service | 38 | | client-secret | client secret used to authenticate to the oauth service | 39 | | gatekeeper-image | Keycloak Gatekeeper image e.g. `keycloak/keycloak-gatekeeper:6.0.1` | 40 | 41 | The rest of the available options can be found at the [Keycloak Gatekeeper documentation](https://www.keycloak.org/docs/latest/securing_apps/index.html#configuration-options) 42 | 43 | Note 1: See the section `Using Secrets` below if you do not want to use ConfigMap (because `client-id` and `client-secret` in plain text) and want to use Secrets to hide them. 44 | 45 | 2. Deploy the controller by running the following command: 46 | 47 | For Kubernetes Cluster using kubectl 48 | ```bash 49 | kubectl apply -f https://raw.githubusercontent.com/stakater/ProxyInjector/master/deployments/kubernetes/proxyinjector.yaml -n default 50 | 51 | 3. When deploying any application that needs Keycloak authentication, add the following annotations to the `deployment`. The `service` will not need changes as such, all configuration can be provided as annotations in the deployment for the app. And proxy injector automatically modifies the service when injecting the sidecar container. 52 | 53 | | Key | Description | 54 | |--------------------------------------------|--------------------------------------------------------| 55 | | authproxy.stakater.com/enabled | (true/false, default=false) Enables Keycloak gatekeeper configuration | 56 | | authproxy.stakater.com/source-service-name | Name of service that needs to be reconfigured to connect to the proxy. instead of the service directly routing to the app container, it will now route to the proxy sidecar instead. | 57 | | authproxy.stakater.com/target-port | (default=80) the port on the pod where the proxy sidecar (keycloak gatekeeper) will be listening. If not specified, the default value of 80 is used. This port should match the `listen` configuration | 58 | | authproxy.stakater.com/resources | String of resources separated by `&` e.g. (`uri=/*|white-listed=true&uri=/css/*|white-listed=false|methods=GET,POST`) 59 | 60 | The `authproxy.stakater.com/listen` annotation or the `listen` property in the ProxyInjector ConfigMap should 61 | specify where the proxy sidecar will listen for incoming requests, e.g. "0.0.0.0:80" i.e. local port 80 62 | 63 | 64 | ### Using Secrets 65 | 66 | To use secrets: 67 | 68 | 1. Open [values.yaml](https://github.com/stakater/ProxyInjector/blob/master/deployments/kubernetes/chart/proxyinjector/values.yaml) file by navigating to `deployments/kubernetes/chart/proxyinjector/` 69 | 70 | 2. Set `proxyinjector.mount` equals to `"secret"` and pass the data in the data section at the bottom. 71 | 72 | 3. Run `helm template . > proxyinjector.yaml` 73 | 74 | 4. Deploy using the `Deploying` section below. 75 | 76 | To use existing Secrets: 77 | 78 | 1. Set `proxyinjector.mount` equals to `"secret"` 79 | 2. set `proxyinjector.existingSecret` equals to `EXISTING_SECRET_NAME` 80 | 81 | ### Using ConfigMap 82 | 83 | To pass user credentials/ API keys in secrets: 84 | 85 | 1. Open [values.yaml](https://github.com/stakater/ProxyInjector/blob/master/deployments/kubernetes/chart/proxyinjector/values.yaml) file by navigating to `deployments/kubernetes/chart/proxyinjector/` 86 | 87 | 2. Set `proxyinjector.mount` equals to `"configmap"` and pass the data in the data section at the bottom. 88 | 89 | 3. Run `helm template . > proxyinjector.yaml` 90 | 91 | 4. Deploy using the `Deploying` section below. 92 | 93 | ### Deploying 94 | 95 | You can deploy the controller in the namespace you want to monitor by running the following kubectl command: 96 | 97 | ```bash 98 | kubectl apply -f proxyinjector.yaml -n 99 | ``` 100 | 101 | *Note*: Before applying `proxyinjector.yaml`, You need to modify the namespace in the `RoleBinding` subjects section to the namespace you want to apply RBAC to. 102 | 103 | ## Help 104 | 105 | ### Documentation 106 | You can find more documentation [here](docs/) 107 | 108 | ### Have a question? 109 | File a GitHub [issue](https://github.com/stakater/ProxyInjector/issues), or send us an [email](mailto:hello@stakater.com). 110 | 111 | ### Talk to us on Slack 112 | Join and talk to us on the #tools-proxyinjector channel for discussing the ProxyInjector 113 | 114 | [![Join Slack](https://stakater.github.io/README/stakater-join-slack-btn.png)](https://slack.stakater.com/) 115 | [![Chat](https://stakater.github.io/README/stakater-chat-btn.png)](https://stakater-community.slack.com/messages/CFCP3MUR4) 116 | 117 | ## License 118 | 119 | Apache2 © [Stakater](http://stakater.com) 120 | 121 | ## About 122 | 123 | The `ProxyInjector` is maintained by [Stakater][website]. Like it? Please let us know at 124 | 125 | See [our other projects][community] 126 | or contact us in case of professional services and queries on 127 | 128 | [website]: http://stakater.com/ 129 | [community]: https://www.stakater.com/projects-overview.html 130 | 131 | ## Contributers 132 | 133 | Stakater Team and the Open Source community! :trophy: 134 | -------------------------------------------------------------------------------- /assets/web/proxyinjector-round-100px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stakater/ProxyInjector/7a6dd3a9fe6cda451a15c779555023ed56ac8260/assets/web/proxyinjector-round-100px.png -------------------------------------------------------------------------------- /build/package/Dockerfile.build: -------------------------------------------------------------------------------- 1 | FROM golang:1.13.1-alpine 2 | MAINTAINER "Stakater Team" 3 | 4 | RUN apk update 5 | 6 | RUN apk -v --update \ 7 | add git build-base && \ 8 | rm -rf /var/cache/apk/* && \ 9 | mkdir -p "$GOPATH/src/github.com/stakater/ProxyInjector" 10 | 11 | ADD . "$GOPATH/src/github.com/stakater/ProxyInjector" 12 | 13 | RUN cd "$GOPATH/src/github.com/stakater/ProxyInjector" && \ 14 | go mod download 15 | 16 | RUN cd "$GOPATH/src/github.com/stakater/ProxyInjector" && \ 17 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a --installsuffix cgo --ldflags="-s" -o /ProxyInjector 18 | 19 | COPY build/package/Dockerfile.run / 20 | 21 | # Running this image produces a tarball suitable to be piped into another 22 | # Docker build command. 23 | CMD tar -cf - -C / Dockerfile.run ProxyInjector 24 | -------------------------------------------------------------------------------- /build/package/Dockerfile.run: -------------------------------------------------------------------------------- 1 | FROM alpine:3.9 2 | MAINTAINER "Stakater Team" 3 | 4 | RUN apk add --update ca-certificates 5 | 6 | COPY ProxyInjector /bin/ProxyInjector 7 | 8 | ENTRYPOINT ["/bin/ProxyInjector"] 9 | -------------------------------------------------------------------------------- /deployments/kubernetes/chart/proxyinjector/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | name: proxyinjector 3 | description: ProxyInjector chart that runs on kubernetes 4 | version: v0.0.23 5 | keywords: 6 | - Proxy 7 | - Authentication 8 | - kubernetes 9 | home: https://github.com/stakater/ProxyInjector 10 | maintainers: 11 | - name: Stakater 12 | email: hello@stakater.com -------------------------------------------------------------------------------- /deployments/kubernetes/chart/proxyinjector/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "proxyinjector-name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" | lower -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | */}} 13 | {{- define "proxyinjector-fullname" -}} 14 | {{- $name := default .Chart.Name .Values.nameOverride -}} 15 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 16 | {{- end -}} 17 | 18 | {{- define "proxyinjector-labels.selector" -}} 19 | app: {{ template "proxyinjector-name" . }} 20 | group: {{ .Values.proxyinjector.labels.group }} 21 | provider: {{ .Values.proxyinjector.labels.provider }} 22 | {{- end -}} 23 | 24 | {{- define "proxyinjector-labels.stakater" -}} 25 | {{ template "proxyinjector-labels.selector" . }} 26 | version: {{ .Values.proxyinjector.labels.version }} 27 | {{- end -}} 28 | 29 | {{- define "proxyinjector-labels.chart" -}} 30 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 31 | release: {{ .Release.Name | quote }} 32 | heritage: {{ .Release.Service | quote }} 33 | {{- end -}} 34 | 35 | {{- define "proxyinjector-vol-config-name" -}} 36 | {{- if .Values.proxyinjector.existingSecret -}} 37 | {{ .Values.proxyinjector.existingSecret }} 38 | {{- else -}} 39 | {{- template "proxyinjector-name" . -}} 40 | {{- end -}} 41 | {{- end -}} -------------------------------------------------------------------------------- /deployments/kubernetes/chart/proxyinjector/templates/configmap.yaml: -------------------------------------------------------------------------------- 1 | {{- if eq .Values.proxyinjector.mount "configmap" }} 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | labels: 6 | app: {{ template "proxyinjector-name" . }} 7 | version: {{ .Chart.Version }} 8 | group: {{ .Values.proxyinjector.labels.group }} 9 | provider: {{ .Values.proxyinjector.labels.provider }} 10 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 11 | release: {{ .Release.Name | quote }} 12 | heritage: {{ .Release.Service | quote }} 13 | name: {{ template "proxyinjector-name" . }} 14 | data: 15 | {{ toYaml .Values.proxyinjector.data | indent 2 }} 16 | {{- end }} -------------------------------------------------------------------------------- /deployments/kubernetes/chart/proxyinjector/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | {{ include "proxyinjector-labels.stakater" . | indent 4 }} 6 | {{ include "proxyinjector-labels.chart" . | indent 4 }} 7 | name: {{ template "proxyinjector-name" . }} 8 | namespace: {{ .Release.Namespace }} 9 | spec: 10 | replicas: 1 11 | revisionHistoryLimit: 2 12 | selector: 13 | matchLabels: 14 | {{ include "proxyinjector-labels.selector" . | indent 6 }} 15 | template: 16 | metadata: 17 | labels: 18 | {{ include "proxyinjector-labels.selector" . | indent 8 }} 19 | spec: 20 | {{- if .Values.proxyinjector.tolerations }} 21 | tolerations: 22 | {{ toYaml .Values.proxyinjector.tolerations | indent 8 -}} 23 | {{- end }} 24 | containers: 25 | - env: 26 | - name: CONFIG_FILE_PATH 27 | value: "{{ .Values.proxyinjector.configFilePath }}/config.yml" 28 | {{- if eq .Values.proxyinjector.watchGlobally false }} 29 | - name: KUBERNETES_NAMESPACE 30 | valueFrom: 31 | fieldRef: 32 | fieldPath: metadata.namespace 33 | {{- end }} 34 | image: "{{ .Values.proxyinjector.image.name }}:{{ .Values.proxyinjector.image.tag }}" 35 | imagePullPolicy: {{ .Values.proxyinjector.image.pullPolicy }} 36 | name: {{ template "proxyinjector-name" . }} 37 | volumeMounts: 38 | - mountPath: {{ .Values.proxyinjector.configFilePath }} 39 | name: config-volume 40 | serviceAccountName: {{ template "proxyinjector-name" . }} 41 | volumes: 42 | {{- if eq .Values.proxyinjector.mount "secret" }} 43 | - secret: 44 | secretName: {{ template "proxyinjector-vol-config-name" . }} 45 | {{- else }} 46 | - configMap: 47 | name: {{ template "proxyinjector-name" . }} 48 | {{- end }} 49 | name: config-volume 50 | -------------------------------------------------------------------------------- /deployments/kubernetes/chart/proxyinjector/templates/rbac.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | labels: 5 | {{ include "proxyinjector-labels.stakater" . | indent 4 }} 6 | {{ include "proxyinjector-labels.chart" . | indent 4 }} 7 | name: {{ template "proxyinjector-name" . }} 8 | --- 9 | {{- if .Values.proxyinjector.watchGlobally }} 10 | apiVersion: rbac.authorization.k8s.io/v1 11 | kind: ClusterRole 12 | metadata: 13 | labels: 14 | {{ include "proxyinjector-labels.stakater" . | indent 4 }} 15 | {{ include "proxyinjector-labels.chart" . | indent 4 }} 16 | name: {{ template "proxyinjector-name" . }}-role 17 | namespace: {{ .Release.Namespace }} 18 | rules: 19 | - apiGroups: 20 | - "" 21 | - "extensions" 22 | - "apps" 23 | resources: 24 | - deployments 25 | - daemonsets 26 | - statefulsets 27 | - services 28 | - configmaps 29 | verbs: 30 | - list 31 | - get 32 | - watch 33 | - update 34 | - create 35 | - patch 36 | --- 37 | apiVersion: rbac.authorization.k8s.io/v1 38 | kind: ClusterRoleBinding 39 | metadata: 40 | labels: 41 | {{ include "proxyinjector-labels.stakater" . | indent 4 }} 42 | {{ include "proxyinjector-labels.chart" . | indent 4 }} 43 | name: {{ template "proxyinjector-name" . }}-role-binding 44 | namespace: {{ .Release.Namespace }} 45 | roleRef: 46 | apiGroup: rbac.authorization.k8s.io 47 | kind: ClusterRole 48 | name: {{ template "proxyinjector-name" . }}-role 49 | subjects: 50 | - kind: ServiceAccount 51 | name: {{ template "proxyinjector-name" . }} 52 | namespace: {{ .Release.Namespace }} 53 | {{- else }} 54 | apiVersion: rbac.authorization.k8s.io/v1 55 | kind: Role 56 | metadata: 57 | labels: 58 | {{ include "proxyinjector-labels.stakater" . | indent 4 }} 59 | {{ include "proxyinjector-labels.chart" . | indent 4 }} 60 | name: {{ template "proxyinjector-name" . }}-role 61 | namespace: {{ .Release.Namespace }} 62 | rules: 63 | - apiGroups: 64 | - "" 65 | - "extensions" 66 | - "apps" 67 | resources: 68 | - deployments 69 | - daemonsets 70 | - statefulsets 71 | - services 72 | - configmaps 73 | verbs: 74 | - list 75 | - get 76 | - watch 77 | - update 78 | - create 79 | - patch 80 | --- 81 | apiVersion: rbac.authorization.k8s.io/v1 82 | kind: RoleBinding 83 | metadata: 84 | labels: 85 | {{ include "proxyinjector-labels.stakater" . | indent 4 }} 86 | {{ include "proxyinjector-labels.chart" . | indent 4 }} 87 | name: {{ template "proxyinjector-name" . }}-role-binding 88 | namespace: {{ .Release.Namespace }} 89 | roleRef: 90 | apiGroup: rbac.authorization.k8s.io 91 | kind: Role 92 | name: {{ template "proxyinjector-name" . }}-role 93 | subjects: 94 | - kind: ServiceAccount 95 | name: {{ template "proxyinjector-name" . }} 96 | namespace: {{ .Release.Namespace }} 97 | {{- end }} 98 | -------------------------------------------------------------------------------- /deployments/kubernetes/chart/proxyinjector/templates/secrets.yaml: -------------------------------------------------------------------------------- 1 | {{- if eq .Values.proxyinjector.mount "secret" }} 2 | apiVersion: v1 3 | kind: Secret 4 | type: Opaque 5 | metadata: 6 | labels: 7 | app: {{ template "proxyinjector-name" . }} 8 | version: {{ .Chart.Version }} 9 | group: {{ .Values.proxyinjector.labels.group }} 10 | provider: {{ .Values.proxyinjector.labels.provider }} 11 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 12 | release: {{ .Release.Name | quote }} 13 | heritage: {{ .Release.Service | quote }} 14 | name: {{ template "proxyinjector-name" . }} 15 | data: 16 | {{- range $key, $value := .Values.proxyinjector.data }} 17 | {{ $key }}: {{ $value | b64enc }} 18 | {{- end }} 19 | {{- end }} -------------------------------------------------------------------------------- /deployments/kubernetes/chart/proxyinjector/values.yaml: -------------------------------------------------------------------------------- 1 | kubernetes: 2 | host: https://kubernetes.default 3 | 4 | proxyinjector: 5 | mount: "configmap" 6 | existingSecret: "" 7 | tolerations: {} 8 | labels: 9 | provider: stakater 10 | group: com.stakater.platform 11 | version: v0.0.23 12 | image: 13 | name: stakater/proxyinjector 14 | tag: "v0.0.23" 15 | pullPolicy: IfNotPresent 16 | watchGlobally: true 17 | configFilePath: /etc/ProxyInjector 18 | data: 19 | config.yml: |- 20 | proxyconfig: 21 | gatekeeper-image : "keycloak/keycloak-gatekeeper:6.0.1" 22 | enable-default-deny: true 23 | secure-cookie: false 24 | verbose: true 25 | enable-logging: true 26 | cors-origins: 27 | - '*' 28 | cors-methods: 29 | - GET 30 | - POST 31 | resources: 32 | - uri: '/*' 33 | scopes: 34 | - 'good-service' -------------------------------------------------------------------------------- /deployments/kubernetes/manifests/configmap.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Source: proxyinjector/templates/configmap.yaml 3 | 4 | apiVersion: v1 5 | kind: ConfigMap 6 | metadata: 7 | labels: 8 | app: proxyinjector 9 | version: v0.0.23 10 | group: com.stakater.platform 11 | provider: stakater 12 | chart: "proxyinjector-v0.0.23" 13 | release: "proxyinjector" 14 | heritage: "Tiller" 15 | name: proxyinjector 16 | data: 17 | config.yml: |- 18 | proxyconfig: 19 | gatekeeper-image : "keycloak/keycloak-gatekeeper:6.0.1" 20 | enable-default-deny: true 21 | secure-cookie: false 22 | verbose: true 23 | enable-logging: true 24 | cors-origins: 25 | - '*' 26 | cors-methods: 27 | - GET 28 | - POST 29 | resources: 30 | - uri: '/*' 31 | scopes: 32 | - 'good-service' 33 | 34 | -------------------------------------------------------------------------------- /deployments/kubernetes/manifests/deployment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Source: proxyinjector/templates/deployment.yaml 3 | apiVersion: apps/v1 4 | kind: Deployment 5 | metadata: 6 | labels: 7 | app: proxyinjector 8 | group: com.stakater.platform 9 | provider: stakater 10 | version: v0.0.23 11 | chart: "proxyinjector-v0.0.23" 12 | release: "proxyinjector" 13 | heritage: "Tiller" 14 | name: proxyinjector 15 | namespace: default 16 | spec: 17 | replicas: 1 18 | revisionHistoryLimit: 2 19 | selector: 20 | matchLabels: 21 | app: proxyinjector 22 | group: com.stakater.platform 23 | provider: stakater 24 | template: 25 | metadata: 26 | labels: 27 | app: proxyinjector 28 | group: com.stakater.platform 29 | provider: stakater 30 | spec: 31 | containers: 32 | - env: 33 | - name: CONFIG_FILE_PATH 34 | value: "/etc/ProxyInjector/config.yml" 35 | image: "stakater/proxyinjector:v0.0.23" 36 | imagePullPolicy: IfNotPresent 37 | name: proxyinjector 38 | volumeMounts: 39 | - mountPath: /etc/ProxyInjector 40 | name: config-volume 41 | serviceAccountName: proxyinjector 42 | volumes: 43 | - configMap: 44 | name: proxyinjector 45 | name: config-volume 46 | 47 | -------------------------------------------------------------------------------- /deployments/kubernetes/manifests/manifest.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Source: proxyinjector/templates/rbac.yaml 3 | apiVersion: v1 4 | kind: ServiceAccount 5 | metadata: 6 | labels: 7 | app: proxyinjector 8 | group: com.stakater.platform 9 | provider: stakater 10 | version: 0.0.1 11 | chart: "proxyinjector-0.0.1" 12 | release: "RELEASE-NAME" 13 | heritage: "Tiller" 14 | name: proxyinjector 15 | --- 16 | apiVersion: rbac.authorization.k8s.io/v1 17 | kind: ClusterRole 18 | metadata: 19 | labels: 20 | app: proxyinjector 21 | group: com.stakater.platform 22 | provider: stakater 23 | version: 0.0.1 24 | chart: "proxyinjector-0.0.1" 25 | release: "RELEASE-NAME" 26 | heritage: "Tiller" 27 | name: proxyinjector-role 28 | namespace: test-proxyinjector 29 | rules: 30 | - apiGroups: 31 | - "" 32 | - "extensions" 33 | - "apps" 34 | resources: 35 | - deployments 36 | - daemonsets 37 | - statefulsets 38 | - services 39 | verbs: 40 | - list 41 | - get 42 | - watch 43 | - update 44 | - patch 45 | --- 46 | apiVersion: rbac.authorization.k8s.io/v1 47 | kind: ClusterRoleBinding 48 | metadata: 49 | labels: 50 | app: proxyinjector 51 | group: com.stakater.platform 52 | provider: stakater 53 | version: 0.0.1 54 | chart: "proxyinjector-0.0.1" 55 | release: "RELEASE-NAME" 56 | heritage: "Tiller" 57 | name: proxyinjector-role-binding 58 | namespace: test-proxyinjector 59 | roleRef: 60 | apiGroup: rbac.authorization.k8s.io 61 | kind: ClusterRole 62 | name: proxyinjector-role 63 | subjects: 64 | - kind: ServiceAccount 65 | name: proxyinjector 66 | namespace: test-proxyinjector 67 | 68 | 69 | 70 | 71 | --- 72 | # Source: proxyinjector/templates/deployment.yaml 73 | apiVersion: apps/v1 74 | kind: Deployment 75 | metadata: 76 | labels: 77 | app: proxyinjector 78 | group: com.stakater.platform 79 | provider: stakater 80 | version: 0.0.1 81 | chart: "proxyinjector-0.0.1" 82 | release: "RELEASE-NAME" 83 | heritage: "Tiller" 84 | name: proxyinjector 85 | spec: 86 | replicas: 1 87 | revisionHistoryLimit: 2 88 | selector: 89 | matchLabels: 90 | app: proxyinjector 91 | group: com.stakater.platform 92 | provider: stakater 93 | template: 94 | metadata: 95 | labels: 96 | app: proxyinjector 97 | group: com.stakater.platform 98 | provider: stakater 99 | spec: 100 | containers: 101 | - env: 102 | image: "stakater/proxyinjector:0.0.1" 103 | imagePullPolicy: Always 104 | name: proxyinjector 105 | serviceAccountName: proxyinjector 106 | 107 | -------------------------------------------------------------------------------- /deployments/kubernetes/manifests/rbac.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Source: proxyinjector/templates/rbac.yaml 3 | apiVersion: v1 4 | kind: ServiceAccount 5 | metadata: 6 | labels: 7 | app: proxyinjector 8 | group: com.stakater.platform 9 | provider: stakater 10 | version: v0.0.23 11 | chart: "proxyinjector-v0.0.23" 12 | release: "proxyinjector" 13 | heritage: "Tiller" 14 | name: proxyinjector 15 | --- 16 | apiVersion: rbac.authorization.k8s.io/v1 17 | kind: ClusterRole 18 | metadata: 19 | labels: 20 | app: proxyinjector 21 | group: com.stakater.platform 22 | provider: stakater 23 | version: v0.0.23 24 | chart: "proxyinjector-v0.0.23" 25 | release: "proxyinjector" 26 | heritage: "Tiller" 27 | name: proxyinjector-role 28 | namespace: default 29 | rules: 30 | - apiGroups: 31 | - "" 32 | - "extensions" 33 | - "apps" 34 | resources: 35 | - deployments 36 | - daemonsets 37 | - statefulsets 38 | - services 39 | - configmaps 40 | verbs: 41 | - list 42 | - get 43 | - watch 44 | - update 45 | - create 46 | - patch 47 | --- 48 | apiVersion: rbac.authorization.k8s.io/v1 49 | kind: ClusterRoleBinding 50 | metadata: 51 | labels: 52 | app: proxyinjector 53 | group: com.stakater.platform 54 | provider: stakater 55 | version: v0.0.23 56 | chart: "proxyinjector-v0.0.23" 57 | release: "proxyinjector" 58 | heritage: "Tiller" 59 | name: proxyinjector-role-binding 60 | namespace: default 61 | roleRef: 62 | apiGroup: rbac.authorization.k8s.io 63 | kind: ClusterRole 64 | name: proxyinjector-role 65 | subjects: 66 | - kind: ServiceAccount 67 | name: proxyinjector 68 | namespace: default 69 | 70 | -------------------------------------------------------------------------------- /deployments/kubernetes/manifests/secrets.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Source: proxyinjector/templates/secrets.yaml 3 | 4 | -------------------------------------------------------------------------------- /deployments/kubernetes/proxyinjector.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Source: proxyinjector/templates/rbac.yaml 3 | apiVersion: v1 4 | kind: ServiceAccount 5 | metadata: 6 | labels: 7 | app: proxyinjector 8 | group: com.stakater.platform 9 | provider: stakater 10 | version: 0.0.1 11 | chart: "proxyinjector-0.0.1" 12 | release: "RELEASE-NAME" 13 | heritage: "Tiller" 14 | name: proxyinjector 15 | --- 16 | apiVersion: rbac.authorization.k8s.io/v1 17 | kind: ClusterRole 18 | metadata: 19 | labels: 20 | app: proxyinjector 21 | group: com.stakater.platform 22 | provider: stakater 23 | version: 0.0.1 24 | chart: "proxyinjector-0.0.1" 25 | release: "RELEASE-NAME" 26 | heritage: "Tiller" 27 | name: proxyinjector-role 28 | namespace: test-proxyinjector 29 | rules: 30 | - apiGroups: 31 | - "" 32 | - "extensions" 33 | - "apps" 34 | resources: 35 | - deployments 36 | - daemonsets 37 | - statefulsets 38 | - services 39 | verbs: 40 | - list 41 | - get 42 | - watch 43 | - update 44 | - patch 45 | --- 46 | apiVersion: rbac.authorization.k8s.io/v1 47 | kind: ClusterRoleBinding 48 | metadata: 49 | labels: 50 | app: proxyinjector 51 | group: com.stakater.platform 52 | provider: stakater 53 | version: 0.0.1 54 | chart: "proxyinjector-0.0.1" 55 | release: "RELEASE-NAME" 56 | heritage: "Tiller" 57 | name: proxyinjector-role-binding 58 | namespace: test-proxyinjector 59 | roleRef: 60 | apiGroup: rbac.authorization.k8s.io 61 | kind: ClusterRole 62 | name: proxyinjector-role 63 | subjects: 64 | - kind: ServiceAccount 65 | name: proxyinjector 66 | namespace: test-proxyinjector 67 | 68 | 69 | 70 | 71 | --- 72 | # Source: proxyinjector/templates/deployment.yaml 73 | apiVersion: apps/v1 74 | kind: Deployment 75 | metadata: 76 | labels: 77 | app: proxyinjector 78 | group: com.stakater.platform 79 | provider: stakater 80 | version: 0.0.1 81 | chart: "proxyinjector-0.0.1" 82 | release: "RELEASE-NAME" 83 | heritage: "Tiller" 84 | name: proxyinjector 85 | spec: 86 | replicas: 1 87 | revisionHistoryLimit: 2 88 | selector: 89 | matchLabels: 90 | app: proxyinjector 91 | group: com.stakater.platform 92 | provider: stakater 93 | template: 94 | metadata: 95 | labels: 96 | app: proxyinjector 97 | group: com.stakater.platform 98 | provider: stakater 99 | spec: 100 | containers: 101 | - env: 102 | image: "stakater/proxyinjector:0.0.1" 103 | imagePullPolicy: Always 104 | name: proxyinjector 105 | serviceAccountName: proxyinjector 106 | 107 | --- 108 | # Source: proxyinjector/templates/deployment.yaml 109 | apiVersion: apps/v1 110 | kind: Deployment 111 | metadata: 112 | labels: 113 | app: proxyinjector 114 | group: com.stakater.platform 115 | provider: stakater 116 | version: v0.0.23 117 | chart: "proxyinjector-v0.0.23" 118 | release: "proxyinjector" 119 | heritage: "Tiller" 120 | name: proxyinjector 121 | namespace: default 122 | spec: 123 | replicas: 1 124 | revisionHistoryLimit: 2 125 | selector: 126 | matchLabels: 127 | app: proxyinjector 128 | group: com.stakater.platform 129 | provider: stakater 130 | template: 131 | metadata: 132 | labels: 133 | app: proxyinjector 134 | group: com.stakater.platform 135 | provider: stakater 136 | spec: 137 | containers: 138 | - env: 139 | - name: CONFIG_FILE_PATH 140 | value: "/etc/ProxyInjector/config.yml" 141 | image: "stakater/proxyinjector:v0.0.23" 142 | imagePullPolicy: IfNotPresent 143 | name: proxyinjector 144 | volumeMounts: 145 | - mountPath: /etc/ProxyInjector 146 | name: config-volume 147 | serviceAccountName: proxyinjector 148 | volumes: 149 | - configMap: 150 | name: proxyinjector 151 | name: config-volume 152 | 153 | --- 154 | # Source: proxyinjector/templates/rbac.yaml 155 | apiVersion: v1 156 | kind: ServiceAccount 157 | metadata: 158 | labels: 159 | app: proxyinjector 160 | group: com.stakater.platform 161 | provider: stakater 162 | version: v0.0.23 163 | chart: "proxyinjector-v0.0.23" 164 | release: "proxyinjector" 165 | heritage: "Tiller" 166 | name: proxyinjector 167 | --- 168 | apiVersion: rbac.authorization.k8s.io/v1 169 | kind: ClusterRole 170 | metadata: 171 | labels: 172 | app: proxyinjector 173 | group: com.stakater.platform 174 | provider: stakater 175 | version: v0.0.23 176 | chart: "proxyinjector-v0.0.23" 177 | release: "proxyinjector" 178 | heritage: "Tiller" 179 | name: proxyinjector-role 180 | namespace: default 181 | rules: 182 | - apiGroups: 183 | - "" 184 | - "extensions" 185 | - "apps" 186 | resources: 187 | - deployments 188 | - daemonsets 189 | - statefulsets 190 | - services 191 | - configmaps 192 | verbs: 193 | - list 194 | - get 195 | - watch 196 | - update 197 | - create 198 | - patch 199 | --- 200 | apiVersion: rbac.authorization.k8s.io/v1 201 | kind: ClusterRoleBinding 202 | metadata: 203 | labels: 204 | app: proxyinjector 205 | group: com.stakater.platform 206 | provider: stakater 207 | version: v0.0.23 208 | chart: "proxyinjector-v0.0.23" 209 | release: "proxyinjector" 210 | heritage: "Tiller" 211 | name: proxyinjector-role-binding 212 | namespace: default 213 | roleRef: 214 | apiGroup: rbac.authorization.k8s.io 215 | kind: ClusterRole 216 | name: proxyinjector-role 217 | subjects: 218 | - kind: ServiceAccount 219 | name: proxyinjector 220 | namespace: default 221 | 222 | --- 223 | # Source: proxyinjector/templates/secrets.yaml 224 | 225 | --- 226 | # Source: proxyinjector/templates/configmap.yaml 227 | 228 | apiVersion: v1 229 | kind: ConfigMap 230 | metadata: 231 | labels: 232 | app: proxyinjector 233 | version: v0.0.23 234 | group: com.stakater.platform 235 | provider: stakater 236 | chart: "proxyinjector-v0.0.23" 237 | release: "proxyinjector" 238 | heritage: "Tiller" 239 | name: proxyinjector 240 | data: 241 | config.yml: |- 242 | proxyconfig: 243 | gatekeeper-image : "keycloak/keycloak-gatekeeper:6.0.1" 244 | enable-default-deny: true 245 | secure-cookie: false 246 | verbose: true 247 | enable-logging: true 248 | cors-origins: 249 | - '*' 250 | cors-methods: 251 | - GET 252 | - POST 253 | resources: 254 | - uri: '/*' 255 | scopes: 256 | - 'good-service' 257 | 258 | -------------------------------------------------------------------------------- /deployments/kubernetes/templates/chart/Chart.yaml.tmpl: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | name: proxyinjector 3 | description: ProxyInjector chart that runs on kubernetes 4 | version: {{ getenv "VERSION" }} 5 | keywords: 6 | - Proxy 7 | - Authentication 8 | - kubernetes 9 | home: https://github.com/stakater/ProxyInjector 10 | maintainers: 11 | - name: Stakater 12 | email: hello@stakater.com -------------------------------------------------------------------------------- /deployments/kubernetes/templates/chart/values.yaml.tmpl: -------------------------------------------------------------------------------- 1 | kubernetes: 2 | host: https://kubernetes.default 3 | 4 | proxyinjector: 5 | mount: "configmap" 6 | existingSecret: "" 7 | tolerations: {} 8 | labels: 9 | provider: stakater 10 | group: com.stakater.platform 11 | version: {{ getenv "VERSION" }} 12 | image: 13 | name: {{ getenv "DOCKER_IMAGE" }} 14 | tag: "{{ getenv "VERSION" }}" 15 | pullPolicy: IfNotPresent 16 | watchGlobally: true 17 | configFilePath: /etc/ProxyInjector 18 | data: 19 | config.yml: |- 20 | proxyconfig: 21 | gatekeeper-image : "keycloak/keycloak-gatekeeper:6.0.1" 22 | enable-default-deny: true 23 | secure-cookie: false 24 | verbose: true 25 | enable-logging: true 26 | cors-origins: 27 | - '*' 28 | cors-methods: 29 | - GET 30 | - POST 31 | resources: 32 | - uri: '/*' 33 | scopes: 34 | - 'good-service' -------------------------------------------------------------------------------- /docs/ProxyInjectorFlow.md: -------------------------------------------------------------------------------- 1 | # ProxyInjector High Level Flow 2 | 3 | ![](./images/proxyinjector-flow.png) -------------------------------------------------------------------------------- /docs/images/proxyinjector-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stakater/ProxyInjector/7a6dd3a9fe6cda451a15c779555023ed56ac8260/docs/images/proxyinjector-flow.png -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/stakater/ProxyInjector 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/imdario/mergo v0.3.8 // indirect 7 | github.com/sirupsen/logrus v1.4.2 8 | github.com/spf13/cobra v0.0.5 9 | golang.org/x/oauth2 v0.0.0-20191122200657-5d9234df094c // indirect 10 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect 11 | gopkg.in/yaml.v2 v2.2.7 12 | k8s.io/api v0.0.0-20191121015604-11707872ac1c 13 | k8s.io/apimachinery v0.0.0-20191123233150-4c4803ed55e3 14 | k8s.io/client-go v11.0.0+incompatible 15 | k8s.io/utils v0.0.0-20191114200735-6ca3b61696b6 // indirect 16 | ) 17 | 18 | replace ( 19 | k8s.io/api => k8s.io/api v0.0.0-20191004120104-195af9ec3521 // release-1.16 20 | k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20191004115801-a2eda9f80ab8 // kubernetes-1.16.0 21 | k8s.io/client-go => k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90 // kubernetes-1.16.0 22 | ) 23 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= 5 | github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= 6 | github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= 7 | github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= 8 | github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= 9 | github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= 10 | github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= 11 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 12 | github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= 13 | github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 14 | github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= 15 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 16 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 17 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 18 | github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= 19 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 20 | github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= 21 | github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 22 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 23 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 24 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 25 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 26 | github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= 27 | github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= 28 | github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= 29 | github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= 30 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 31 | github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 32 | github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= 33 | github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= 34 | github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= 35 | github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= 36 | github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= 37 | github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I= 38 | github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= 39 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 40 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 41 | github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 42 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 43 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 44 | github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 45 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 46 | github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= 47 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 48 | github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= 49 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 50 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 51 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 52 | github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= 53 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 54 | github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= 55 | github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= 56 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 57 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 58 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 59 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 60 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 61 | github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k= 62 | github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= 63 | github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= 64 | github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= 65 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 66 | github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= 67 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 68 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 69 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 70 | github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= 71 | github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= 72 | github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= 73 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 74 | github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 75 | github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= 76 | github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 77 | github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= 78 | github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 79 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 80 | github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 81 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 82 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 83 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 84 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 85 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 86 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 87 | github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 88 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 89 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 90 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 91 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 92 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 93 | github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 94 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 95 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= 96 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 97 | github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 98 | github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= 99 | github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 100 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 101 | github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 102 | github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 103 | github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= 104 | github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 105 | github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 106 | github.com/openshift/api v3.9.0+incompatible h1:fJ/KsefYuZAjmrr3+5U9yZIZbTOpVkDDLDLFresAeYs= 107 | github.com/openshift/api v3.9.0+incompatible/go.mod h1:dh9o4Fs58gpFXGSYfnVxGR9PnV53I8TW84pQaJDdGiY= 108 | github.com/openshift/api v3.9.1-0.20190923092516-169848dd8137+incompatible h1:vfxB7vqajc9CkO9e68Aj4kt7qzuKsOBcLJcEnSzNrM0= 109 | github.com/openshift/api v3.9.1-0.20190923092516-169848dd8137+incompatible/go.mod h1:dh9o4Fs58gpFXGSYfnVxGR9PnV53I8TW84pQaJDdGiY= 110 | github.com/openshift/client-go v0.0.0-20190923092832-6afefc9bb372 h1:iOFI/ua5QKKEHTy8ohWdY1kgWpEmik8nw5ZCESx0Crg= 111 | github.com/openshift/client-go v0.0.0-20190923092832-6afefc9bb372/go.mod h1:6rzn+JTr7+WYS2E1TExP4gByoABxMznR6y2SnUIkmxk= 112 | github.com/openshift/client-go v3.9.0+incompatible h1:13k3Ok0B7TA2hA3bQW2aFqn6y04JaJWdk7ITTyg+Ek0= 113 | github.com/openshift/client-go v3.9.0+incompatible/go.mod h1:6rzn+JTr7+WYS2E1TExP4gByoABxMznR6y2SnUIkmxk= 114 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 115 | github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= 116 | github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 117 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 118 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 119 | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= 120 | github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= 121 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 122 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 123 | github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= 124 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 125 | github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= 126 | github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= 127 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 128 | github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 129 | github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= 130 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 131 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 132 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 133 | github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= 134 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 135 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 136 | github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 137 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 138 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 139 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 140 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 141 | github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= 142 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 143 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 144 | golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 145 | golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 146 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= 147 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 148 | golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU= 149 | golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 150 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 151 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 152 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 153 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 154 | golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 155 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 156 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 157 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 158 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 159 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 160 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 161 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 162 | golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc h1:gkKoSkUmnU6bpS/VhkuO27bzQeSA51uaEfbOW5dNb68= 163 | golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 164 | golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI= 165 | golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 166 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 167 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 168 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 169 | golang.org/x/oauth2 v0.0.0-20191122200657-5d9234df094c h1:HjRaKPaiWks0f5tA6ELVF7ZfqSppfPwOEEAvsrKUTO4= 170 | golang.org/x/oauth2 v0.0.0-20191122200657-5d9234df094c/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 171 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 172 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 173 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 174 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 175 | golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 176 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 177 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 178 | golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 179 | golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 180 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 181 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 182 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 183 | golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f h1:25KHgbfyiSm6vwQLbM3zZIe1v9p/3ea4Rz+nnM5K/i4= 184 | golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 185 | golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456 h1:ng0gs1AKnRRuEMZoTLLlbOd+C17zUDepwGQBb/n+JVg= 186 | golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 187 | golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 188 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 189 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 190 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 191 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 192 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 193 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= 194 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 195 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 196 | golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 197 | golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 198 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 199 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 200 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 201 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 202 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 203 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 204 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 205 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 206 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 207 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 208 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 209 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 210 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 211 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 212 | gopkg.in/inf.v0 v0.9.0 h1:3zYtXIO92bvsdS3ggAdA8Gb4Azj0YU+TVY1uGYNFA8o= 213 | gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 214 | gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= 215 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 216 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 217 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 218 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 219 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 220 | gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= 221 | gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 222 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 223 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 224 | k8s.io/api v0.0.0-20191004120104-195af9ec3521 h1:StP5An9aFEWfPckudvHEJc4q/WUDCUVGoZJrE/efGME= 225 | k8s.io/api v0.0.0-20191004120104-195af9ec3521/go.mod h1:/L5qH+AD540e7Cetbui1tuJeXdmNhO8jM6VkXeDdDhQ= 226 | k8s.io/api v0.0.0-20191121015604-11707872ac1c h1:Z87my3sF4WhG0OMxzARkWY/IKBtOr+MhXZAb4ts6qFc= 227 | k8s.io/api v0.0.0-20191121015604-11707872ac1c/go.mod h1:R/s4gKT0V/cWEnbQa9taNRJNbWUK57/Dx6cPj6MD3A0= 228 | k8s.io/apimachinery v0.0.0-20191004115801-a2eda9f80ab8 h1:Iieh/ZEgT3BWwbLD5qEKcY06jKuPEl6zC7gPSehoLw4= 229 | k8s.io/apimachinery v0.0.0-20191004115801-a2eda9f80ab8/go.mod h1:llRdnznGEAqC3DcNm6yEj472xaFVfLM7hnYofMb12tQ= 230 | k8s.io/apimachinery v0.0.0-20191121015412-41065c7a8c2a/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= 231 | k8s.io/apimachinery v0.0.0-20191123233150-4c4803ed55e3 h1:FErmbNIJruD5GT2oVEjtPn5Ar5+rcWJsC8/PPUkR0s4= 232 | k8s.io/apimachinery v0.0.0-20191123233150-4c4803ed55e3/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= 233 | k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90 h1:mLmhKUm1X+pXu0zXMEzNsOF5E2kKFGe5o6BZBIIqA6A= 234 | k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90/go.mod h1:J69/JveO6XESwVgG53q3Uz5OSfgsv4uxpScmmyYOOlk= 235 | k8s.io/client-go v11.0.0+incompatible h1:LBbX2+lOwY9flffWlJM7f1Ct8V2SRNiMRDFeiwnJo9o= 236 | k8s.io/client-go v11.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= 237 | k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= 238 | k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= 239 | k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= 240 | k8s.io/klog v0.4.0 h1:lCJCxf/LIowc2IGS9TPjWDyXY4nOmdGdfcwwDQCOURQ= 241 | k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= 242 | k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= 243 | k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= 244 | k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= 245 | k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= 246 | k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= 247 | k8s.io/utils v0.0.0-20191114200735-6ca3b61696b6 h1:p0Ai3qVtkbCG/Af26dBmU0E1W58NID3hSSh7cMyylpM= 248 | k8s.io/utils v0.0.0-20191114200735-6ca3b61696b6/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= 249 | sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= 250 | sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= 251 | sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= 252 | -------------------------------------------------------------------------------- /internal/pkg/app/app.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import "github.com/stakater/ProxyInjector/internal/pkg/cmd" 4 | 5 | // Run runs the command 6 | func Run() error { 7 | cmd := cmd.NewProxyInjectorCommand() 8 | return cmd.Execute() 9 | } 10 | -------------------------------------------------------------------------------- /internal/pkg/callbacks/resourceActionFuncs.go: -------------------------------------------------------------------------------- 1 | package callbacks 2 | 3 | import ( 4 | apps "k8s.io/api/apps/v1" 5 | ext "k8s.io/api/apps/v1" 6 | ) 7 | 8 | func GetDeploymentAnnotations(resource interface{}) map[string]string { 9 | return resource.(*ext.Deployment).GetAnnotations() 10 | } 11 | 12 | func GetDeploymentName(resource interface{}) string { 13 | return resource.(*ext.Deployment).Name 14 | } 15 | 16 | func GetDeploymentNamespace(resource interface{}) string { 17 | return resource.(*ext.Deployment).Namespace 18 | } 19 | 20 | func GetDaemonsetAnnotations(resource interface{}) map[string]string { 21 | return resource.(*apps.DaemonSet).GetAnnotations() 22 | } 23 | 24 | func GetDaemonsetName(resource interface{}) string { 25 | return resource.(*apps.DaemonSet).Name 26 | } 27 | 28 | func GetDaemonsetNamespace(resource interface{}) string { 29 | return resource.(*apps.DaemonSet).Namespace 30 | } 31 | 32 | func GetStatefulsetAnnotations(resource interface{}) map[string]string { 33 | return resource.(*apps.StatefulSet).GetAnnotations() 34 | } 35 | 36 | func GetStatefulsetName(resource interface{}) string { 37 | return resource.(*apps.StatefulSet).Name 38 | } 39 | 40 | func GetStatefulsetNamespace(resource interface{}) string { 41 | return resource.(*apps.StatefulSet).Namespace 42 | } 43 | -------------------------------------------------------------------------------- /internal/pkg/cmd/proxyinjector.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/sirupsen/logrus" 7 | "github.com/spf13/cobra" 8 | "github.com/stakater/ProxyInjector/internal/pkg/config" 9 | "github.com/stakater/ProxyInjector/internal/pkg/controller" 10 | "github.com/stakater/ProxyInjector/pkg/kube" 11 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 | ) 13 | 14 | // NewProxyInjectorCommand starts the proxy injector controller 15 | func NewProxyInjectorCommand() *cobra.Command { 16 | cmd := &cobra.Command{ 17 | Use: "proxyInjector", 18 | Short: "An authentication proxy injector for Kubernetes pods", 19 | Run: startProxyInjector, 20 | } 21 | return cmd 22 | } 23 | 24 | func startProxyInjector(cmd *cobra.Command, args []string) { 25 | logrus.Info("Starting ProxyInjector") 26 | currentNamespace := os.Getenv("KUBERNETES_NAMESPACE") 27 | if len(currentNamespace) == 0 { 28 | currentNamespace = v1.NamespaceAll 29 | logrus.Warnf("KUBERNETES_NAMESPACE is unset, will detect changes in all namespaces.") 30 | } 31 | 32 | // create the clientset 33 | clientset, err := kube.GetKubernetesClient() 34 | if err != nil { 35 | logrus.Fatal(err) 36 | } 37 | 38 | config := config.GetControllerConfig() 39 | 40 | for resource := range kube.ResourceMap { 41 | c, err := controller.NewController(clientset, resource, config, currentNamespace) 42 | if err != nil { 43 | logrus.Fatalf("%s", err) 44 | } 45 | 46 | // Now let's start the controller 47 | stop := make(chan struct{}) 48 | defer close(stop) 49 | 50 | go c.Run(1, stop) 51 | } 52 | 53 | // Wait forever 54 | select {} 55 | } 56 | -------------------------------------------------------------------------------- /internal/pkg/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "gopkg.in/yaml.v2" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | ) 9 | 10 | type Config struct { 11 | GatekeeperImage string `yaml:"gatekeeper-image"` 12 | ClientId string `yaml:"client-id"` 13 | ClientSecret string `yaml:"client-secret"` 14 | DiscoveryUrl string `yaml:"discovery-url"` 15 | EnableDefaultDeny string `yaml:"enable-default-deny"` 16 | Listen string `yaml:"listen"` 17 | SecureCookie string `yaml:"secure-cookie"` 18 | Verbose string `yaml:"verbose"` 19 | EnableLogging string `yaml:"enable-logging"` 20 | CorsOrigins []string `yaml:"cors-origins"` 21 | CorsMethods []string `yaml:"cors-methods"` 22 | Resources []struct { 23 | URI string `yaml:"uri"` 24 | Methods []string `yaml:"methods"` 25 | Roles []string `yaml:"roles"` 26 | } `yaml:"resources"` 27 | Scopes []string `yaml:"scopes"` 28 | } 29 | 30 | func ReadConfig(filePath string) Config { 31 | var config Config 32 | // Read YML 33 | log.Println("Reading YAML Configuration") 34 | source, err := ioutil.ReadFile(filePath) 35 | if err != nil { 36 | log.Panic(err) 37 | } 38 | 39 | // Unmarshall 40 | err = yaml.Unmarshal(source, &config) 41 | if err != nil { 42 | log.Panic(err) 43 | } 44 | 45 | return config 46 | } 47 | 48 | func GetControllerConfig() Config { 49 | configFilePath := os.Getenv("CONFIG_FILE_PATH") 50 | if len(configFilePath) == 0 { 51 | configFilePath = "config.yaml" 52 | } 53 | 54 | return ReadConfig(configFilePath) 55 | } 56 | -------------------------------------------------------------------------------- /internal/pkg/constants/annotations.go: -------------------------------------------------------------------------------- 1 | package constants 2 | 3 | const ( 4 | AnnotationPrefix = "authproxy.stakater.com/" 5 | EnabledAnnotation = "authproxy.stakater.com/enabled" 6 | SourceServiceNameAnnotation = "authproxy.stakater.com/source-service-name" 7 | ImageNameAnnotation = "authproxy.stakater.com/gatekeeper-image" 8 | ImagePullPolicyAnnotation = "authproxy.stakater.com/image-pull-policy" 9 | TargetPortAnnotation = "authproxy.stakater.com/target-port" 10 | ) 11 | 12 | var KeycloakArgs = []string{ 13 | "listen", 14 | "listen-http", 15 | "discovery-url", 16 | "client-id", 17 | "client-secret", 18 | "redirection-url", 19 | "revocation-url", 20 | "skip-openid-provider-tls-verify", 21 | "openid-provider-proxy", 22 | "openid-provider-timeout", 23 | "base-uri", 24 | "oauth-uri", 25 | "scopes", 26 | "upstream-url", 27 | "upstream-ca", 28 | "resources", 29 | "headers", 30 | "preserve-host", 31 | "request-id-header", 32 | "response-headers", 33 | "enable-self-signed-tls", 34 | "self-signed-tls-hostnames", 35 | "self-signed-tls-expiration", 36 | "enable-request-id", 37 | "enable-logout-redirect", 38 | "enable-default-deny", 39 | "enable-encrypted-token", 40 | "enable-logging", 41 | "enable-json-logging", 42 | "enable-forwarding", 43 | "enable-security-filter", 44 | "enable-refresh-tokens", 45 | "enable-session-cookies", 46 | "enable-login-handler", 47 | "enable-token-header", 48 | "enable-authorization-header", 49 | "enable-authorization-cookies", 50 | "enable-https-redirection", 51 | "enable-profiling", 52 | "enable-metrics", 53 | "filter-browser-xss", 54 | "filter-content-nosniff", 55 | "filter-frame-deny", 56 | "content-security-policy", 57 | "localhost-metrics", 58 | "access-token-duration", 59 | "cookie-domain", 60 | "cookie-access-name", 61 | "cookie-refresh-name", 62 | "secure-cookie", 63 | "http-only-cookie", 64 | "match-claims", 65 | "add-claims", 66 | "tls-cert", 67 | "tls-private-key", 68 | "tls-ca-certificate", 69 | "tls-ca-key", 70 | "tls-client-certificate", 71 | "skip-upstream-tls-verify", 72 | "skip-client-id", 73 | "cors-origins", 74 | "cors-methods", 75 | "cors-headers", 76 | "cors-exposed-headers", 77 | "cors-credentials", 78 | "cors-max-age", 79 | "hostnames", 80 | "store-url", 81 | "encryption-key", 82 | "invalid-auth-redirects-with-303", 83 | "no-redirects", 84 | "skip-token-verification", 85 | "upstream-keepalives", 86 | "upstream-timeout", 87 | "upstream-keepalive-timeout", 88 | "upstream-tls-handshake-timeout", 89 | "upstream-response-header-timeout", 90 | "upstream-expect-continue-timeout", 91 | "verbose", 92 | "max-idle-connections", 93 | "max-idle-connections-per-host", 94 | "enabled-proxy-protocol", 95 | "server-read-timeout", 96 | "server-write-timeout", 97 | "server-idle-timeout", 98 | "use-letsencrypt", 99 | "letsencrypt-cache-dir", 100 | "sign-in-page", 101 | "forbidden-page", 102 | "tags", 103 | "forwarding-username", 104 | "forwarding-password", 105 | "forwarding-domains", 106 | "disable-all-logging", 107 | } 108 | -------------------------------------------------------------------------------- /internal/pkg/controller/controller.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | logger "github.com/sirupsen/logrus" 8 | "github.com/stakater/ProxyInjector/internal/pkg/config" 9 | "github.com/stakater/ProxyInjector/internal/pkg/handler" 10 | "github.com/stakater/ProxyInjector/pkg/kube" 11 | "k8s.io/apimachinery/pkg/fields" 12 | "k8s.io/apimachinery/pkg/util/runtime" 13 | "k8s.io/apimachinery/pkg/util/wait" 14 | "k8s.io/client-go/kubernetes" 15 | "k8s.io/client-go/tools/cache" 16 | "k8s.io/client-go/util/workqueue" 17 | ) 18 | 19 | // Controller for checking events 20 | type Controller struct { 21 | client kubernetes.Interface 22 | indexer cache.Indexer 23 | queue workqueue.RateLimitingInterface 24 | informer cache.Controller 25 | cfg config.Config 26 | namespace string 27 | res string 28 | } 29 | 30 | // NewController for initializing a Controller 31 | func NewController( 32 | client kubernetes.Interface, resource string, conf config.Config, namespace string) (*Controller, error) { 33 | 34 | c := Controller{ 35 | client: client, 36 | namespace: namespace, 37 | cfg: conf, 38 | res: resource, 39 | } 40 | 41 | queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()) 42 | listWatcher := cache.NewListWatchFromClient(client.AppsV1().RESTClient(), resource, namespace, fields.Everything()) 43 | 44 | indexer, informer := cache.NewIndexerInformer(listWatcher, kube.ResourceMap[resource], 0, cache.ResourceEventHandlerFuncs{ 45 | AddFunc: c.Add, 46 | UpdateFunc: c.Update, 47 | }, cache.Indexers{}) 48 | c.indexer = indexer 49 | c.informer = informer 50 | c.queue = queue 51 | return &c, nil 52 | } 53 | 54 | // Add function to add a new object to the queue in case of creating a resource 55 | func (c *Controller) Add(obj interface{}) { 56 | c.queue.Add(handler.ResourceCreatedHandler{ 57 | Resource: obj, 58 | }) 59 | } 60 | 61 | // Add function to add a new object to the queue in case of creating a resource 62 | func (c *Controller) Update(old interface{}, new interface{}) { 63 | c.queue.Add(handler.ResourceCreatedHandler{ 64 | Resource: new, 65 | }) 66 | } 67 | 68 | //Run function for controller which handles the queue 69 | func (c *Controller) Run(threadiness int, stopCh chan struct{}) { 70 | 71 | logger.Infof("Starting Controller") 72 | defer runtime.HandleCrash() 73 | 74 | // Let the workers stop when we are done 75 | defer c.queue.ShutDown() 76 | 77 | go c.informer.Run(stopCh) 78 | 79 | // Wait for all involved caches to be synced, before processing items from the queue is started 80 | if !cache.WaitForCacheSync(stopCh, c.informer.HasSynced) { 81 | runtime.HandleError(fmt.Errorf("Timed out waiting for caches to sync")) 82 | return 83 | } 84 | 85 | for i := 0; i < threadiness; i++ { 86 | go wait.Until(c.runWorker, time.Second, stopCh) 87 | } 88 | 89 | <-stopCh 90 | logger.Infof("Stopping Controller") 91 | } 92 | 93 | func (c *Controller) runWorker() { 94 | for c.processNextItem() { 95 | } 96 | } 97 | 98 | func (c *Controller) processNextItem() bool { 99 | // Wait until there is a new item in the working queue 100 | resourceHandler, quit := c.queue.Get() 101 | if quit { 102 | logger.Info("quit") 103 | return false 104 | } 105 | // Tell the queue that we are done with processing this key. This unblocks the key for other workers 106 | // This allows safe parallel processing because two events with the same key are never processed in 107 | // parallel. 108 | defer c.queue.Done(resourceHandler) 109 | 110 | // Invoke the method containing the business logic 111 | err := resourceHandler.(handler.ResourceHandler).Handle(c.cfg, c.res) 112 | // Handle the error if something went wrong during the execution of the business logic 113 | c.handleErr(err, resourceHandler) 114 | return true 115 | } 116 | 117 | // handleErr checks if an error happened and makes sure we will retry later. 118 | func (c *Controller) handleErr(err error, key interface{}) { 119 | if err == nil { 120 | // Forget about the #AddRateLimited history of the key on every successful synchronization. 121 | // This ensures that future processing of updates for this key is not delayed because of 122 | // an outdated error history. 123 | c.queue.Forget(key) 124 | return 125 | } 126 | 127 | // This controller retries 5 times if something goes wrong. After that, it stops trying. 128 | if c.queue.NumRequeues(key) < 5 { 129 | logger.Errorf("Error syncing events %v: %v", key, err) 130 | 131 | // Re-enqueue the key rate limited. Based on the rate limiter on the 132 | // queue and the re-enqueue history, the key will be processed later again. 133 | c.queue.AddRateLimited(key) 134 | return 135 | } 136 | 137 | c.queue.Forget(key) 138 | // Report to an external entity that, even after several retries, we could not successfully process this key 139 | runtime.HandleError(err) 140 | logger.Infof("Dropping the key %q out of the queue: %v", key, err) 141 | } 142 | -------------------------------------------------------------------------------- /internal/pkg/handler/create.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "strings" 7 | 8 | logger "github.com/sirupsen/logrus" 9 | "github.com/stakater/ProxyInjector/internal/pkg/callbacks" 10 | "github.com/stakater/ProxyInjector/internal/pkg/config" 11 | "github.com/stakater/ProxyInjector/internal/pkg/constants" 12 | "github.com/stakater/ProxyInjector/pkg/kube" 13 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 | "k8s.io/apimachinery/pkg/types" 15 | "k8s.io/apimachinery/pkg/util/intstr" 16 | "k8s.io/client-go/kubernetes" 17 | "k8s.io/client-go/util/retry" 18 | ) 19 | 20 | // ResourceCreatedHandler contains new objects 21 | type ResourceCreatedHandler struct { 22 | Resource interface{} `json:"resource"` 23 | } 24 | 25 | type patch struct { 26 | Spec struct { 27 | Template struct { 28 | Spec struct { 29 | Containers []Container `json:"containers"` 30 | } `json:"spec"` 31 | } `json:"template"` 32 | } `json:"spec"` 33 | } 34 | 35 | type Container struct { 36 | Name string `json:"name"` 37 | Image string `json:"image"` 38 | Args []string `json:"args"` 39 | } 40 | 41 | // Handle processes the newly created resource 42 | func (r ResourceCreatedHandler) Handle(conf config.Config, resourceType string) error { 43 | if r.Resource == nil { 44 | logger.Errorf("Resource creation handler received nil resource") 45 | } else { 46 | 47 | var name string 48 | var namespace string 49 | var annotations map[string]string 50 | 51 | if resourceType == "deployments" { 52 | name = callbacks.GetDeploymentName(r.Resource) 53 | namespace = callbacks.GetDeploymentNamespace(r.Resource) 54 | annotations = callbacks.GetDeploymentAnnotations(r.Resource) 55 | } else if resourceType == "daemonsets" { 56 | name = callbacks.GetDaemonsetName(r.Resource) 57 | namespace = callbacks.GetDaemonsetNamespace(r.Resource) 58 | annotations = callbacks.GetDaemonsetAnnotations(r.Resource) 59 | } else if resourceType == "statefulsets" { 60 | name = callbacks.GetStatefulsetName(r.Resource) 61 | namespace = callbacks.GetStatefulsetNamespace(r.Resource) 62 | annotations = callbacks.GetStatefulsetAnnotations(r.Resource) 63 | } 64 | logger.Infof("Resource creation handler checking resource %s of type %s in namespace %s", name, resourceType, namespace) 65 | 66 | if annotations[constants.EnabledAnnotation] == "true" { 67 | 68 | client, err := kube.GetKubernetesClient() 69 | 70 | logger.Infof("Updating resource ... %s", name) 71 | 72 | containerArgs := getConfigArgs(conf, annotations) 73 | 74 | for _, arg := range constants.KeycloakArgs { 75 | if ContainsKey(annotations, arg) { 76 | containerArgs = removeIfExists(containerArgs, arg) 77 | resourceStrings := strings.Split(annotations[constants.AnnotationPrefix+arg], "&") 78 | 79 | for _, resourceString := range resourceStrings { 80 | containerArgs = append(containerArgs, "--"+arg+"="+resourceString) 81 | } 82 | } else if annotations[constants.AnnotationPrefix+arg] != "" { 83 | containerArgs = removeIfExists(containerArgs, arg) 84 | containerArgs = append(containerArgs, "--"+arg+"="+annotations[constants.AnnotationPrefix+arg]) 85 | } 86 | } 87 | 88 | if annotations[constants.ImageNameAnnotation] == "" { 89 | annotations[constants.ImageNameAnnotation] = conf.GatekeeperImage 90 | } 91 | 92 | if err == nil { 93 | payloadBytes, err3 := getPatch(containerArgs, annotations[constants.ImageNameAnnotation]) 94 | 95 | if err3 == nil { 96 | 97 | var err2 error 98 | logger.Info("checking resource type and updating...") 99 | if resourceType == "deployments" { 100 | logger.Info("patching deployment") 101 | _, err2 = client.AppsV1().Deployments(namespace).Patch(name, types.StrategicMergePatchType, payloadBytes) 102 | } else if resourceType == "daemonsets" { 103 | logger.Info("patching daemonset") 104 | _, err2 = client.AppsV1().DaemonSets(namespace).Patch(name, types.StrategicMergePatchType, payloadBytes) 105 | } else if resourceType == "statefulsets" { 106 | logger.Info("patching statefulset") 107 | _, err2 = client.AppsV1().StatefulSets(namespace).Patch(name, types.StrategicMergePatchType, payloadBytes) 108 | } else { 109 | return errors.New("unexpected resource type") 110 | } 111 | 112 | if err2 == nil { 113 | logger.Infof("Updated resource... %s", name) 114 | } else { 115 | logger.Error(err2) 116 | } 117 | 118 | updateService(client, namespace, annotations[constants.SourceServiceNameAnnotation], annotations[constants.TargetPortAnnotation]) 119 | 120 | } else { 121 | logger.Error(err3) 122 | } 123 | } else { 124 | logger.Error(err) 125 | } 126 | 127 | } 128 | } 129 | return nil 130 | } 131 | 132 | func removeIfExists(containerArgs []string, arg string) []string { 133 | i := 0 // output index 134 | for _, containerArg := range containerArgs { 135 | if !strings.Contains(containerArg, arg) { 136 | containerArgs[i] = containerArg 137 | i++ 138 | } 139 | } 140 | return containerArgs[:i] 141 | } 142 | 143 | // ContainsKey tells whether a key exist in map[string]string. 144 | func ContainsKey(list map[string]string, word string) bool { 145 | for key := range list { 146 | if strings.Contains(key, word) { 147 | return true 148 | } 149 | } 150 | return false 151 | } 152 | 153 | func getPatch(containerArgs []string, image string) ([]byte, error) { 154 | 155 | payload := &patch{} 156 | payload.Spec.Template.Spec.Containers = []Container{{ 157 | Name: "proxy", 158 | Image: image, 159 | Args: containerArgs, 160 | }} 161 | 162 | return json.Marshal(payload) 163 | } 164 | 165 | func updateService(client *kubernetes.Clientset, namespace string, service string, port string) { 166 | 167 | retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error { 168 | // RetryOnConflict uses exponential backoff to avoid exhausting the apiserver 169 | result, getErr := client.CoreV1().Services(namespace).Get(service, metav1.GetOptions{}) 170 | if getErr != nil { 171 | logger.Errorf("Failed to get latest version of Service: %v", getErr) 172 | } 173 | 174 | if port == "" { 175 | result.Spec.Ports[0].TargetPort = intstr.FromInt(80) 176 | } else { 177 | result.Spec.Ports[0].TargetPort = intstr.Parse(port) 178 | } 179 | _, updateErr := client.CoreV1().Services(namespace).Update(result) 180 | return updateErr 181 | }) 182 | 183 | if retryErr == nil { 184 | logger.Infof("Updated service... %s", service) 185 | } else { 186 | logger.Errorf("Update failed: %v", retryErr) 187 | } 188 | } 189 | 190 | func getConfigArgs(config config.Config, annotations map[string]string) []string { 191 | 192 | configArgs := []string{} 193 | 194 | //config from global proxy injector config is injected only if not overriden at the app level 195 | 196 | if config.ClientId != "" && annotations[constants.AnnotationPrefix+"client-id"] == "" { 197 | configArgs = append(configArgs, "--client-id="+config.ClientId) 198 | } 199 | if config.ClientSecret != "" && annotations[constants.AnnotationPrefix+"client-secret"] == "" { 200 | configArgs = append(configArgs, "--client-secret="+config.ClientSecret) 201 | } 202 | if config.DiscoveryUrl != "" && annotations[constants.AnnotationPrefix+"discovery-url"] == "" { 203 | configArgs = append(configArgs, "--discovery-url="+config.DiscoveryUrl) 204 | } 205 | /*if config.EnableDefaultDeny !="" && annotations[constants.AnnotationPrefix+"enable-default-deny"] == "" { 206 | configArgs = append(configArgs, "--enable-default-deny="+config.EnableDefaultDeny) 207 | }*/ 208 | if config.Listen != "" && annotations[constants.AnnotationPrefix+"listen"] == "" { 209 | configArgs = append(configArgs, "--listen="+config.Listen) 210 | } 211 | if config.SecureCookie != "" && annotations[constants.AnnotationPrefix+"secure-cookie"] == "" { 212 | configArgs = append(configArgs, "--secure-cookie="+config.SecureCookie) 213 | } 214 | if config.Verbose != "" && annotations[constants.AnnotationPrefix+"verbose"] == "" { 215 | configArgs = append(configArgs, "--verbose="+config.Verbose) 216 | } 217 | if config.EnableLogging != "" && annotations[constants.AnnotationPrefix+"enable-logging"] == "" { 218 | configArgs = append(configArgs, "--enable-logging="+config.EnableLogging) 219 | } 220 | for _, origin := range config.CorsOrigins { 221 | configArgs = append(configArgs, "--cors-origins="+origin) 222 | } 223 | for _, method := range config.CorsMethods { 224 | configArgs = append(configArgs, "--cors-methods="+method) 225 | } 226 | for _, resource := range config.Resources { 227 | // --resources "uri=/admin*|roles=admin,superuser|methods=POST,DELETE 228 | res := "" 229 | if resource.URI != "" { 230 | res = "uri=" + resource.URI 231 | } 232 | if len(resource.Methods) != 0 { 233 | res = res + "|methods=" + strings.Join(resource.Methods, ",") 234 | } 235 | configArgs = append(configArgs, "--resources="+res) 236 | } 237 | for _, scope := range config.Scopes { 238 | configArgs = append(configArgs, "--scopes="+scope) 239 | } 240 | 241 | return configArgs 242 | } 243 | -------------------------------------------------------------------------------- /internal/pkg/handler/handler.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import "github.com/stakater/ProxyInjector/internal/pkg/config" 4 | 5 | // ResourceHandler handles the creation and update of resources 6 | type ResourceHandler interface { 7 | Handle(conf config.Config, resourceType string) error 8 | } 9 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/stakater/ProxyInjector/internal/pkg/app" 7 | ) 8 | 9 | func main() { 10 | if err := app.Run(); err != nil { 11 | os.Exit(1) 12 | } 13 | os.Exit(0) 14 | } 15 | -------------------------------------------------------------------------------- /pkg/kube/client.go: -------------------------------------------------------------------------------- 1 | package kube 2 | 3 | import ( 4 | "os" 5 | 6 | "k8s.io/client-go/tools/clientcmd" 7 | 8 | "k8s.io/client-go/kubernetes" 9 | "k8s.io/client-go/rest" 10 | ) 11 | 12 | // GetKubernetesClient gets the client for k8s, if ~/.kube/config exists so get that config else incluster config 13 | func GetKubernetesClient() (*kubernetes.Clientset, error) { 14 | config, err := getConfig() 15 | if err != nil { 16 | return nil, err 17 | } 18 | return kubernetes.NewForConfig(config) 19 | } 20 | 21 | func getConfig() (*rest.Config, error) { 22 | var config *rest.Config 23 | var err error 24 | kubeconfigPath := os.Getenv("KUBECONFIG") 25 | if kubeconfigPath == "" { 26 | kubeconfigPath = os.Getenv("HOME") + "/.kube/config" 27 | } 28 | //If file exists so use that config settings 29 | if _, err := os.Stat(kubeconfigPath); err == nil { 30 | config, err = clientcmd.BuildConfigFromFlags("", kubeconfigPath) 31 | if err != nil { 32 | return nil, err 33 | } 34 | } else { //Use Incluster Configuration 35 | config, err = rest.InClusterConfig() 36 | if err != nil { 37 | return nil, err 38 | } 39 | } 40 | if err != nil { 41 | return nil, err 42 | } 43 | 44 | return config, nil 45 | } 46 | -------------------------------------------------------------------------------- /pkg/kube/resourcemapper.go: -------------------------------------------------------------------------------- 1 | package kube 2 | 3 | import ( 4 | ext "k8s.io/api/apps/v1" 5 | "k8s.io/apimachinery/pkg/runtime" 6 | //apps "k8s.io/api/apps/v1beta2" 7 | ) 8 | 9 | // ResourceMap are resources from where changes are going to be detected 10 | var ResourceMap = map[string]runtime.Object{ 11 | "deployments": &ext.Deployment{}, 12 | //"daemonsets": &apps.DaemonSet{}, 13 | //"statefulsets": &apps.StatefulSet{}, 14 | } 15 | -------------------------------------------------------------------------------- /stk.yaml: -------------------------------------------------------------------------------- 1 | issues: 2 | kind: 1 3 | url: https://aurorasolutions.atlassian.net 4 | project: STK --------------------------------------------------------------------------------