├── .dockerignore ├── .github └── workflows │ ├── golangci-lint.yml │ └── k8s.yml ├── .gitignore ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── charts ├── README.md ├── dex-k8s-authenticator │ ├── .helmignore │ ├── Chart.yaml │ ├── README.md │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── ca_secrets.yaml │ │ ├── configmap.yaml │ │ ├── deployment.yaml │ │ ├── ingress.yaml │ │ └── service.yaml │ └── values.yaml └── dex │ ├── .helmignore │ ├── Chart.yaml │ ├── README.md │ ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── configmap.yaml │ ├── deployment.yaml │ ├── ingress.yaml │ ├── rbac.yaml │ ├── secret.yaml │ ├── service.yaml │ └── serviceaccount.yaml │ └── values.yaml ├── dex-auth.go ├── docs ├── config.md ├── develop.md ├── eks.md ├── helm.md ├── images │ ├── auth-managed.jpg │ └── auth-regular.jpg └── ssl.md ├── entrypoint.sh ├── examples ├── config.yaml ├── dex-server-config-dev.yaml ├── index-page.png └── kubeconfig-page.png ├── go.mod ├── go.sum ├── html └── static │ ├── button.svg │ ├── clipboard.min.js │ ├── clippy.svg │ ├── highlight.pack.min.js │ ├── main.css │ ├── snippets.js │ ├── styles.css │ ├── tabs.css │ └── tooltips.js ├── main.go ├── templates.go ├── templates ├── error.html ├── id-token-tab.html ├── index.html ├── kubeconfig.html ├── linux-mac-common.html ├── linux-tab.html ├── mac-tab.html └── windows-tab.html └── tests └── e2e └── helm ├── dex-k8s-auth-overrides.yaml └── dex-overrides.yaml /.dockerignore: -------------------------------------------------------------------------------- 1 | charts 2 | examples 3 | docs 4 | -------------------------------------------------------------------------------- /.github/workflows/golangci-lint.yml: -------------------------------------------------------------------------------- 1 | name: golangci-lint 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - main 7 | pull_request: 8 | jobs: 9 | golangci: 10 | name: lint 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: golangci-lint 15 | uses: golangci/golangci-lint-action@v2 16 | with: 17 | version: v1.29 18 | -------------------------------------------------------------------------------- /.github/workflows/k8s.yml: -------------------------------------------------------------------------------- 1 | name: k8s-lint 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - main 7 | pull_request: 8 | jobs: 9 | k8s-lint: 10 | name: helm-lint 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v1 14 | - name: helm-lint-dex 15 | uses: stefanprodan/kube-tools@v1.5.0 16 | with: 17 | helmv3: 3.0.0 18 | run: | 19 | helmv3 lint charts/dex 20 | - name: helm-lint-dex-k8s-auth 21 | uses: stefanprodan/kube-tools@v1.5.0 22 | with: 23 | helmv3: 3.0.0 24 | command: | 25 | helmv3 lint charts/dex-k8s-authenticator 26 | - name: helm-template 27 | uses: stefanprodan/kube-tools@v1.5.0 28 | with: 29 | helmv3: 3.0.0 30 | command: | 31 | helmv3 template charts/dex --output-dir /github/workspace/rendered-charts/dex 32 | helmv3 template charts/dex-k8s-authenticator --output-dir /github/workspace/rendered-charts/dex-k8s-authenticator 33 | - name: kubeval 34 | uses: stefanprodan/kube-tools@v1.5.0 35 | with: 36 | helmv3: 3.0.0 37 | command: | 38 | kubeval -d /github/workspace/rendered-charts --strict --ignore-missing-schemas 39 | e2e: 40 | runs-on: ubuntu-latest 41 | steps: 42 | - uses: actions/checkout@v1 43 | - name: setup-kind 44 | uses: engineerd/setup-kind@v0.5.0 45 | with: 46 | name: kind 47 | - name: build-ci-image 48 | run: | 49 | docker build -t mintel/dex-k8s-authenticator:${GITHUB_SHA} . 50 | kind load docker-image mintel/dex-k8s-authenticator:${GITHUB_SHA} 51 | - name: update-helm-values 52 | run: | 53 | set -x 54 | export NODE_IP=$(kubectl get nodes -o jsonpath="{.items[0].status.addresses[0].address}") 55 | export CI_TAG=$GITHUB_SHA 56 | envsubst < ./tests/e2e/helm/dex-overrides.yaml > /tmp/dex-overrides.yaml 57 | envsubst < ./tests/e2e/helm/dex-k8s-auth-overrides.yaml > /tmp/dex-k8s-auth-overrides.yaml 58 | - name: install-dex 59 | run: | 60 | helm install -f /tmp/dex-overrides.yaml dex ./charts/dex 61 | kubectl describe deployment dex 62 | kubectl rollout status deploy dex -w 63 | - name: install-dex-auth 64 | run: | 65 | helm install -f /tmp/dex-k8s-auth-overrides.yaml dex-k8s-authenticator ./charts/dex-k8s-authenticator 66 | kubectl describe deployment dex-k8s-authenticator 67 | kubectl rollout status deploy dex-k8s-authenticator -w 68 | - name: test 69 | run: | 70 | kubectl get pods 71 | export NODE_IP=$(kubectl get nodes -o jsonpath="{.items[0].status.addresses[0].address}") 72 | curl -Lsf "http://${NODE_IP}:30000/login/my-cluster" | grep "Log in to Your Account" 73 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | bin 3 | ./idea 4 | *.iml 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | ## [v1.4.0] 5 | 6 | ### Changed 7 | 8 | - Bump dex version to `v2.27.0` (security release) 9 | - Switch to `dexidp/dex` container image registry 10 | 11 | ## [v1.3.0] 12 | 13 | ### Added 14 | 15 | - Pass optional `connector_id` to cluster context (#146) 16 | - Added `trusted_root_ca` to dex-k8s-authenticator helm chart (#143) 17 | - Added `k8s_ca_pem_file` option (#136) 18 | - Allow OIDC scopes per cluster (#129) 19 | - Added namespace field to cluster-config (#124) 20 | - Added HTTP Proxy support (#109) 21 | - Added CircleCI tests 22 | 23 | ### Fixed 24 | 25 | - Fix indentation for `nodeSelector` and `tolerations` in dex-k8s-authenticator helm chart (#137) 26 | - Propgate SIGTERM for graceful shutdown (#110) 27 | 28 | ## [v1.2.0] 29 | 30 | ### Added 31 | 32 | - Additional tab to display only the id-token 33 | - Service Loadbalancer IP override capability in Helm chart 34 | - Service annotations capability in Helm chart 35 | - Options to specify `idp_ca_pem_file` and `trusted_root_ca_file` 36 | - Support for fixed context name (instead of auto-generated) 37 | 38 | ### Changed 39 | 40 | - Bump dex version to `v2.17.0` 41 | - Bump to `golang:1.12-alpine3.10` 42 | - Switch to Go Modules 43 | - Minor update (skip cluster selection if only 1 cluster defined) 44 | 45 | ### Fixed 46 | 47 | - Fixed Affinity indentation in Helm chart 48 | 49 | ## [v1.1.0] 50 | 51 | ### Added 52 | 53 | - Documentation on `web_path_prefix` 54 | - Helm charts now add a checksum annotation on the configmap to roll-deployments when configuration changes 55 | - Added IDPCaPem option to support displaying of idp-ca inline 56 | 57 | 58 | ### Changed 59 | 60 | - Bump dex version to `v2.13.0` and pull from new repo at quay.io/dexidp/dex 61 | - Documentation improvements 62 | 63 | ### Fixed 64 | 65 | - Fixes to some css to use relative paths 66 | 67 | ## [v1.0.0] 68 | ### Added 69 | 70 | - New tabbed layout with clipboard copy options. Key driver for this is to 71 | enable Windows specific instructions. 72 | - Added envar substitutions. Can now generate a config based on values in the 73 | environment (useful for the `client_secret`). 74 | - Added `nodePort` support to Helm charts. 75 | - Added `kubectl_version` option in config. Used to construct a download link to `kubectl` which may be useful. 76 | - Added `web_path_prefix` config option to set url-prefix for serving requests and assets. 77 | - Added `trusted_root_ca` config option to specifiy 1 more root CA's. 78 | - Added `k8s_ca_pem` config option to provide abililty to specify the Kubernetes CA inline. 79 | 80 | ### Changed 81 | 82 | - Use `ClusterName` in preference to `ClientID` when generating k8s context commands 83 | - Bump dex version to `v2.10.0` 84 | - Bump base image to `alpine 3.8` 85 | - Documentation updates. 86 | - Helm chart for dex-k8s-authenticator would fail when caCerts were specified due to breaking naming conventions on and Secret and Volume resources. Introduce a required `filename` option which lets us separate out the filename of the cert and the name of the k8s resource created. 87 | - Slim down final docker container size. 88 | 89 | ### Fixed 90 | 91 | - `update-ca-certificates` only accepts *.crt (only attempt to copy these) 92 | 93 | ## [v0.4.0] 94 | ### Added 95 | - Abililty to provide K8s cert file content in configuration via k8s_ca_pem 96 | cluster option. 97 | 98 | ### Fixed 99 | - Explicitly define the CA certificate path using ${HOME} 100 | 101 | ## [v0.3.0] 102 | ### Added 103 | - Allow self-signed certs to be used 104 | 105 | ### Changed 106 | - Bump to golang:1.9.4-alpine3.7 107 | 108 | ### Fixed 109 | - Fixed helm-chart ingress servicePort 110 | 111 | ## [v0.2.0] 112 | ### Added 113 | - Helm chart serviceAccountName 114 | - Documentation improvements 115 | 116 | ### Changed 117 | - Helm chart RBAC (renamed some vars). 118 | 119 | ## [v0.1.0] 120 | ### Added 121 | - Initial release 122 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.16.4-alpine3.13 2 | 3 | RUN apk add --no-cache --update alpine-sdk bash 4 | 5 | ENV GO111MODULE=on 6 | 7 | WORKDIR /app 8 | 9 | COPY go.mod . 10 | COPY go.sum . 11 | 12 | RUN go mod download 13 | 14 | COPY . . 15 | 16 | RUN make build 17 | 18 | FROM alpine:3.13.5 19 | 20 | # Dex connectors, such as GitHub and Google logins require root certificates. 21 | # Proper installations should manage those certificates, but it's a bad user 22 | # experience when this doesn't work out of the box. 23 | # 24 | # OpenSSL is required so wget can query HTTPS endpoints for health checking. 25 | RUN apk add --update ca-certificates openssl curl tini 26 | 27 | RUN mkdir -p /app/bin 28 | COPY --from=0 /app/bin/dex-k8s-authenticator /app/bin/ 29 | COPY --from=0 /app/html /app/html 30 | COPY --from=0 /app/templates /app/templates 31 | 32 | # Add any required certs/key by mounting a volume on /certs 33 | # The entrypoint will copy them and run update-ca-certificates at startup 34 | RUN mkdir -p /certs 35 | 36 | WORKDIR /app 37 | 38 | COPY entrypoint.sh / 39 | RUN chmod a+x /entrypoint.sh 40 | 41 | ENTRYPOINT ["/sbin/tini", "--", "/entrypoint.sh"] 42 | 43 | CMD ["--help"] 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Mintel Group Ltd 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | GOBIN=$(shell pwd)/bin 2 | GOFILES=$(wildcard *.go) 3 | GONAME=dex-k8s-authenticator 4 | TAG=latest 5 | 6 | all: build 7 | 8 | .PHONY: build 9 | build: 10 | @echo "Building $(GOFILES) to ./bin" 11 | GOBIN=$(GOBIN) go build -o bin/$(GONAME) $(GOFILES) 12 | 13 | .PHONY: container 14 | container: 15 | @echo "Building container image" 16 | docker build -t ${GONAME}:${TAG} . 17 | .PHONY: clean 18 | clean: 19 | @echo "Cleaning" 20 | GOBIN=$(GOBIN) go clean 21 | rm -rf ./bin 22 | 23 | .PHONY: lint 24 | lint: 25 | golangci-lint run 26 | 27 | .PHONY: lint-fix 28 | lint-fix: lint 29 | golangci-lint run --fix 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dex K8s Authenticator 2 | 3 | [![golang-lint](https://github.com/mintel/dex-k8s-authenticator/workflows/golangci-lint/badge.svg)](https://github.com/mintel/dex-k8s-authenticator/actions/workflows/golangci-lint.yml) 4 | [![k8s-lint](https://github.com/mintel/dex-k8s-authenticator/workflows/k8s-lint/badge.svg)](https://github.com/mintel/dex-k8s-authenticator/actions/workflows/k8s.yml) 5 | 6 | A helper web-app which talks to one or more [Dex Identity services](https://github.com/dexidp/dex) to generate 7 | `kubectl` commands for creating and modifying a `kubeconfig`. 8 | 9 | The Web UI supports generating tokens against multiple cluster such as Dev / Staging / Production. 10 | 11 | 12 | ## Also provides 13 | * Helm Charts 14 | * SSL Support 15 | * Linux/Mac/Windows instructions 16 | 17 | ## Documentation 18 | 19 | - [Developing and Running](docs/develop.md) 20 | - [Configuration Options](docs/config.md) 21 | - [Using the Helm Charts](docs/helm.md) 22 | - [SSL Support](docs/ssl.md) 23 | 24 | ## Screen shots 25 | 26 | ![Index Page](examples/index-page.png) 27 | 28 | ![Kubeconfig Page](examples/kubeconfig-page.png) 29 | 30 | 31 | ## Contributing 32 | 33 | Feel free to raise feature-requests and bugs. PR's are also very welcome. 34 | 35 | ## Alternatives 36 | 37 | - https://github.com/heptiolabs/gangway 38 | - https://github.com/micahhausler/k8s-oidc-helper 39 | - https://github.com/negz/kuberos 40 | - https://github.com/negz/kubehook 41 | - https://github.com/fydrah/loginapp 42 | 43 | This application is based on the original [example-app](https://github.com/coreos/dex/tree/master/cmd/example-app 44 | ) available in the CoreOS Dex repo. 45 | -------------------------------------------------------------------------------- /charts/README.md: -------------------------------------------------------------------------------- 1 | # Helm charts for installing 'dex' with 'dex-k8s-authenticator' 2 | 3 | The charts in this folder install [`dex`](https://github.com/coreos/dex) 4 | with [`dex-k8s-authenticator`](https://github.com/mintel/dex-k8s-authenticator) 5 | 6 | `dex-k8s-authenticator` is a helper application for `dex`. `dex` lets you use external 7 | Identify Providers (like Google, Microsoft, GitHub, LDAP) to authenticate access to Kubernetes cluster 8 | (e.g. for `kubectl`). This helper makes it easy to provide a web UI for one or more clusters. 9 | It gives users the information and commands to configure `kubectl` to work with the credentials `dex` provides. 10 | 11 | Each install of `dex` and/or `dex-k8s-authenticator` can support multiple Kubernetes clusters. 12 | So you can install one of each for all your clusters, one in each cluster, or any combination. 13 | 14 | ``` 15 | git clone https://github.com/mintel/dex-k8s-authenticator.git 16 | helm inspect values charts/dex > dex.yaml 17 | helm inspect values charts/dex-k8s-authenticator > dex-k8s-authenticator.yaml 18 | ``` 19 | Edit the values files for your environment and requirements (`dex.yaml` and `dex-k8s-authenticator.yaml`). 20 | 21 | Create the DNS names for your `dex` (e.g. 'dex.example.com') and `dex-k8s-authenticator` (e.g. 'login.example.com') 22 | pointed at the ingress controller you are using. Be sure to enable HTTPS. You can install 23 | [`cert-manager`](https://github.com/jetstack/cert-manager) to automatically issue Lets Encrypt certificates. 24 | 25 | You also need to configure each Kubernetes cluster to use `dex` at e.g. 'dex.example.com' by [setting the OIDC parameters 26 | for the Kubernetes API server](https://kubernetes.io/docs/admin/authentication/#openid-connect-tokens). 27 | This is easy using [`kube-aws` installer](https://github.com/kubernetes-incubator/kube-aws/tree/master/contrib/dex). 28 | 29 | ``` 30 | helm install --namespace dex --values dex.yaml charts/dex 31 | helm install --namespace dex --values dex-k8s-authenticator charts/dex-k8s-authenticator 32 | ``` 33 | Navigate to https://login.example.com and follow the instructions to authenticate using `dex` and configure `kubectl`. 34 | -------------------------------------------------------------------------------- /charts/dex-k8s-authenticator/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | -------------------------------------------------------------------------------- /charts/dex-k8s-authenticator/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | appVersion: "v1.4.0" 3 | description: "Authenticator for using Dex with Kubernetes" 4 | name: dex-k8s-authenticator 5 | version: 1.4.0 6 | sources: 7 | - https://github.com/mintel/dex-k8s-authenticator 8 | maintainers: 9 | - name: Nick Badger 10 | email: nbadger@mintel.com 11 | -------------------------------------------------------------------------------- /charts/dex-k8s-authenticator/README.md: -------------------------------------------------------------------------------- 1 | # Helm chart for dex-k8s-authenticator 2 | 3 | This chart installs [`dex-k8s-authenticator`](https://github.com/mintel/dex-k8s-authenticator) in a Kubernetes cluster. 4 | `dex-k8s-authenticator` is a helper application for [`dex`](https://github.com/coreos/dex). `dex` lets you use external 5 | Identify Providers (like Google, Microsoft, GitHUb, LDAP) to authenticate access to Kubernetes cluster 6 | (e.g. for `kubectl`). This helper makes it easy to provide a web UI for one or more clusters. 7 | It give uses the information and commands to configure `kubectl` to work with the credentials `dex` provides. 8 | 9 | You can configure one or more clusters in this chart configuration, for the one or more `dex` installs you may have. 10 | 11 | ```yaml 12 | # Default values for dex-k8s-authenticator. 13 | 14 | # Deploy environment label, e.g. dev, test, prod 15 | global: 16 | deployEnv: dev 17 | 18 | replicaCount: 1 19 | 20 | image: 21 | repository: mintel/dex-k8s-authenticator 22 | tag: latest 23 | pullPolicy: Always 24 | 25 | dexK8sAuthenticator: 26 | port: 5555 27 | debug: false 28 | web_path_prefix: / 29 | #logoUrl: http:// 30 | #kubectl_version: v1.16.2 31 | #tlsCert: /path/to/dex-client.crt 32 | #tlsKey: /path/to/dex-client.key 33 | clusters: 34 | - name: my-cluster 35 | short_description: "My Cluster" 36 | description: "Example Cluster Long Description..." 37 | client_secret: pUBnBOY80SnXgjibTYM9ZWNzY2xreNGQok 38 | issuer: https://dex.example.com 39 | k8s_master_uri: https://my-cluster.example.com 40 | client_id: my-cluster 41 | redirect_uri: https://login.example.com/callback/my-cluster 42 | k8s_ca_uri: https://url-to-your-ca.crt 43 | 44 | service: 45 | type: ClusterIP 46 | port: 5555 47 | # loadBalancerIP: 127.0.0.1 48 | 49 | ingress: 50 | enabled: false 51 | annotations: {} 52 | # kubernetes.io/ingress.class: nginx 53 | # kubernetes.io/tls-acme: "true" 54 | path: / 55 | hosts: 56 | - chart-example.local 57 | tls: [] 58 | # - secretName: chart-example-tls 59 | # hosts: 60 | # - chart-example.local 61 | 62 | resources: {} 63 | # We usually recommend not to specify default resources and to leave this as a conscious 64 | # choice for the user. This also increases chances charts run on environments with little 65 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 66 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 67 | # limits: 68 | # cpu: 100m 69 | # memory: 128Mi 70 | # requests: 71 | # cpu: 100m 72 | # memory: 128Mi 73 | 74 | caCerts: 75 | enabled: false 76 | secrets: {} 77 | # Array of Self Signed Certificates 78 | # cat CA.crt | base64 -w 0 79 | # 80 | # name: The internal k8s name of the secret we create. It's also used in 81 | # the volumeMount name. It must respect the k8s naming convension (avoid 82 | # upper-case and '.' to be safe). 83 | # 84 | # filename: The filename of the CA to be mounted. It must end in .crt for 85 | # update-ca-certificates to work 86 | # 87 | # value: The base64 encoded value of the CA 88 | # 89 | #secrets: 90 | #- name: ca-cert1 91 | # filename: ca1.crt 92 | # value: LS0tLS1......X2F 93 | #- name: ca-cert2 94 | # filename: ca2.crt 95 | # value: DS1tFA1......X2F 96 | 97 | 98 | nodeSelector: {} 99 | 100 | tolerations: [] 101 | 102 | affinity: {} 103 | ``` 104 | 105 | ## SSL 106 | 107 | Additional reading [here](./ssl.md) 108 | 109 | ### Adding Trusted Certs 110 | 111 | Multiple trusted certs can be added using the `caCerts` option. Make sure to `base64` encode each CA. 112 | 113 | ### Service Requests on SSL 114 | 115 | Define the filepath to your cert and key using the following options in your helm chart. 116 | - `dexK8sAuthenticator.tlsCert` 117 | - `dexK8sAuthenticator.tlsKey` 118 | 119 | TODO: Requires more work as we don't have a way deploy self-signed certs or change to https scheme. 120 | -------------------------------------------------------------------------------- /charts/dex-k8s-authenticator/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | 1. Get the application URL by running these commands: 2 | {{- if .Values.ingress.enabled }} 3 | {{- range .Values.ingress.hosts }} 4 | http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }} 5 | {{- end }} 6 | {{- else if contains "NodePort" .Values.service.type }} 7 | export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "dex-k8s-authenticator.fullname" . }}) 8 | export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") 9 | echo http://$NODE_IP:$NODE_PORT 10 | {{- else if contains "LoadBalancer" .Values.service.type }} 11 | NOTE: It may take a few minutes for the LoadBalancer IP to be available. 12 | You can watch the status of by running 'kubectl get svc -w {{ template "dex-k8s-authenticator.fullname" . }}' 13 | export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "dex-k8s-authenticator.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') 14 | echo http://$SERVICE_IP:{{ .Values.service.port }} 15 | {{- else if contains "ClusterIP" .Values.service.type }} 16 | export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "dex-k8s-authenticator.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") 17 | echo "Visit http://127.0.0.1:8080 to use your application" 18 | kubectl port-forward $POD_NAME 8080:80 19 | {{- end }} 20 | -------------------------------------------------------------------------------- /charts/dex-k8s-authenticator/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "dex-k8s-authenticator.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 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 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "dex-k8s-authenticator.fullname" -}} 15 | {{- if .Values.fullnameOverride -}} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 17 | {{- else -}} 18 | {{- $name := default .Chart.Name .Values.nameOverride -}} 19 | {{- if contains $name .Release.Name -}} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 21 | {{- else -}} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 23 | {{- end -}} 24 | {{- end -}} 25 | {{- end -}} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "dex-k8s-authenticator.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 32 | {{- end -}} 33 | 34 | {{/* 35 | Create the healthCheckPath for readiness and liveness probes. 36 | 37 | Based on the following template values: 38 | - healthCheckPath 39 | - ingress.path 40 | - dexK8sAuthenticator.web_path_prefix 41 | 42 | The default is '/healthz' 43 | */}} 44 | 45 | {{- define "dex-k8s-authenticator.healthCheckPath" -}} 46 | {{- if .Values.healthCheckPath -}} 47 | {{ .Values.healthCheckPath }} 48 | {{- else -}} 49 | {{- if .Values.ingress.enabled -}} 50 | {{ default "" .Values.ingress.path | trimSuffix "/" }}/healthz 51 | {{- else -}} 52 | {{- if .Values.dexK8sAuthenticator.web_path_prefix -}} 53 | {{ .Values.dexK8sAuthenticator.web_path_prefix | trimSuffix "/" }}/healthz 54 | {{- else -}} 55 | {{ "/healthz" }} 56 | {{- end -}} 57 | {{- end -}} 58 | {{- end -}} 59 | {{- end -}} 60 | -------------------------------------------------------------------------------- /charts/dex-k8s-authenticator/templates/ca_secrets.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.caCerts.enabled }} 2 | {{- range .Values.caCerts.secrets }} 3 | apiVersion: v1 4 | kind: Secret 5 | metadata: 6 | name: {{ template "dex-k8s-authenticator.fullname" $ }}-{{ .name }} 7 | labels: 8 | app: {{ template "dex-k8s-authenticator.name" $ }} 9 | env: {{ default "dev" $.Values.global.deployEnv }} 10 | chart: {{ template "dex-k8s-authenticator.chart" $ }} 11 | release: {{ $.Release.Name }} 12 | heritage: {{ $.Release.Service }} 13 | type: Opaque 14 | data: 15 | {{ .name }}: {{ .value }} 16 | --- 17 | {{- end }} 18 | {{- end }} 19 | -------------------------------------------------------------------------------- /charts/dex-k8s-authenticator/templates/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ template "dex-k8s-authenticator.fullname" . }} 5 | labels: 6 | app: {{ template "dex-k8s-authenticator.fullname" . }} 7 | env: {{ default "dev" .Values.global.deployEnv }} 8 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 9 | release: "{{ .Release.Name }}" 10 | heritage: "{{ .Release.Service }}" 11 | data: 12 | config.yaml: |- 13 | {{- with .Values.dexK8sAuthenticator }} 14 | listen: http://0.0.0.0:{{ default "5555" .port }} 15 | web_path_prefix: {{ default "/" .web_path_prefix }} 16 | debug: {{ default "false" .debug }} 17 | {{- if .logoUrl }} 18 | logo_uri: {{ .logoUrl }} 19 | {{- end }} 20 | {{- if .idpCaURI }} 21 | idp_ca_uri: {{ .idpCaURI }} 22 | {{- end }} 23 | {{- if .idpCaPem }} 24 | idp_ca_pem: {{ toYaml .idpCaPem | indent 4 }} 25 | {{- end }} 26 | {{- if and .tlsCert .tlsKey }} 27 | tls_cert: "{{ .tlsCert }}" 28 | tls_key: "{{ .tlsKey }}" 29 | {{- end }} 30 | {{- if .trusted_root_ca }} 31 | trusted_root_ca: {{ toYaml .trusted_root_ca | indent 4 }} 32 | {{- end }} 33 | clusters: 34 | {{ toYaml .clusters | indent 4 }} 35 | {{- end }} 36 | 37 | -------------------------------------------------------------------------------- /charts/dex-k8s-authenticator/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | {{- if semverCompare ">= 1.9-0" .Capabilities.KubeVersion.GitVersion -}} 2 | apiVersion: apps/v1 3 | {{- else if semverCompare ">= 1.8-0, <= 1.9-0" .Capabilities.KubeVersion.GitVersion -}} 4 | apiVersion: apps/v1beta2 5 | {{- else -}} 6 | apiVersion: apps/v1beta1 7 | {{- end }} 8 | kind: Deployment 9 | metadata: 10 | name: {{ template "dex-k8s-authenticator.fullname" . }} 11 | labels: 12 | app: {{ template "dex-k8s-authenticator.name" . }} 13 | env: {{ default "dev" .Values.global.deployEnv }} 14 | chart: {{ template "dex-k8s-authenticator.chart" . }} 15 | release: {{ .Release.Name }} 16 | heritage: {{ .Release.Service }} 17 | spec: 18 | replicas: {{ .Values.replicaCount }} 19 | selector: 20 | matchLabels: 21 | app: {{ template "dex-k8s-authenticator.name" . }} 22 | env: {{ default "dev" .Values.global.deployEnv }} 23 | release: {{ .Release.Name }} 24 | template: 25 | metadata: 26 | labels: 27 | app: {{ template "dex-k8s-authenticator.name" . }} 28 | env: {{ default "dev" .Values.global.deployEnv }} 29 | release: {{ .Release.Name }} 30 | annotations: 31 | checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} 32 | spec: 33 | containers: 34 | - name: {{ .Chart.Name }} 35 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" 36 | imagePullPolicy: {{ .Values.image.pullPolicy }} 37 | args: [ "--config", "config.yaml" ] 38 | {{- with .Values.envFrom }} 39 | envFrom: 40 | {{- toYaml . | nindent 10 }} 41 | {{- end }} 42 | ports: 43 | - name: http 44 | containerPort: {{ default "5555" .Values.dexK8sAuthenticator.port }} 45 | protocol: TCP 46 | livenessProbe: 47 | httpGet: 48 | path: {{ template "dex-k8s-authenticator.healthCheckPath" . }} 49 | port: http 50 | readinessProbe: 51 | httpGet: 52 | path: {{ template "dex-k8s-authenticator.healthCheckPath" . }} 53 | port: http 54 | volumeMounts: 55 | - name: config 56 | subPath: config.yaml 57 | mountPath: /app/config.yaml 58 | {{- if .Values.caCerts.enabled }} 59 | {{- range .Values.caCerts.secrets }} 60 | - name: {{ template "dex-k8s-authenticator.fullname" $ }}-{{ .name }} 61 | subPath: {{ .name }} 62 | mountPath: /certs/{{ .filename }} 63 | {{- end }} 64 | {{- end }} 65 | resources: 66 | {{ toYaml .Values.resources | indent 10 }} 67 | {{- with .Values.nodeSelector }} 68 | nodeSelector: 69 | {{ toYaml . | indent 8 }} 70 | {{- end }} 71 | {{- with .Values.affinity }} 72 | affinity: 73 | {{ toYaml . | indent 8 }} 74 | {{- end }} 75 | {{- with .Values.tolerations }} 76 | tolerations: 77 | {{ toYaml . | indent 8 }} 78 | {{- end }} 79 | {{- if .Values.imagePullSecrets }} 80 | imagePullSecrets: 81 | {{ toYaml .Values.imagePullSecrets | indent 8 }} 82 | {{- end }} 83 | volumes: 84 | - name: config 85 | configMap: 86 | name: {{ template "dex-k8s-authenticator.fullname" . }} 87 | {{- if .Values.caCerts.enabled }} 88 | {{- range .Values.caCerts.secrets }} 89 | - name: {{ template "dex-k8s-authenticator.fullname" $ }}-{{ .name }} 90 | secret: 91 | secretName: {{ template "dex-k8s-authenticator.fullname" $ }}-{{ .name }} 92 | {{- end }} 93 | {{- end }} 94 | -------------------------------------------------------------------------------- /charts/dex-k8s-authenticator/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | {{- $fullName := include "dex-k8s-authenticator.fullname" . -}} 3 | {{- $servicePort := .Values.service.port -}} 4 | {{- $ingressPath := .Values.ingress.path -}} 5 | apiVersion: extensions/v1beta1 6 | kind: Ingress 7 | metadata: 8 | name: {{ $fullName }} 9 | labels: 10 | app: {{ template "dex-k8s-authenticator.name" . }} 11 | env: {{ default "dev" .Values.global.deployEnv }} 12 | chart: {{ template "dex-k8s-authenticator.chart" . }} 13 | release: {{ .Release.Name }} 14 | heritage: {{ .Release.Service }} 15 | {{- with .Values.ingress.annotations }} 16 | annotations: 17 | {{ toYaml . | indent 4 }} 18 | {{- end }} 19 | spec: 20 | {{- if .Values.ingress.tls }} 21 | tls: 22 | {{- range .Values.ingress.tls }} 23 | - hosts: 24 | {{- range .hosts }} 25 | - {{ . }} 26 | {{- end }} 27 | secretName: {{ .secretName }} 28 | {{- end }} 29 | {{- end }} 30 | rules: 31 | {{- range .Values.ingress.hosts }} 32 | - host: {{ . }} 33 | http: 34 | paths: 35 | - path: {{ $ingressPath }} 36 | backend: 37 | serviceName: {{ $fullName }} 38 | servicePort: {{ $servicePort }} 39 | {{- end }} 40 | {{- end }} 41 | -------------------------------------------------------------------------------- /charts/dex-k8s-authenticator/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ template "dex-k8s-authenticator.fullname" . }} 5 | labels: 6 | app: {{ template "dex-k8s-authenticator.name" . }} 7 | env: {{ default "dev" .Values.global.deployEnv }} 8 | chart: {{ template "dex-k8s-authenticator.chart" . }} 9 | release: {{ .Release.Name }} 10 | heritage: {{ .Release.Service }} 11 | {{- if .Values.service.labels }} 12 | {{ toYaml .Values.service.labels | indent 4 }} 13 | {{- end }} 14 | {{- if .Values.service.annotations }} 15 | annotations: 16 | {{ toYaml .Values.service.annotations | indent 4 }} 17 | {{- end }} 18 | spec: 19 | type: {{ .Values.service.type }} 20 | ports: 21 | - port: {{ .Values.service.port }} 22 | targetPort: http 23 | protocol: TCP 24 | name: http 25 | {{- if and .Values.service.nodePort (eq "NodePort" .Values.service.type) }} 26 | nodePort: {{ .Values.service.nodePort }} 27 | {{- end }} 28 | {{- if hasKey .Values.service "loadBalancerIP" }} 29 | loadBalancerIP: {{ .Values.service.loadBalancerIP }} 30 | {{- end }} 31 | selector: 32 | app: {{ template "dex-k8s-authenticator.name" . }} 33 | release: {{ .Release.Name }} 34 | -------------------------------------------------------------------------------- /charts/dex-k8s-authenticator/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for dex-k8s-authenticator. 2 | 3 | # Deploy environment label, e.g. dev, test, prod 4 | global: 5 | deployEnv: dev 6 | 7 | replicaCount: 1 8 | 9 | image: 10 | repository: mintel/dex-k8s-authenticator 11 | tag: 1.4.0 12 | pullPolicy: Always 13 | 14 | imagePullSecrets: {} 15 | 16 | dexK8sAuthenticator: 17 | port: 5555 18 | debug: false 19 | web_path_prefix: / 20 | #logoUrl: http:// 21 | #tlsCert: /path/to/dex-client.crt 22 | #tlsKey: /path/to/dex-client.key 23 | clusters: 24 | - name: my-cluster 25 | short_description: "My Cluster" 26 | description: "Example Cluster Long Description..." 27 | client_secret: pUBnBOY80SnXgjibTYM9ZWNzY2xreNGQok 28 | issuer: https://dex.example.com 29 | k8s_master_uri: https://my-cluster.example.com 30 | client_id: my-cluster 31 | redirect_uri: https://login.example.com/callback/my-cluster 32 | k8s_ca_uri: https://url-to-your-ca.crt 33 | 34 | service: 35 | annotations: {} 36 | type: ClusterIP 37 | port: 5555 38 | # loadBalancerIP: 127.0.0.1 39 | 40 | # For nodeport, specify the following: 41 | # type: NodePort 42 | # nodePort: 43 | 44 | ingress: 45 | enabled: false 46 | annotations: {} 47 | # kubernetes.io/ingress.class: nginx 48 | # kubernetes.io/tls-acme: "true" 49 | path: / 50 | hosts: 51 | - chart-example.local 52 | tls: [] 53 | # - secretName: chart-example-tls 54 | # hosts: 55 | # - chart-example.local 56 | 57 | resources: {} 58 | # We usually recommend not to specify default resources and to leave this as a conscious 59 | # choice for the user. This also increases chances charts run on environments with little 60 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 61 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 62 | # limits: 63 | # cpu: 100m 64 | # memory: 128Mi 65 | # requests: 66 | # cpu: 100m 67 | # memory: 128Mi 68 | 69 | caCerts: 70 | enabled: false 71 | secrets: [] 72 | # Array of Self Signed Certificates 73 | # cat CA.crt | base64 -w 0 74 | # 75 | # name: The internal k8s name of the secret we create. It's also used in 76 | # the volumeMount name. It must respect the k8s naming convension (avoid 77 | # upper-case and '.' to be safe). 78 | # 79 | # filename: The filename of the CA to be mounted. It must end in .crt for 80 | # update-ca-certificates to work 81 | # 82 | # value: The base64 encoded value of the CA 83 | # 84 | #secrets: 85 | #- name: ca-cert1 86 | # filename: ca1.crt 87 | # value: LS0tLS1......X2F 88 | #- name: ca-cert2 89 | # filename: ca2.crt 90 | # value: DS1tFA1......X2F 91 | 92 | envFrom: [] 93 | 94 | nodeSelector: {} 95 | 96 | tolerations: [] 97 | 98 | affinity: {} 99 | -------------------------------------------------------------------------------- /charts/dex/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | -------------------------------------------------------------------------------- /charts/dex/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | appVersion: "2.27.0" 3 | description: "Dex federated authentication service" 4 | name: dex 5 | version: 1.4.0 6 | icon: https://github.com/coreos/dex/blob/master/Documentation/logos/dex-horizontal-color.png 7 | sources: 8 | - https://github.com/coreos/dex 9 | - https://github.com/mintel/dex-k8s-authenticator/tree/master/charts/dex 10 | maintainers: 11 | - name: Nick Badger 12 | email: nbadger@mintel.com 13 | -------------------------------------------------------------------------------- /charts/dex/README.md: -------------------------------------------------------------------------------- 1 | # Helm Chart for deploying dex 2 | 3 | This chart installs `dex` in a Kubernetes cluster. You can use this deployment for one cluster 4 | or for any number of cluster you configure under `staticClients` in the configuration. 5 | 6 | You also need to configure each Kubernetes cluster to use `dex` by [setting the OIDC parameters 7 | for the Kubernetes API server](https://kubernetes.io/docs/admin/authentication/#openid-connect-tokens). 8 | This is easy using [`kube-aws` installer](https://github.com/kubernetes-incubator/kube-aws/tree/master/contrib/dex). 9 | 10 | If you want an easy way to issue and install `kubectl` credentials, then you should also 11 | install [`dex-k8s-authenticator`](https://github.com/mintel/dex-k8s-authenticator). There 12 | is a `helm` chart available for that too (in its repo). 13 | 14 | 15 | ```yaml 16 | # Default values for dex 17 | 18 | # Deploy environment label, e.g. dev, test, prod 19 | global: 20 | deployEnv: dev 21 | 22 | replicaCount: 1 23 | 24 | image: 25 | repository: quay.io/coreos/dex 26 | tag: v2.10.0 27 | pullPolicy: IfNotPresent 28 | 29 | service: 30 | type: ClusterIP 31 | port: 5556 32 | 33 | tls: 34 | # Specify whether a TLS secret for Dex should be created 35 | # The provided certificate and key values are used to populate the 36 | # tlsCert and tlsKey values in the Dex configuration. 37 | # 38 | # If set to true, be sure to update the listen directive in the Dex 39 | # configuration to use https. 40 | create: false 41 | 42 | # Provide values for certificate and key 43 | # certificate: |- 44 | # -----BEGIN CERTIFICATE----- 45 | # ... 46 | # ----END CERTIFICATE----- 47 | # 48 | # key: |- 49 | # -----BEGIN RSA PRIVATE KEY----- 50 | # ... 51 | # -----END RSA PRIVATE KEY----- 52 | 53 | ingress: 54 | enabled: false 55 | annotations: {} 56 | # kubernetes.io/ingress.class: nginx 57 | # kubernetes.io/tls-acme: "true" 58 | path: / 59 | hosts: 60 | - dex.example.com 61 | tls: [] 62 | # - secretName: dex.example.com 63 | # hosts: 64 | # - dex.example.com 65 | 66 | rbac: 67 | # Specifies whether RBAC resources should be created 68 | create: true 69 | 70 | serviceAccount: 71 | # Specifies whether a ServiceAccount should be created 72 | create: true 73 | # The name of the ServiceAccount to use. 74 | # If not set and create is true, a name is generated using the fullname template 75 | name: 76 | 77 | resources: {} 78 | # We usually recommend not to specify default resources and to leave this as a conscious 79 | # choice for the user. This also increases chances charts run on environments with little 80 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 81 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 82 | # limits: 83 | # cpu: 100m 84 | # memory: 50Mi 85 | # requests: 86 | # cpu: 100m 87 | # memory: 50Mi 88 | 89 | nodeSelector: {} 90 | 91 | tolerations: [] 92 | 93 | affinity: {} 94 | 95 | 96 | # Configuration file for Dex 97 | # Certainly secret fields can use environment variables 98 | # 99 | config: |- 100 | issuer: https://dex.example.com 101 | 102 | storage: 103 | type: kubernetes 104 | config: 105 | inCluster: true 106 | 107 | web: 108 | http: 0.0.0.0:5556 109 | 110 | # If enabled, be sure to configure tls settings above, or use a tool 111 | # such as let-encrypt to manage the certs. 112 | # Currently this chart does not support both http and https, and the port 113 | # is fixed to 5556 114 | # 115 | # https: 0.0.0.0:5556 116 | # tlsCert: /etc/dex/tls/tls.crt 117 | # tlsKey: /etc/dex/tls/tls.key 118 | 119 | frontend: 120 | theme: "coreos" 121 | issuer: "Example Co" 122 | issuerUrl: "https://example.com" 123 | logoUrl: https://example.com/images/logo-250x25.png 124 | 125 | expiry: 126 | signingKeys: "6h" 127 | idTokens: "24h" 128 | 129 | logger: 130 | level: debug 131 | format: json 132 | 133 | oauth2: 134 | responseTypes: ["code", "token", "id_token"] 135 | skipApprovalScreen: true 136 | 137 | # Remember you can have multiple connectors of the same 'type' (with different 'id's) 138 | # If you need e.g. logins with groups for two different Microsoft 'tenants' 139 | connectors: 140 | 141 | # GitHub configure 'OAuth Apps' -> 'New OAuth App', add callback URL 142 | # https://github.com/settings/developers 143 | - type: github 144 | id: github 145 | name: GitHub 146 | config: 147 | clientID: $GITHUB_CLIENT_ID 148 | clientSecret: $GITHUB_CLIENT_SECRET 149 | redirectURI: https://dex.example.com/callback 150 | # 'orgs' can be used to map groups from Github 151 | # https://github.com/coreos/dex/blob/master/Documentation/connectors/github.md 152 | #orgs: 153 | #- name: foo 154 | # teams: 155 | # - team-red 156 | # - team-blue 157 | #- name: bar 158 | 159 | # Google APIs account, 'Create Credentials' -> 'OAuth Client ID', add callback URL 160 | # https://console.developers.google.com/apis/credentials 161 | - type: oidc 162 | id: google 163 | name: Google 164 | config: 165 | issuer: https://accounts.google.com 166 | clientID: $GOOGLE_CLIENT_ID 167 | clientSecret: $GOOGLE_CLIENT_SECRET 168 | redirectURI: https://dex.example.com/callback 169 | # Google supports whitelisting allowed domains when using G Suite 170 | # (Google Apps). The following field can be set to a list of domains 171 | # that can log in: 172 | # hostedDomains: 173 | # - example.com 174 | # - other.example.com 175 | 176 | # Microsoft App Dev account, 'Add an app' 177 | # 'Application Secrets' -> 'Generate new password' 178 | # 'Platforms' -> 'Add Platform' -> 'Web', add the callback URL 179 | # https://apps.dev.microsoft.com/ 180 | - type: microsoft 181 | id: microsoft 182 | name: Microsoft 183 | config: 184 | clientID: $MICROSOFT_APPLICATION_ID 185 | clientSecret: $MICROSOFT_CLIENT_SECRET 186 | redirectURI: https://dex.example.com/callback 187 | # Restrict access to one tenant 188 | # tenant: or 189 | # Restrict access to certain groups 190 | # groups: 191 | # - group-red 192 | # - group-blue 193 | 194 | # These may not match the schema used by your LDAP server 195 | # https://github.com/coreos/dex/blob/master/Documentation/connectors/ldap.md 196 | - type: ldap 197 | id: ldap 198 | name: "LDAP" 199 | config: 200 | host: ldap.example.com:389 201 | startTLS: true 202 | bindDN: "cn=serviceAccount,dc=example,dc=com" 203 | bindPW: $LDAP_BINDPW 204 | usernamePrompt: "Username" 205 | userSearch: 206 | # Query should be "(&(objectClass=inetorgperson)(cn=))" 207 | baseDN: "ou=Users,dc=example,dc=com" 208 | filter: "(objectClass=inetorgperson)" 209 | username: cn 210 | # DN must be in capitals 211 | idAttr: DN 212 | emailAttr: mail 213 | nameAttr: displayName 214 | groupSearch: 215 | # Query should be "(&(objectClass=groupOfUniqueNames)(uniqueMember=))" 216 | baseDN: "ou=Groups,dc=example,dc=com" 217 | filter: "(objectClass=groupOfUniqueNames)" 218 | # DN must be in capitals 219 | userAttr: DN 220 | groupAttr: uniqueMember 221 | nameAttr: cn 222 | 223 | # The 'name' must match the k8s API server's 'oidc-client-id' 224 | staticClients: 225 | - id: my-cluster 226 | name: "my-cluster" 227 | secret: "pUBnBOY80SnXgjibTYM9ZWNzY2xreNGQok" 228 | redirectURIs: 229 | - https://login.example.com/callback/my-cluster 230 | 231 | enablePasswordDB: True 232 | staticPasswords: 233 | - email: "admin@example.com" 234 | # bcrypt hash of the string "password" 235 | hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" 236 | username: "admin" 237 | userID: "08a8684b-db88-4b73-90a9-3cd1661f5466" 238 | 239 | 240 | # You should not enter your secrets here if this file will be stored in source control 241 | # Instead create a separate file to hold or override these values 242 | # You need only list the environment variables you used in the 'config' above 243 | # You can add any additional ones you need, or remove ones you don't need 244 | # 245 | envSecrets: 246 | # GitHub 247 | GITHUB_CLIENT_ID: "override-me" 248 | GITHUB_CLIENT_SECRET: "override-me" 249 | # Google (oidc) 250 | GOOGLE_CLIENT_ID: "override-me" 251 | GOOGLE_CLIENT_SECRET: "override-me" 252 | # Microsoft 253 | MICROSOFT_APPLICATION_ID: "override-me" 254 | MICROSOFT_CLIENT_SECRET: "override-me" 255 | # LDAP 256 | LDAP_BINDPW: "override-me" 257 | ``` -------------------------------------------------------------------------------- /charts/dex/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | 1. Get the application URL by running these commands: 2 | {{- if .Values.ingress.enabled }} 3 | {{- range .Values.ingress.hosts }} 4 | http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }} 5 | {{- end }} 6 | {{- else if contains "NodePort" .Values.service.type }} 7 | export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "dex.fullname" . }}) 8 | export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") 9 | echo http://$NODE_IP:$NODE_PORT 10 | {{- else if contains "LoadBalancer" .Values.service.type }} 11 | NOTE: It may take a few minutes for the LoadBalancer IP to be available. 12 | You can watch the status of by running 'kubectl get svc -w {{ template "dex.fullname" . }}' 13 | export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "dex.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') 14 | echo http://$SERVICE_IP:{{ .Values.service.port }} 15 | {{- else if contains "ClusterIP" .Values.service.type }} 16 | export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "dex.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") 17 | echo "Visit http://127.0.0.1:8080 to use your application" 18 | kubectl port-forward $POD_NAME 8080:80 19 | {{- end }} 20 | -------------------------------------------------------------------------------- /charts/dex/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "dex.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 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 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "dex.fullname" -}} 15 | {{- if .Values.fullnameOverride -}} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 17 | {{- else -}} 18 | {{- $name := default .Chart.Name .Values.nameOverride -}} 19 | {{- if contains $name .Release.Name -}} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 21 | {{- else -}} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 23 | {{- end -}} 24 | {{- end -}} 25 | {{- end -}} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "dex.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 32 | {{- end -}} 33 | 34 | {{/* 35 | Create the name of the service account to use 36 | */}} 37 | {{- define "dex.serviceAccountName" -}} 38 | {{- if .Values.serviceAccount.create -}} 39 | {{ default (include "dex.fullname" .) .Values.serviceAccount.name }} 40 | {{- else -}} 41 | {{ default "default" .Values.serviceAccount.name }} 42 | {{- end -}} 43 | {{- end -}} 44 | 45 | {{/* 46 | Create the health check path 47 | */}} 48 | 49 | {{/* 50 | Create secret key from environment variables 51 | */}} 52 | {{- define "dex.envkey" -}} 53 | {{ . | replace "_" "-" | lower }} 54 | {{- end -}} 55 | 56 | {{/* 57 | Create the healthCheckPath for readiness and liveness probes. 58 | 59 | Based on the following template values: 60 | - healthCheckPath 61 | - ingress.path 62 | 63 | The default is '/healthz' 64 | */}} 65 | 66 | {{- define "dex.healthCheckPath" -}} 67 | {{- if .Values.healthCheckPath -}} 68 | {{ .Values.healthCheckPath }} 69 | {{- else -}} 70 | {{- if .Values.ingress.enabled -}} 71 | {{ default "" .Values.ingress.path | trimSuffix "/" }}/healthz 72 | {{- else -}} 73 | {{ default "/healthz" }} 74 | {{- end -}} 75 | {{- end -}} 76 | {{- end -}} 77 | -------------------------------------------------------------------------------- /charts/dex/templates/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ template "dex.fullname" . }} 5 | labels: 6 | app: {{ template "dex.fullname" . }} 7 | env: {{ default "dev" .Values.global.deployEnv }} 8 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 9 | release: "{{ .Release.Name }}" 10 | heritage: "{{ .Release.Service }}" 11 | data: 12 | config.yaml: |- 13 | {{ .Values.config | indent 4 }} 14 | -------------------------------------------------------------------------------- /charts/dex/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | {{- if semverCompare ">= 1.9-0" .Capabilities.KubeVersion.GitVersion -}} 2 | apiVersion: apps/v1 3 | {{- else if semverCompare ">= 1.8-0, <= 1.9-0" .Capabilities.KubeVersion.GitVersion -}} 4 | apiVersion: apps/v1beta2 5 | {{- else -}} 6 | apiVersion: apps/v1beta1 7 | {{- end }} 8 | kind: Deployment 9 | metadata: 10 | name: {{ template "dex.fullname" . }} 11 | labels: 12 | app: {{ template "dex.name" . }} 13 | env: {{ default "dev" .Values.global.deployEnv }} 14 | chart: {{ template "dex.chart" . }} 15 | release: {{ .Release.Name }} 16 | heritage: {{ .Release.Service }} 17 | annotations: 18 | scheduler.alpha.kubernetes.io/critical-pod: '' 19 | spec: 20 | replicas: {{ .Values.replicaCount }} 21 | minReadySeconds: 30 22 | strategy: 23 | rollingUpdate: 24 | maxUnavailable: 0 25 | selector: 26 | matchLabels: 27 | app: {{ template "dex.name" . }} 28 | env: {{ default "dev" .Values.global.deployEnv }} 29 | release: {{ .Release.Name }} 30 | template: 31 | metadata: 32 | labels: 33 | app: {{ template "dex.name" . }} 34 | env: {{ default "dev" .Values.global.deployEnv }} 35 | release: {{ .Release.Name }} 36 | annotations: 37 | checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} 38 | spec: 39 | volumes: 40 | - name: config 41 | configMap: 42 | name: {{ template "dex.fullname" . }} 43 | items: 44 | - key: config.yaml 45 | path: config.yaml 46 | {{- if .Values.tls.create }} 47 | - name: tls 48 | secret: 49 | secretName: {{ template "dex.fullname" $ }}-tls 50 | {{- end }} 51 | serviceAccountName: {{ template "dex.serviceAccountName" . }} 52 | containers: 53 | - name: {{ .Chart.Name }} 54 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" 55 | imagePullPolicy: {{ .Values.image.pullPolicy }} 56 | command: ["/usr/local/bin/dex", "serve", "/etc/dex/config.yaml"] 57 | env: 58 | {{- range $key, $value := .Values.envSecrets }} 59 | - name: {{ $key }} 60 | valueFrom: 61 | secretKeyRef: 62 | name: {{ template "dex.fullname" $ }} 63 | key: {{ template "dex.envkey" $key }} 64 | {{- end }} 65 | ports: 66 | - name: http 67 | containerPort: 5556 68 | protocol: TCP 69 | livenessProbe: 70 | httpGet: 71 | path: {{ template "dex.healthCheckPath" . }} 72 | port: 5556 73 | {{- if .Values.tls.create }} 74 | scheme: HTTPS 75 | {{- end }} 76 | readinessProbe: 77 | httpGet: 78 | path: {{ template "dex.healthCheckPath" . }} 79 | port: 5556 80 | {{- if .Values.tls.create }} 81 | scheme: HTTPS 82 | {{- end }} 83 | initialDelaySeconds: 5 84 | timeoutSeconds: 1 85 | volumeMounts: 86 | - name: config 87 | mountPath: /etc/dex 88 | {{- if .Values.tls.create }} 89 | - name: tls 90 | mountPath: /etc/dex/tls 91 | {{- end }} 92 | resources: 93 | {{ toYaml .Values.resources | indent 10 }} 94 | {{- with .Values.nodeSelector }} 95 | nodeSelector: 96 | {{ toYaml . | indent 6 }} 97 | {{- end }} 98 | {{- with .Values.affinity }} 99 | affinity: 100 | {{ toYaml . | indent 8 }} 101 | {{- end }} 102 | {{- with .Values.tolerations }} 103 | tolerations: 104 | {{ toYaml . | indent 6 }} 105 | {{- end }} 106 | -------------------------------------------------------------------------------- /charts/dex/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | {{- $fullName := include "dex.fullname" . -}} 3 | {{- $servicePort := .Values.service.port -}} 4 | {{- $ingressPath := .Values.ingress.path -}} 5 | apiVersion: extensions/v1beta1 6 | kind: Ingress 7 | metadata: 8 | name: {{ $fullName }} 9 | labels: 10 | app: {{ template "dex.name" . }} 11 | env: {{ default "dev" .Values.global.deployEnv }} 12 | chart: {{ template "dex.chart" . }} 13 | release: {{ .Release.Name }} 14 | heritage: {{ .Release.Service }} 15 | {{- with .Values.ingress.annotations }} 16 | annotations: 17 | {{ toYaml . | indent 4 }} 18 | {{- end }} 19 | spec: 20 | {{- if .Values.ingress.tls }} 21 | tls: 22 | {{- range .Values.ingress.tls }} 23 | - hosts: 24 | {{- range .hosts }} 25 | - {{ . }} 26 | {{- end }} 27 | secretName: {{ .secretName }} 28 | {{- end }} 29 | {{- end }} 30 | rules: 31 | {{- range .Values.ingress.hosts }} 32 | - host: {{ . }} 33 | http: 34 | paths: 35 | - path: {{ $ingressPath }} 36 | backend: 37 | serviceName: {{ $fullName }} 38 | servicePort: {{ $servicePort }} 39 | {{- end }} 40 | {{- end }} 41 | -------------------------------------------------------------------------------- /charts/dex/templates/rbac.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.rbac.create }} 2 | apiVersion: rbac.authorization.k8s.io/v1beta1 3 | kind: ClusterRole 4 | metadata: 5 | name: {{ template "dex.fullname" . }} 6 | rules: 7 | - apiGroups: 8 | - apiextensions.k8s.io 9 | resources: 10 | - customresourcedefinitions 11 | verbs: 12 | - "*" 13 | --- 14 | apiVersion: rbac.authorization.k8s.io/v1beta1 15 | kind: Role 16 | metadata: 17 | name: {{ template "dex.fullname" . }} 18 | namespace: {{ .Release.Namespace }} 19 | rules: 20 | - apiGroups: 21 | - dex.coreos.com 22 | resources: 23 | - authcodes 24 | - authrequests 25 | - connectors 26 | - oauth2clients 27 | - offlinesessionses 28 | - passwords 29 | - refreshtokens 30 | - signingkeies 31 | verbs: 32 | - "*" 33 | --- 34 | apiVersion: rbac.authorization.k8s.io/v1beta1 35 | kind: ClusterRoleBinding 36 | metadata: 37 | name: {{ template "dex.fullname" . }} 38 | roleRef: 39 | apiGroup: rbac.authorization.k8s.io 40 | kind: ClusterRole 41 | name: {{ template "dex.fullname" . }} 42 | subjects: 43 | - kind: ServiceAccount 44 | name: {{ template "dex.serviceAccountName" . }} 45 | namespace: {{ .Release.Namespace }} 46 | --- 47 | apiVersion: rbac.authorization.k8s.io/v1beta1 48 | kind: RoleBinding 49 | metadata: 50 | name: {{ template "dex.fullname" . }} 51 | namespace: {{ .Release.Namespace }} 52 | roleRef: 53 | apiGroup: rbac.authorization.k8s.io 54 | kind: Role 55 | name: {{ template "dex.fullname" . }} 56 | subjects: 57 | - kind: ServiceAccount 58 | name: {{ template "dex.serviceAccountName" . }} 59 | namespace: {{ .Release.Namespace }} 60 | {{- end }} 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /charts/dex/templates/secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: {{ template "dex.fullname" . }} 5 | labels: 6 | app: {{ template "dex.fullname" . }} 7 | env: {{ default "dev" .Values.global.deployEnv }} 8 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 9 | release: "{{ .Release.Name }}" 10 | heritage: "{{ .Release.Service }}" 11 | data: 12 | {{- range $key, $value := .Values.envSecrets }} 13 | {{ template "dex.envkey" $key }}: "{{ $value | b64enc }}" 14 | {{- end }} 15 | {{- if .Values.tls.create }} 16 | --- 17 | apiVersion: v1 18 | kind: Secret 19 | metadata: 20 | name: {{ template "dex.fullname" . }}-tls 21 | labels: 22 | app: {{ template "dex.fullname" . }} 23 | env: {{ default "dev" .Values.global.deployEnv }} 24 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 25 | release: "{{ .Release.Name }}" 26 | heritage: "{{ .Release.Service }}" 27 | type: kubernetes.io/tls 28 | data: 29 | tls.crt: {{ .Values.tls.certificate | b64enc }} 30 | tls.key: {{ .Values.tls.key | b64enc }} 31 | {{- end }} 32 | -------------------------------------------------------------------------------- /charts/dex/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ template "dex.fullname" . }} 5 | labels: 6 | app: {{ template "dex.name" . }} 7 | env: {{ default "dev" .Values.global.deployEnv }} 8 | chart: {{ template "dex.chart" . }} 9 | release: {{ .Release.Name }} 10 | heritage: {{ .Release.Service }} 11 | spec: 12 | type: {{ .Values.service.type }} 13 | ports: 14 | - port: {{ .Values.service.port }} 15 | targetPort: http 16 | protocol: TCP 17 | name: http 18 | {{- if and .Values.service.nodePort (eq "NodePort" .Values.service.type) }} 19 | nodePort: {{ .Values.service.nodePort }} 20 | {{- end }} 21 | selector: 22 | app: {{ template "dex.name" . }} 23 | release: {{ .Release.Name }} 24 | -------------------------------------------------------------------------------- /charts/dex/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create }} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ template "dex.serviceAccountName" . }} 6 | labels: 7 | app: {{ template "dex.fullname" . }} 8 | env: {{ default "dev" .Values.global.deployEnv }} 9 | chart: {{ template "dex.chart" . }} 10 | release: "{{ .Release.Name }}" 11 | heritage: "{{ .Release.Service }}" 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /charts/dex/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for dex 2 | 3 | # Deploy environment label, e.g. dev, test, prod 4 | global: 5 | deployEnv: dev 6 | 7 | replicaCount: 1 8 | 9 | image: 10 | repository: dexidp/dex 11 | tag: v2.27.0 12 | pullPolicy: IfNotPresent 13 | 14 | service: 15 | type: ClusterIP 16 | port: 5556 17 | 18 | # For nodeport, specify the following: 19 | # type: NodePort 20 | # nodePort: 21 | 22 | tls: 23 | # Specify whether a TLS secret for Dex should be created 24 | # The provided certificate and key values are used to populate the 25 | # tlsCert and tlsKey values in the Dex configuration. 26 | # 27 | # If set to true, be sure to update the listen directive in the Dex 28 | # configuration to use https. 29 | create: false 30 | 31 | # Provide values for certificate and key 32 | # certificate: |- 33 | # -----BEGIN CERTIFICATE----- 34 | # ... 35 | # ----END CERTIFICATE----- 36 | # 37 | # key: |- 38 | # -----BEGIN RSA PRIVATE KEY----- 39 | # ... 40 | # -----END RSA PRIVATE KEY----- 41 | 42 | ingress: 43 | enabled: false 44 | annotations: {} 45 | # kubernetes.io/ingress.class: nginx 46 | # kubernetes.io/tls-acme: "true" 47 | path: / 48 | hosts: 49 | - dex.example.com 50 | tls: [] 51 | # - secretName: dex.example.com 52 | # hosts: 53 | # - dex.example.com 54 | 55 | rbac: 56 | # Specifies whether RBAC resources should be created 57 | create: true 58 | 59 | serviceAccount: 60 | # Specifies whether a ServiceAccount should be created 61 | create: true 62 | # The name of the ServiceAccount to use. 63 | # If not set and create is true, a name is generated using the fullname template 64 | name: 65 | 66 | resources: {} 67 | # We usually recommend not to specify default resources and to leave this as a conscious 68 | # choice for the user. This also increases chances charts run on environments with little 69 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 70 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 71 | # limits: 72 | # cpu: 100m 73 | # memory: 50Mi 74 | # requests: 75 | # cpu: 100m 76 | # memory: 50Mi 77 | 78 | nodeSelector: {} 79 | 80 | tolerations: [] 81 | 82 | affinity: {} 83 | 84 | 85 | # Configuration file for Dex 86 | # Certainly secret fields can use environment variables 87 | # 88 | config: |- 89 | issuer: https://dex.example.com 90 | 91 | storage: 92 | type: kubernetes 93 | config: 94 | inCluster: true 95 | 96 | web: 97 | http: 0.0.0.0:5556 98 | 99 | # If enabled, be sure to configure tls settings above, or use a tool 100 | # such as let-encrypt to manage the certs. 101 | # Currently this chart does not support both http and https, and the port 102 | # is fixed to 5556 103 | # 104 | # https: 0.0.0.0:5556 105 | # tlsCert: /etc/dex/tls/tls.crt 106 | # tlsKey: /etc/dex/tls/tls.key 107 | 108 | frontend: 109 | theme: "coreos" 110 | issuer: "Example Co" 111 | issuerUrl: "https://example.com" 112 | logoUrl: https://example.com/images/logo-250x25.png 113 | 114 | expiry: 115 | signingKeys: "6h" 116 | idTokens: "24h" 117 | 118 | logger: 119 | level: debug 120 | format: json 121 | 122 | oauth2: 123 | responseTypes: ["code", "token", "id_token"] 124 | skipApprovalScreen: true 125 | 126 | # Remember you can have multiple connectors of the same 'type' (with different 'id's) 127 | # If you need e.g. logins with groups for two different Microsoft 'tenants' 128 | connectors: 129 | 130 | # GitHub configure 'OAuth Apps' -> 'New OAuth App', add callback URL 131 | # https://github.com/settings/developers 132 | - type: github 133 | id: github 134 | name: GitHub 135 | config: 136 | clientID: $GITHUB_CLIENT_ID 137 | clientSecret: $GITHUB_CLIENT_SECRET 138 | redirectURI: https://dex.example.com/callback 139 | # 'orgs' can be used to map groups from Github 140 | # https://github.com/coreos/dex/blob/master/Documentation/connectors/github.md 141 | #orgs: 142 | #- name: foo 143 | # teams: 144 | # - team-red 145 | # - team-blue 146 | #- name: bar 147 | 148 | # Google APIs account, 'Create Credentials' -> 'OAuth Client ID', add callback URL 149 | # https://console.developers.google.com/apis/credentials 150 | - type: oidc 151 | id: google 152 | name: Google 153 | config: 154 | issuer: https://accounts.google.com 155 | clientID: $GOOGLE_CLIENT_ID 156 | clientSecret: $GOOGLE_CLIENT_SECRET 157 | redirectURI: https://dex.example.com/callback 158 | # Google supports whitelisting allowed domains when using G Suite 159 | # (Google Apps). The following field can be set to a list of domains 160 | # that can log in: 161 | # hostedDomains: 162 | # - example.com 163 | # - other.example.com 164 | 165 | # Microsoft App Dev account, 'Add an app' 166 | # 'Application Secrets' -> 'Generate new password' 167 | # 'Platforms' -> 'Add Platform' -> 'Web', add the callback URL 168 | # https://apps.dev.microsoft.com/ 169 | - type: microsoft 170 | id: microsoft 171 | name: Microsoft 172 | config: 173 | clientID: $MICROSOFT_APPLICATION_ID 174 | clientSecret: $MICROSOFT_CLIENT_SECRET 175 | redirectURI: https://dex.example.com/callback 176 | # Restrict access to one tenant 177 | # tenant: or 178 | # Restrict access to certain groups 179 | # groups: 180 | # - group-red 181 | # - group-blue 182 | 183 | # These may not match the schema used by your LDAP server 184 | # https://github.com/coreos/dex/blob/master/Documentation/connectors/ldap.md 185 | - type: ldap 186 | id: ldap 187 | name: "LDAP" 188 | config: 189 | host: ldap.example.com:389 190 | startTLS: true 191 | bindDN: "cn=serviceAccount,dc=example,dc=com" 192 | bindPW: $LDAP_BINDPW 193 | usernamePrompt: "Username" 194 | userSearch: 195 | # Query should be "(&(objectClass=inetorgperson)(cn=))" 196 | baseDN: "ou=Users,dc=example,dc=com" 197 | filter: "(objectClass=inetorgperson)" 198 | username: cn 199 | # DN must be in capitals 200 | idAttr: DN 201 | emailAttr: mail 202 | nameAttr: displayName 203 | groupSearch: 204 | # Query should be "(&(objectClass=groupOfUniqueNames)(uniqueMember=))" 205 | baseDN: "ou=Groups,dc=example,dc=com" 206 | filter: "(objectClass=groupOfUniqueNames)" 207 | # DN must be in capitals 208 | userAttr: DN 209 | groupAttr: uniqueMember 210 | nameAttr: cn 211 | 212 | # The 'name' must match the k8s API server's 'oidc-client-id' 213 | staticClients: 214 | - id: my-cluster 215 | name: "my-cluster" 216 | secret: "pUBnBOY80SnXgjibTYM9ZWNzY2xreNGQok" 217 | redirectURIs: 218 | - https://login.example.com/callback/my-cluster 219 | 220 | enablePasswordDB: True 221 | staticPasswords: 222 | - email: "admin@example.com" 223 | # bcrypt hash of the string "password" 224 | hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" 225 | username: "admin" 226 | userID: "08a8684b-db88-4b73-90a9-3cd1661f5466" 227 | 228 | 229 | # You should not enter your secrets here if this file will be stored in source control 230 | # Instead create a separate file to hold or override these values 231 | # You need only list the environment variables you used in the 'config' above 232 | # You can add any additional ones you need, or remove ones you don't need 233 | # 234 | envSecrets: 235 | # GitHub 236 | GITHUB_CLIENT_ID: "override-me" 237 | GITHUB_CLIENT_SECRET: "override-me" 238 | # Google (oidc) 239 | GOOGLE_CLIENT_ID: "override-me" 240 | GOOGLE_CLIENT_SECRET: "override-me" 241 | # Microsoft 242 | MICROSOFT_APPLICATION_ID: "override-me" 243 | MICROSOFT_CLIENT_SECRET: "override-me" 244 | # LDAP 245 | LDAP_BINDPW: "override-me" 246 | -------------------------------------------------------------------------------- /dex-auth.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "io/ioutil" 8 | "log" 9 | "net/http" 10 | "path" 11 | "time" 12 | 13 | "github.com/coreos/go-oidc" 14 | "github.com/spf13/cast" 15 | "golang.org/x/oauth2" 16 | ) 17 | 18 | const exampleAppState = "Vgn2lp5QnymFtLntKX5dM8k773PwcM87T4hQtiESC1q8wkUBgw5D3kH0r5qJ" 19 | 20 | func (cluster *Cluster) oauth2Config() *oauth2.Config { 21 | 22 | return &oauth2.Config{ 23 | ClientID: cluster.Client_ID, 24 | ClientSecret: cluster.Client_Secret, 25 | Endpoint: cluster.Provider.Endpoint(), 26 | Scopes: cluster.Scopes, 27 | RedirectURL: cluster.Redirect_URI, 28 | } 29 | } 30 | 31 | func (config *Config) handleIndex(w http.ResponseWriter, r *http.Request) { 32 | 33 | if len(config.Clusters) == 1 && r.URL.String() == config.Web_Path_Prefix { 34 | http.Redirect(w, r, path.Join(config.Web_Path_Prefix, "login", config.Clusters[0].Name), http.StatusSeeOther) 35 | } else { 36 | renderIndex(w, config) 37 | } 38 | } 39 | 40 | func (cluster *Cluster) handleLogin(w http.ResponseWriter, r *http.Request) { 41 | log.Printf("Handling login-uri for: %s", cluster.Name) 42 | authCodeURL := cluster.oauth2Config().AuthCodeURL(exampleAppState, oauth2.AccessTypeOffline) 43 | if cluster.Connector_ID != "" { 44 | log.Printf("Using dex connector with id %#q", cluster.Connector_ID) 45 | authCodeURL = fmt.Sprintf("%s&connector_id=%s", authCodeURL, cluster.Connector_ID) 46 | } 47 | log.Printf("Redirecting post-loginto: %s", authCodeURL) 48 | http.Redirect(w, r, authCodeURL, http.StatusSeeOther) 49 | } 50 | 51 | func (cluster *Cluster) handleCallback(w http.ResponseWriter, r *http.Request) { 52 | var ( 53 | err error 54 | token *oauth2.Token 55 | IdpCaPem string 56 | ) 57 | 58 | // An error message to that presented to the user 59 | userErrorMsg := "Invalid token request" 60 | 61 | log.Printf("Handling callback for: %s", cluster.Name) 62 | 63 | ctx := oidc.ClientContext(r.Context(), cluster.Client) 64 | oauth2Config := cluster.oauth2Config() 65 | switch r.Method { 66 | case "GET": 67 | // Authorization redirect callback from OAuth2 auth flow. 68 | if errMsg := r.FormValue("error"); errMsg != "" { 69 | cluster.renderHTMLError(w, userErrorMsg, http.StatusBadRequest) 70 | log.Printf("handleCallback: request error. error: %s, error_description: %s", errMsg, r.FormValue("error_description")) 71 | return 72 | } 73 | code := r.FormValue("code") 74 | if code == "" { 75 | cluster.renderHTMLError(w, userErrorMsg, http.StatusBadRequest) 76 | log.Printf("handleCallback: no code in request: %q", r.Form) 77 | return 78 | } 79 | if state := r.FormValue("state"); state != exampleAppState { 80 | cluster.renderHTMLError(w, userErrorMsg, http.StatusBadRequest) 81 | log.Printf("handleCallback: expected state %q got %q", exampleAppState, state) 82 | return 83 | } 84 | token, err = oauth2Config.Exchange(ctx, code) 85 | case "POST": 86 | // Form request from frontend to refresh a token. 87 | refresh := r.FormValue("refresh_token") 88 | if refresh == "" { 89 | cluster.renderHTMLError(w, userErrorMsg, http.StatusBadRequest) 90 | log.Printf("handleCallback: no refresh_token in request: %q", r.Form) 91 | return 92 | } 93 | t := &oauth2.Token{ 94 | RefreshToken: refresh, 95 | Expiry: time.Now().Add(-time.Hour), 96 | } 97 | token, err = oauth2Config.TokenSource(ctx, t).Token() 98 | default: 99 | // Return non-HTML error for non GET/POST requests which probably wasn't executed by browser 100 | http.Error(w, fmt.Sprintf("Method not implemented: %s", r.Method), http.StatusBadRequest) 101 | return 102 | } 103 | 104 | if err != nil { 105 | cluster.renderHTMLError(w, userErrorMsg, http.StatusBadRequest) 106 | log.Printf("handleCallback: failed to get token: %v", err) 107 | return 108 | } 109 | 110 | rawIDToken, ok := token.Extra("id_token").(string) 111 | if !ok { 112 | cluster.renderHTMLError(w, userErrorMsg, http.StatusBadRequest) 113 | log.Printf("handleCallback: no id_token in response: %q", token) 114 | return 115 | } 116 | 117 | idToken, err := cluster.Verifier.Verify(r.Context(), rawIDToken) 118 | if err != nil { 119 | cluster.renderHTMLError(w, userErrorMsg, http.StatusBadRequest) 120 | log.Printf("handleCallback: failed to verify ID token: %q, err: %v", rawIDToken, err) 121 | return 122 | } 123 | var claims json.RawMessage 124 | if err = idToken.Claims(&claims); err != nil { 125 | cluster.renderHTMLError(w, userErrorMsg, http.StatusBadRequest) 126 | log.Printf("handleCallback: failed to unmarshal json payload of ID token into claims: %v", err) 127 | return 128 | } 129 | 130 | buff := new(bytes.Buffer) 131 | if err = json.Indent(buff, []byte(claims), "", " "); err != nil { 132 | cluster.renderHTMLError(w, userErrorMsg, http.StatusBadRequest) 133 | log.Printf("handleCallback: failed to indent json: %v", err) 134 | return 135 | 136 | } 137 | 138 | if cluster.Config.IDP_Ca_Pem != "" { 139 | IdpCaPem = cluster.Config.IDP_Ca_Pem 140 | } else if cluster.Config.IDP_Ca_Pem_File != "" { 141 | content, err := ioutil.ReadFile(cluster.Config.IDP_Ca_Pem_File) 142 | if err != nil { 143 | log.Fatalf("Failed to load CA from file %s, %s", cluster.Config.IDP_Ca_Pem_File, err) 144 | } 145 | IdpCaPem = cast.ToString(content) 146 | } 147 | 148 | cluster.renderToken(w, rawIDToken, token.RefreshToken, 149 | cluster.Config.IDP_Ca_URI, 150 | IdpCaPem, 151 | cluster.Config.Logo_Uri, 152 | cluster.Config.Web_Path_Prefix, 153 | cluster.Config.Kubectl_Version, 154 | buff.Bytes()) 155 | } 156 | -------------------------------------------------------------------------------- /docs/config.md: -------------------------------------------------------------------------------- 1 | # Configuration 2 | 3 | An example configuration is available [here](../examples/config.yaml) 4 | 5 | ## Configuration options 6 | 7 | 8 | | Name | Required | Context | Description | 9 | |---------------------------|----------|---------|---------------------------------------------------------------------------------------| 10 | | name | yes | cluster | Internal id of cluster | 11 | | short_description | yes | cluster | Short description of cluster | 12 | | description | yes | cluster | Extended description of cluster | 13 | | client_secret | yes | cluster | OAuth2 client-secret (shared between dex-k8s-auth and dex) | 14 | | client_id | yes | cluster | OAuth2 client-id public identifier (shared between dex-k8s-auth and dex) | 15 | | connector_id | no | cluster | Dex connector ID to use by default omitting other available connectors | 16 | | issuer | yes | cluster | Dex issuer url | 17 | | redirect_uri | yes | cluster | Redirect uri called by dex (defines a callback on dex-k8s-auth) | 18 | | k8s_master_uri | no | cluster | Kubernetes api-server endpoint (used in kubeconfig) | 19 | | k8s_ca_uri | no | cluster | A url pointing to the CA for generating 'certificate-authority' option in kubeconfig | 20 | | k8s_ca_pem | no | cluster | The CA for your k8s server (used in generating instructions) | 21 | | k8s_ca_pem_base64_encoded | no | cluster | The Base64 encoded CA for your k8s server (used in generating instructions) | 22 | | k8s_ca_pem_file | no | cluster | The CA file for your k8s server (used in generating instructions) | 23 | | scopes | no | cluster | A list OpenID scopes to request | 24 | | tls_cert | no | root | Path to TLS cert if SSL enabled | 25 | | tls_key | no | root | Path to TLS key if SSL enabled | 26 | | idp_ca_uri | no | root | A url pointing to the CA for generating 'idp-certificate-authority' in the kubeconfig | 27 | | idp_ca_pem | no | root | The CA for generating 'idp-certificate-authority' in the kubeconfig | 28 | | idp_ca_pem_file | no | root | The CA for generating 'idp-certificate-authority' in the kubeconfig - load from file | 29 | | trusted_root_ca | no | root | A list of trusted-root CA's to be loaded by dex-k8s-auth at runtime | 30 | | trusted_root_ca_file | no | root | A list of trusted-root CA's to be loaded by dex-k8s-auth at runtime - load from file | 31 | | listen | yes | root | The listen address/port | 32 | | web_path_prefix | no | root | A path-prefix to serve dex-k8s-auth at (defaults to '/') | 33 | | kubectl_version | no | root | A kubectl-version string that is used to provided a download path | 34 | | logo_uri | no | root | A url pointing to a logo image that is displayed in the header | 35 | | debug | no | root | Enable more debug by setting to true | 36 | 37 | ## Environment Variable Support 38 | 39 | Environment variables can be included in the configuration. This enables you to pass in dynamic variables or secrets without having to hardcode them. 40 | ``` 41 | clusters: 42 | - name: example-cluster 43 | short_description: "Example Cluster" 44 | description: "Example Cluster Long Description..." 45 | client_secret: ${CLIENT_SECRET_EXAMPLE_CLUSTER} 46 | ``` 47 | 48 | In this example, `${CLIENT_SECRET_EXAMPLE_CLUSTER}` is substituted at runtime. Must be enclosed by `${...}` 49 | 50 | ## Web Path Prefix 51 | 52 | A common practise is configure `dex-k8s-authenticator` to serve requests under a specific path prefix, such as `https://mycompany.example.com/dex-auth` 53 | 54 | You can achieve this by configuring the `web_path_prefix`. In most cases you will need to adjust the Ingress `path` setting to match. 55 | 56 | In addition to this, you need to update the `redirect_uri` value. 57 | 58 | ```yaml 59 | clusters: 60 | - name: example-cluster 61 | short_description: "Example Cluster" 62 | description: "Example Cluster Long Description..." 63 | redirect_uri: http://127.0.0.1:5555/dex-auth/callback/example-cluster 64 | client_secret: ... 65 | client_id: example-cluster-client-id 66 | issuer: http://127.0.0.1:5556 67 | k8s_master_uri: https://your-k8s-master.cluster 68 | scopes: 69 | - email 70 | - profile 71 | - openid 72 | 73 | # A path-prefix from which to serve requests and assets 74 | web_path_prefix: /dex-auth 75 | ``` 76 | 77 | Don't forget to update the Dex `staticClients.redirectURIs` value to include the prefix as well. 78 | 79 | ### Helm 80 | 81 | The `dex-k8s-authenticator` helm charts support this via the `dexK8sAuthenticator.web_path_prefix` and `ingress.path` options. You typically set these to the same value. 82 | 83 | Note that the health-checks are configured automatically. 84 | -------------------------------------------------------------------------------- /docs/develop.md: -------------------------------------------------------------------------------- 1 | # Developing and Running 2 | 3 | ## Building a binary 4 | 5 | make 6 | 7 | Creates ./bin/dex-k8s-authenticator 8 | 9 | ## Building a container 10 | 11 | make container 12 | 13 | ## Running it 14 | 15 | ### Start a Dex Server instance 16 | 17 | You must have a `dex` instance running before starting `dex-k8s-authenticator`. 18 | 19 | Follow the example here: 20 | https://github.com/coreos/dex/blob/master/Documentation/getting-started.md 21 | 22 | Start it with using the provided `./examples/dex-server-config-dev.yaml` 23 | 24 | ### Start Dex K8s Authenticator 25 | 26 | ./bin/dex-k8s-authenticator --config ./examples/config.yaml 27 | 28 | * Browse to http://localhost:5555 29 | * Click 'Example Cluster' 30 | * Click 'Log in with Email' 31 | * Login with `admin@example.com` followed by the password `password` 32 | * You should be redirected back to the dex-k8s-authenticator 33 | 34 | ### Configuration Options 35 | 36 | Additional configuration options are explained [here](config.md) -------------------------------------------------------------------------------- /docs/eks.md: -------------------------------------------------------------------------------- 1 | # Running on AWS EKS 2 | 3 | Running `dex-k8s-authenticator` on AWS EKS is a bit tricky. The reason is, AWS EKS does not allow you to set custom API server flags. [1] This is required to set DEX as an endpoint for OIDC provider for kubernetes API server. 4 | 5 | The good news is, you can still get DEX working properly with EKS, however, this will make your auth-flow a bit more complicated than the one on not-managed regular kubernetes setups. This will also introduce an additional service to your auth-flow. kube-oidc-proxy. [2] 6 | 7 | Here, I'll provide 2 diagrams to show how end-to-end auth works with `dex-k8s-authenticator` on both regular kubernetes clusters and on managed kubernetes clusters. 8 | 9 | **Auth flow on self-setup kubernetes cluster** 10 | ![Authentication on a self-setup cluster](/docs/images/auth-regular.jpg) 11 | 12 | **Auth flow on managed kubernetes cluster** 13 | ![Authentication on a managed cluster](/docs/images/auth-managed.jpg) 14 | 15 | 16 | ### How to Setup 17 | 18 | Thankfully, setting all these up is not as difficult as it seems. Mostly because, all the required services have a proper helm-chart. You just need to pass correct values to these charts, depending on your environment. 19 | 20 | Required Charts 21 | - nginx-ingress-controller - https://github.com/helm/charts/tree/master/stable/nginx-ingress 22 | - dex - https://github.com/helm/charts/tree/master/stable/dex 23 | - kube-oidc-proxy - https://github.com/jetstack/kube-oidc-proxy/tree/master/deploy/charts/kube-oidc-proxy 24 | - dex-k8s-authenticator - https://github.com/mintel/dex-k8s-authenticator/tree/master/charts 25 | 26 | You should also setup a DNS record that points to your nginx controller (load-balancer), and setup an AWS ACM certificate. 27 | 28 | **Dex Setup** 29 | 30 | - Enable ingress on the chart values. Set your ingress host as "dex.example.com" 31 | - Setup your connectors. (actual identity providers) 32 | - Setup a static client, for redirect urls, provide "login.example.com" 33 | 34 | **kube-oidc-proxy Setup** 35 | 36 | - Enable ingress on the chart values. Set your ingress host as "oidc-proxy.example.com" 37 | - Setup OIDC configuration. For issuer URL, provide ""dex.example.com"" 38 | 39 | **dex-k8s-authenticator Setup** 40 | 41 | - Enable ingress on the chart values. Set your ingress host as "login.example.com" 42 | - Setup your clusters. 43 | - for issuer; provide ""dex.example.com"" 44 | - for k8s-master-uri; provide "oidc-proxy.example.com", instead of actual k8s-master-uri. 45 | - provide your client_secret as you set it up on your static_client during Dex setup. 46 | - for k8s-ca-pem; you need to provide the certificate for the ELB that serves "oidc-proxy.example.com", in PEM format. 47 | 48 | ### References 49 | 50 | - [1] https://github.com/aws/containers-roadmap/issues/166 51 | - [2] https://github.com/jetstack/kube-oidc-proxy 52 | -------------------------------------------------------------------------------- /docs/helm.md: -------------------------------------------------------------------------------- 1 | # Helm Charts 2 | 3 | This project provides Helm Charts to install `dex` and `dex-k8s-authenticator` into a Kubernetes cluster. 4 | 5 | Read the overview to find out how to install these charts: 6 | 7 | - [Overview](../charts) 8 | 9 | Follow the links for specific documentation on each chart: 10 | - [dex-k8s-authenticator](../charts/dex-k8s-authenticator) 11 | - [dex](../charts/dex) 12 | 13 | 14 | Please note, whilst we maintain our own `dex` chart, there is now an official version available in the main helm charts repo: 15 | 16 | - https://github.com/helm/charts/tree/master/stable/dex 17 | 18 | -------------------------------------------------------------------------------- /docs/images/auth-managed.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mintel/dex-k8s-authenticator/2cbcf28dc25eb75f6227b1658bfce71847612b60/docs/images/auth-managed.jpg -------------------------------------------------------------------------------- /docs/images/auth-regular.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mintel/dex-k8s-authenticator/2cbcf28dc25eb75f6227b1658bfce71847612b60/docs/images/auth-regular.jpg -------------------------------------------------------------------------------- /docs/ssl.md: -------------------------------------------------------------------------------- 1 | # SSL 2 | 3 | ## Adding Trusted Certificates 4 | 5 | There are a couple of ways to have `dex-k8s-authenticator` use trusted certificates. 6 | 7 | ### 1. Mounting `/certs/` volume 8 | 9 | `entrypoint.sh` runs `update-ca-certificates` against certificates found in `/certs/` 10 | 11 | They must end in the extension `.crt` 12 | 13 | If using docker, you can mount a volume like so: 14 | 15 | ``` 16 | docker run --rm -t -i \ 17 | -v /tmp/certs:/certs:ro \ 18 | -v /tmp/config.yml:/tmp/config.yml:ro \ 19 | mintel/dex-k8s-authenticator:latest --config /tmp/config.yml 20 | ``` 21 | 22 | ### 2. `trusted_root_ca` config option 23 | 24 | You can define multiple certificates via the configuration file: 25 | 26 | ```yaml 27 | trusted_root_ca: 28 | - | 29 | -----BEGIN CERTIFICATE----- 30 | MIIGJDCCBAygAwI... 31 | -----END CERTIFICATE----- 32 | ``` 33 | 34 | ## Serving requests on SSL 35 | 36 | The configuration file requires the following: 37 | 38 | 39 | ```yaml 40 | listen: https://127.0.0.1:5555 41 | tls_cert: /path/to/dex-client.crt 42 | tls_key: /path/to/dex-client.key 43 | ``` 44 | 45 | - Note, the `listen` option is using `https` not `http` 46 | - You need to supply both `.crt` and the `.key` files 47 | 48 | The `.crt` and `.key` file can be mounted as files in a volume. 49 | 50 | ## Helm Charts 51 | 52 | ### dex-k8a-authenticator 53 | 54 | Our Helm chart provides options for both using trusted root certs, and serving requests on SSL. 55 | 56 | For more information on SSL support, please read [here](../charts/dex-k8s-authenticator) 57 | -------------------------------------------------------------------------------- /entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ ! -z "$(ls -A /certs)" ]; then 4 | cp -L /certs/*.crt /usr/local/share/ca-certificates/ 2>/dev/null 5 | update-ca-certificates 6 | fi 7 | 8 | # Execute dex-k8s-authenticator with any argument passed to docker run 9 | /app/bin/dex-k8s-authenticator $@ 10 | -------------------------------------------------------------------------------- /examples/config.yaml: -------------------------------------------------------------------------------- 1 | clusters: 2 | # Specify 1 or more clusters 3 | - name: example-cluster 4 | 5 | # Default namespace (optional) 6 | namespace: my-namespace 7 | 8 | # Descriptions used in the WebUI 9 | short_description: "Example Cluster" 10 | description: "Example Cluster Long Description..." 11 | 12 | # Redirect Url pointing to dex-k8s-authenticator callback for this cluster 13 | # This should be configured in Dex as part of the staticClients 14 | # redirectURIs option 15 | redirect_uri: http://127.0.0.1:5555/callback/example-cluster 16 | 17 | # Client Secret - should match value in Dex 18 | client_secret: ZXhhbXBsZS1hcHAtc2VjcmV0 19 | 20 | # Client ID - should match value in Dex 21 | client_id: example-cluster-client-id 22 | 23 | # Dex Issuer - Must be resolvable 24 | issuer: http://127.0.0.1:5556 25 | 26 | # Url to k8s API endpoint - used in WebUI instructions for generating 27 | # kubeconfig 28 | k8s_master_uri: https://your-k8s-master.cluster 29 | 30 | # don't use username for context 31 | static_context_name: false 32 | 33 | # CA for your k8s cluster - used in WebUI instructions for generating 34 | # kubeconfig 35 | # Both k8s_ca_uri and k8s_ca_pem are optional - you typically specifiy 36 | # one or the other if required 37 | # 38 | # Provides a link to the CA from a hosted site 39 | # k8s_ca_uri: http://url-to-your-ca.crt 40 | # 41 | # Provides abililty to specify CA inline 42 | # k8s_ca_pem: | 43 | # -----BEGIN CERTIFICATE----- 44 | # ... 45 | # -----END CERTIFICATE----- 46 | 47 | # Specify multiple extra root CA files to be loaded 48 | # trusted_root_ca: 49 | # -| 50 | # -----BEGIN CERTIFICATE----- 51 | # ... 52 | # -----END CERTIFICATE----- 53 | 54 | # Specify path to tls_cert and tls_key - if enabled, set liten to use https 55 | # tls_cert: /path/to/dex-client.crt 56 | # tls_key: /path/to/dex-client.key 57 | 58 | # CA for your IDP - used in WebUI instructions for generating 59 | # kubeconfig 60 | # Both idp_ca_uri and idp_ca_pem are optional - you typically specifiy 61 | # one or the other if required 62 | # 63 | # Provides a link to the CA from a hosted site 64 | # idp_ca_uri: http://url-to-your-ca.crt 65 | # 66 | # Provides abililty to specify CA inline 67 | # idp_ca_pem: | 68 | # -----BEGIN CERTIFICATE----- 69 | # ... 70 | # -----END CERTIFICATE----- 71 | 72 | 73 | # Which address to listen on (set to https if tls configured) 74 | listen: http://127.0.0.1:5555 75 | 76 | # A path-prefix from which to serve requests and assets 77 | web_path_prefix: / 78 | 79 | # Optional kubectl version which provides a download link to the the binary 80 | kubectl_version: v1.16.2 81 | 82 | # Optional Url to display a logo image 83 | # logo_uri: http:// 84 | 85 | # Enable more debug 86 | debug: false 87 | -------------------------------------------------------------------------------- /examples/dex-server-config-dev.yaml: -------------------------------------------------------------------------------- 1 | issuer: http://127.0.0.1:5556 2 | storage: 3 | type: sqlite3 4 | config: 5 | file: examples/dex.db 6 | web: 7 | http: 0.0.0.0:5556 8 | 9 | expiry: 10 | signingKeys: "10m" 11 | idTokens: "30m" 12 | 13 | logger: 14 | level: "debug" 15 | format: "json" 16 | 17 | oauth2: 18 | responseTypes: ["code", "token", "id_token"] 19 | skipApprovalScreen: true 20 | 21 | connectors: 22 | - type: mockCallback 23 | id: mock 24 | name: Example 25 | 26 | staticClients: 27 | - id: example-cluster-client-id 28 | name: Kubernetes Dev Cluster 29 | secret: ZXhhbXBsZS1hcHAtc2VjcmV0 30 | redirectURIs: 31 | - http://127.0.0.1:5555/callback/example-cluster 32 | 33 | enablePasswordDB: True 34 | 35 | staticPasswords: 36 | - email: "admin@example.com" 37 | # bcrypt hash of the string "password" 38 | hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" 39 | username: "admin" 40 | userID: "08a8684b-db88-4b73-90a9-3cd1661f5466" -------------------------------------------------------------------------------- /examples/index-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mintel/dex-k8s-authenticator/2cbcf28dc25eb75f6227b1658bfce71847612b60/examples/index-page.png -------------------------------------------------------------------------------- /examples/kubeconfig-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mintel/dex-k8s-authenticator/2cbcf28dc25eb75f6227b1658bfce71847612b60/examples/kubeconfig-page.png -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mintel/dex-k8s-authenticator 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/coreos/go-oidc v2.2.1+incompatible 7 | github.com/pquerna/cachecontrol v0.1.0 // indirect 8 | github.com/spf13/cast v1.3.1 9 | github.com/spf13/cobra v1.1.3 10 | github.com/spf13/viper v1.7.1 11 | golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect 12 | golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c 13 | gopkg.in/square/go-jose.v2 v2.5.1 // indirect 14 | ) 15 | -------------------------------------------------------------------------------- /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 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 8 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 9 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 10 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 11 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 12 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 13 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 14 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 15 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 16 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 17 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 18 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 19 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 20 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 21 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 22 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 23 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 24 | cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= 25 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 26 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 27 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 28 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 29 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 30 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 31 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 32 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 33 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 34 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 35 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 36 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 37 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 38 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 39 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 40 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 41 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 42 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 43 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 44 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 45 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 46 | github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= 47 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 48 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 49 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 50 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 51 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 52 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 53 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 54 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 55 | github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 56 | github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk= 57 | github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= 58 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 59 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 60 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 61 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 62 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 63 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 64 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 65 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 66 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 67 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 68 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 69 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 70 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 71 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 72 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 73 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 74 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 75 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 76 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 77 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 78 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 79 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 80 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 81 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 82 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 83 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 84 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 85 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 86 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 87 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 88 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 89 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 90 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 91 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 92 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 93 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 94 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 95 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 96 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 97 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 98 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 99 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 100 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 101 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 102 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 103 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 104 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 105 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 106 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 107 | github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= 108 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 109 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 110 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 111 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 112 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 113 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 114 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 115 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 116 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 117 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 118 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 119 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 120 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 121 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 122 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 123 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 124 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 125 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 126 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 127 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 128 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 129 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 130 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 131 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 132 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 133 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 134 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 135 | github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= 136 | github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 137 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 138 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 139 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 140 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 141 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 142 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 143 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 144 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 145 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 146 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 147 | github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 148 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 149 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 150 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 151 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 152 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 153 | github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 154 | github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 155 | github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 156 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 157 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 158 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 159 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 160 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 161 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 162 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 163 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 164 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 165 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 166 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 167 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 168 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 169 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 170 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 171 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 172 | github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= 173 | github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 174 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 175 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 176 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 177 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 178 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 179 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 180 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 181 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 182 | github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 183 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 184 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 185 | github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= 186 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 187 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 188 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 189 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 190 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 191 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 192 | github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= 193 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 194 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 195 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 196 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 197 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 198 | github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc= 199 | github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= 200 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 201 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 202 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 203 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 204 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 205 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 206 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 207 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 208 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 209 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 210 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 211 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 212 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 213 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 214 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 215 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 216 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 217 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 218 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 219 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 220 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 221 | github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= 222 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 223 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 224 | github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= 225 | github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 226 | github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M= 227 | github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= 228 | github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= 229 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 230 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 231 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 232 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 233 | github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= 234 | github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= 235 | github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= 236 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 237 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 238 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 239 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 240 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 241 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 242 | github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= 243 | github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 244 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 245 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 246 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 247 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 248 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 249 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 250 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 251 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 252 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 253 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 254 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 255 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 256 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 257 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 258 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 259 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 260 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 261 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 262 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 263 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 264 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 265 | golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= 266 | golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= 267 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 268 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 269 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 270 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 271 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 272 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 273 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 274 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 275 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 276 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 277 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 278 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 279 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 280 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 281 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 282 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 283 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 284 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 285 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 286 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 287 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 288 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 289 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 290 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 291 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 292 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 293 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 294 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 295 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 296 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 297 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 298 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 299 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 300 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 301 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 302 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 303 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 304 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 305 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 306 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 307 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 308 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 309 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 310 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 311 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 312 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 313 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 314 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 315 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 316 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 317 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 318 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 319 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 320 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 321 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 322 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 323 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 324 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 325 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 326 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 327 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= 328 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 329 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 330 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 331 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 332 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 333 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 334 | golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c h1:pkQiBZBvdos9qq4wBAHqlzuZHEXo07pqV06ef90u1WI= 335 | golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 336 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 337 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 338 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 339 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 340 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 341 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 342 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 343 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 344 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 345 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 346 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 347 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 348 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 349 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 350 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 351 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 352 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 353 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 354 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 355 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 356 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 357 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 358 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 359 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 360 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 361 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 362 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 363 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 364 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 365 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 366 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 367 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 368 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 369 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 370 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 371 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 372 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 373 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 374 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= 375 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 376 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 377 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 378 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 379 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 380 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 381 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= 382 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 383 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 384 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 385 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 386 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 387 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 388 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 389 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 390 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 391 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 392 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 393 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 394 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 395 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 396 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 397 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 398 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 399 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 400 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 401 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 402 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 403 | golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 404 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 405 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 406 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 407 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 408 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 409 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 410 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 411 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 412 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 413 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 414 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 415 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 416 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 417 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 418 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 419 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 420 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 421 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 422 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 423 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 424 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 425 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 426 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 427 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 428 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 429 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 430 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 431 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 432 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 433 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 434 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 435 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 436 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 437 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 438 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 439 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 440 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 441 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 442 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 443 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 444 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 445 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 446 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 447 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 448 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 449 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 450 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 451 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 452 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 453 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 454 | google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= 455 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 456 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 457 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 458 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 459 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 460 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 461 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 462 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 463 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 464 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 465 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 466 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 467 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 468 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 469 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 470 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 471 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 472 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 473 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 474 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 475 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 476 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 477 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 478 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 479 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 480 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 481 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 482 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 483 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 484 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 485 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 486 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 487 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 488 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 489 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 490 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 491 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 492 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 493 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 494 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 495 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 496 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 497 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 498 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 499 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 500 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 501 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 502 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 503 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 504 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 505 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 506 | google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= 507 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 508 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 509 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 510 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 511 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 512 | gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= 513 | gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 514 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 515 | gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= 516 | gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= 517 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 518 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 519 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 520 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 521 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 522 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 523 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 524 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 525 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 526 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 527 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 528 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 529 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 530 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 531 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 532 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 533 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 534 | -------------------------------------------------------------------------------- /html/static/button.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Combined-Shape 5 | Created with Sketch. 6 | 7 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /html/static/clipboard.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * clipboard.js v2.0.0 3 | * https://zenorocha.github.io/clipboard.js 4 | * 5 | * Licensed MIT © Zeno Rocha 6 | */ 7 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return function(t){function e(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,e),r.l=!0,r.exports}var n={};return e.m=t,e.c=n,e.i=function(t){return t},e.d=function(t,n,o){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:o})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=3)}([function(t,e,n){var o,r,i;!function(a,c){r=[t,n(7)],o=c,void 0!==(i="function"==typeof o?o.apply(e,r):o)&&(t.exports=i)}(0,function(t,e){"use strict";function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}var o=function(t){return t&&t.__esModule?t:{default:t}}(e),r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i=function(){function t(t,e){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};this.action=t.action,this.container=t.container,this.emitter=t.emitter,this.target=t.target,this.text=t.text,this.trigger=t.trigger,this.selectedText=""}},{key:"initSelection",value:function(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function(){var t=this,e="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return t.removeFake()},this.fakeHandler=this.container.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[e?"right":"left"]="-9999px";var n=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.style.top=n+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,this.container.appendChild(this.fakeElem),this.selectedText=(0,o.default)(this.fakeElem),this.copyText()}},{key:"removeFake",value:function(){this.fakeHandler&&(this.container.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(this.container.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function(){this.selectedText=(0,o.default)(this.target),this.copyText()}},{key:"copyText",value:function(){var t=void 0;try{t=document.execCommand(this.action)}catch(e){t=!1}this.handleResult(t)}},{key:"handleResult",value:function(t){this.emitter.emit(t?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function(){this.trigger&&this.trigger.focus(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function(){this.removeFake()}},{key:"action",set:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"copy";if(this._action=t,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function(){return this._action}},{key:"target",set:function(t){if(void 0!==t){if(!t||"object"!==(void 0===t?"undefined":r(t))||1!==t.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&t.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(t.hasAttribute("readonly")||t.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=t}},get:function(){return this._target}}]),t}();t.exports=a})},function(t,e,n){function o(t,e,n){if(!t&&!e&&!n)throw new Error("Missing required arguments");if(!c.string(e))throw new TypeError("Second argument must be a String");if(!c.fn(n))throw new TypeError("Third argument must be a Function");if(c.node(t))return r(t,e,n);if(c.nodeList(t))return i(t,e,n);if(c.string(t))return a(t,e,n);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function r(t,e,n){return t.addEventListener(e,n),{destroy:function(){t.removeEventListener(e,n)}}}function i(t,e,n){return Array.prototype.forEach.call(t,function(t){t.addEventListener(e,n)}),{destroy:function(){Array.prototype.forEach.call(t,function(t){t.removeEventListener(e,n)})}}}function a(t,e,n){return u(document.body,t,e,n)}var c=n(6),u=n(5);t.exports=o},function(t,e){function n(){}n.prototype={on:function(t,e,n){var o=this.e||(this.e={});return(o[t]||(o[t]=[])).push({fn:e,ctx:n}),this},once:function(t,e,n){function o(){r.off(t,o),e.apply(n,arguments)}var r=this;return o._=e,this.on(t,o,n)},emit:function(t){var e=[].slice.call(arguments,1),n=((this.e||(this.e={}))[t]||[]).slice(),o=0,r=n.length;for(o;o0&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof t.action?t.action:this.defaultAction,this.target="function"==typeof t.target?t.target:this.defaultTarget,this.text="function"==typeof t.text?t.text:this.defaultText,this.container="object"===d(t.container)?t.container:document.body}},{key:"listenClick",value:function(t){var e=this;this.listener=(0,f.default)(t,"click",function(t){return e.onClick(t)})}},{key:"onClick",value:function(t){var e=t.delegateTarget||t.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new l.default({action:this.action(e),target:this.target(e),text:this.text(e),container:this.container,trigger:e,emitter:this})}},{key:"defaultAction",value:function(t){return u("action",t)}},{key:"defaultTarget",value:function(t){var e=u("target",t);if(e)return document.querySelector(e)}},{key:"defaultText",value:function(t){return u("text",t)}},{key:"destroy",value:function(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["copy","cut"],e="string"==typeof t?[t]:t,n=!!document.queryCommandSupported;return e.forEach(function(t){n=n&&!!document.queryCommandSupported(t)}),n}}]),e}(s.default);t.exports=p})},function(t,e){function n(t,e){for(;t&&t.nodeType!==o;){if("function"==typeof t.matches&&t.matches(e))return t;t=t.parentNode}}var o=9;if("undefined"!=typeof Element&&!Element.prototype.matches){var r=Element.prototype;r.matches=r.matchesSelector||r.mozMatchesSelector||r.msMatchesSelector||r.oMatchesSelector||r.webkitMatchesSelector}t.exports=n},function(t,e,n){function o(t,e,n,o,r){var a=i.apply(this,arguments);return t.addEventListener(n,a,r),{destroy:function(){t.removeEventListener(n,a,r)}}}function r(t,e,n,r,i){return"function"==typeof t.addEventListener?o.apply(null,arguments):"function"==typeof n?o.bind(null,document).apply(null,arguments):("string"==typeof t&&(t=document.querySelectorAll(t)),Array.prototype.map.call(t,function(t){return o(t,e,n,r,i)}))}function i(t,e,n,o){return function(n){n.delegateTarget=a(n.target,e),n.delegateTarget&&o.call(t,n)}}var a=n(4);t.exports=r},function(t,e){e.node=function(t){return void 0!==t&&t instanceof HTMLElement&&1===t.nodeType},e.nodeList=function(t){var n=Object.prototype.toString.call(t);return void 0!==t&&("[object NodeList]"===n||"[object HTMLCollection]"===n)&&"length"in t&&(0===t.length||e.node(t[0]))},e.string=function(t){return"string"==typeof t||t instanceof String},e.fn=function(t){return"[object Function]"===Object.prototype.toString.call(t)}},function(t,e){function n(t){var e;if("SELECT"===t.nodeName)t.focus(),e=t.value;else if("INPUT"===t.nodeName||"TEXTAREA"===t.nodeName){var n=t.hasAttribute("readonly");n||t.setAttribute("readonly",""),t.select(),t.setSelectionRange(0,t.value.length),n||t.removeAttribute("readonly"),e=t.value}else{t.hasAttribute("contenteditable")&&t.focus();var o=window.getSelection(),r=document.createRange();r.selectNodeContents(t),o.removeAllRanges(),o.addRange(r),e=o.toString()}return e}t.exports=n}])}); -------------------------------------------------------------------------------- /html/static/clippy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /html/static/main.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | margin: 0; 7 | font-family: Arial; 8 | } 9 | 10 | h3 { font-size: 16px; } 11 | 12 | pre { 13 | background-color: #eff0f1; 14 | padding: 5px; 15 | white-space: pre-wrap; 16 | overflow-wrap: break-word; 17 | } 18 | 19 | .dex-container { 20 | color: #333; 21 | margin: 45px auto; 22 | max-width: 500px; 23 | min-width: 320px; 24 | text-align: center; 25 | } 26 | 27 | .dex-kubeconfig-container { 28 | color: #333; 29 | margin: 45px auto; 30 | max-width: 90%; 31 | min-width: 320px; 32 | text-align: left; 33 | } 34 | 35 | .dex-btn { 36 | border-radius: 4px; 37 | border: 0; 38 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.25), 0 0 1px rgba(0, 0, 0, 0.25); 39 | cursor: pointer; 40 | font-size: 16px; 41 | padding: 0; 42 | } 43 | 44 | .dex-btn:focus { 45 | outline: none; 46 | } 47 | 48 | .dex-btn:active { 49 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); 50 | outline: none; 51 | } 52 | 53 | .dex-btn-icon { 54 | background-position: center; 55 | background-repeat: no-repeat; 56 | background-size: 24px; 57 | border-radius: 4px 0 0 4px; 58 | float: left; 59 | height: 36px; 60 | margin-right: 5px; 61 | width: 36px; 62 | } 63 | 64 | .dex-btn-icon--local { 65 | background-color: #84B6EF; 66 | background-image: url(./button.svg); 67 | } 68 | 69 | .dex-btn-text { 70 | font-weight: 600; 71 | line-height: 36px; 72 | padding: 6px 12px; 73 | text-align: center; 74 | } 75 | 76 | .dex-separator { 77 | color: #999; 78 | } 79 | 80 | .dex-error-box { 81 | background-color: #DD1327; 82 | color: #fff; 83 | font-size: 14px; 84 | font-weight: normal; 85 | max-width: 320px; 86 | padding: 4px 0; 87 | } 88 | 89 | .dex-error-box { 90 | margin: 20px auto; 91 | } 92 | 93 | div.groups { 94 | border: 1px solid #ccc; 95 | background-color: #f1f1f1; 96 | margin-bottom: 15px; 97 | } 98 | 99 | div.command { 100 | margin-top: 15px; 101 | } 102 | 103 | -------------------------------------------------------------------------------- /html/static/snippets.js: -------------------------------------------------------------------------------- 1 | var snippets=document.querySelectorAll('.snippet');[].forEach.call(snippets,function(snippet){snippet.firstChild.insertAdjacentHTML('beforebegin','');});var clipboardSnippets=new ClipboardJS('[data-clipboard-snippet]',{target:function(trigger){return trigger.nextElementSibling;}});clipboardSnippets.on('success',function(e){e.clearSelection();showTooltip(e.trigger,'Copied!');});clipboardSnippets.on('error',function(e){showTooltip(e.trigger,fallbackMessage(e.action));}); -------------------------------------------------------------------------------- /html/static/styles.css: -------------------------------------------------------------------------------- 1 | .theme-body { 2 | background-color: #efefef; 3 | color: #333; 4 | font-family: 'Source Sans Pro', Helvetica, sans-serif; 5 | } 6 | 7 | .theme-navbar { 8 | background-color: #fff; 9 | box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2); 10 | color: #333; 11 | font-size: 13px; 12 | font-weight: 100; 13 | height: 46px; 14 | overflow: hidden; 15 | padding: 0 10px; 16 | } 17 | 18 | .theme-navbar__logo-wrap { 19 | display: inline-block; 20 | height: 100%; 21 | overflow: hidden; 22 | padding: 10px 15px; 23 | width: 300px; 24 | } 25 | 26 | .theme-navbar__logo { 27 | height: 100%; 28 | max-height: 25px; 29 | } 30 | 31 | .theme-heading { 32 | font-size: 20px; 33 | font-weight: 500; 34 | margin-bottom: 10px; 35 | margin-top: 0; 36 | } 37 | 38 | .theme-panel { 39 | background-color: #fff; 40 | box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); 41 | padding: 30px; 42 | } 43 | 44 | .theme-btn-provider { 45 | background-color: #fff; 46 | color: #333; 47 | min-width: 350px; 48 | } 49 | 50 | .theme-btn-provider:hover { 51 | color: #999; 52 | } 53 | 54 | .theme-btn--primary { 55 | background-color: #333; 56 | border: none; 57 | color: #fff; 58 | min-width: 200px; 59 | padding: 6px 12px; 60 | } 61 | 62 | .theme-btn--primary:hover { 63 | background-color: #666; 64 | color: #fff; 65 | } 66 | 67 | .theme-btn--success { 68 | background-color: #2FC98E; 69 | color: #fff; 70 | width: 350px; 71 | } 72 | 73 | .theme-btn--success:hover { 74 | background-color: #49E3A8; 75 | } 76 | 77 | .theme-form-row { 78 | display: block; 79 | margin: 20px auto; 80 | } 81 | 82 | .theme-form-input:focus, 83 | .theme-form-input:active { 84 | border-color: #66AFE9; 85 | outline: none; 86 | } 87 | 88 | .theme-form-label { 89 | font-size: 13px; 90 | font-weight: 600; 91 | margin: 4px auto; 92 | position: relative; 93 | text-align: left; 94 | width: 350px; 95 | } 96 | 97 | .theme-form-description { 98 | font-size: 13px; 99 | font-weight: 300; 100 | margin: 4px auto; 101 | position: relative; 102 | text-align: left; 103 | width: 350px; 104 | } 105 | -------------------------------------------------------------------------------- /html/static/tabs.css: -------------------------------------------------------------------------------- 1 | /* Style the tab */ 2 | .tab { 3 | overflow: hidden; 4 | border: 1px solid #ccc; 5 | background-color: #f1f1f1; 6 | } 7 | 8 | /* Style the buttons inside the tab */ 9 | .tab button { 10 | background-color: inherit; 11 | float: left; 12 | border: none; 13 | outline: none; 14 | cursor: pointer; 15 | padding: 14px 16px; 16 | transition: 0.3s; 17 | font-size: 17px; 18 | } 19 | 20 | /* Change background color of buttons on hover */ 21 | .tab button:hover { 22 | background-color: #ddd; 23 | } 24 | 25 | /* Create an active/current tablink class */ 26 | .tab button.active { 27 | background-color: #ccc; 28 | } 29 | 30 | /* Style the tab content */ 31 | .tabcontent { 32 | display: none; 33 | padding: 6px 12px; 34 | border: 1px solid #ccc; 35 | border-top: none; 36 | } 37 | -------------------------------------------------------------------------------- /html/static/tooltips.js: -------------------------------------------------------------------------------- 1 | var btns=document.querySelectorAll('.btn');for(var i=0;i 0 { 133 | for _, cert := range config.Trusted_Root_Ca { 134 | ok := certp.AppendCertsFromPEM([]byte(cert)) 135 | if !ok { 136 | log.Fatalf("Failed to parse a trusted cert, pem format expected") 137 | } 138 | } 139 | } 140 | // Load CA certs from file 141 | if config.Trusted_Root_Ca_File != "" { 142 | content, err := ioutil.ReadFile(config.Trusted_Root_Ca_File) 143 | if err != nil { 144 | log.Fatalf("Failed to read file Trusted Root CA %s, %v", config.Trusted_Root_Ca_File, err) 145 | } 146 | ok := certp.AppendCertsFromPEM([]byte(content)) 147 | if !ok { 148 | log.Fatalf("Failed to parse a trusted cert from file %s, pem format expected", config.Trusted_Root_Ca_File) 149 | } 150 | } 151 | 152 | mTlsConfig := &tls.Config{ 153 | MinVersion: tls.VersionTLS12, // minimum TLS 1.2 154 | // P curve order does not matter, as breaking one means all others can be brute-forced as well: 155 | // Golang developers prefer: 156 | CurvePreferences: []tls.CurveID{tls.X25519, tls.CurveP256, tls.CurveP384, tls.CurveP521}, 157 | PreferServerCipherSuites: true, // Server chooses ciphersuite, order matters below: 158 | CipherSuites: []uint16{ 159 | tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 160 | tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 161 | tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, 162 | tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 163 | tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 164 | tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, 165 | tls.TLS_CHACHA20_POLY1305_SHA256, // TLS 1.3 166 | tls.TLS_AES_256_GCM_SHA384, // TLS 1.3 167 | tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 168 | tls.TLS_AES_128_GCM_SHA256, // TLS 1.3 169 | }, 170 | RootCAs: certp, 171 | } 172 | 173 | tr := &http.Transport{ 174 | TLSClientConfig: mTlsConfig, 175 | 176 | // Set proxy callback for proxy support in this transport 177 | Proxy: http.ProxyFromEnvironment, 178 | } 179 | 180 | // Ensure trailing slash on web-path-prefix 181 | web_path_prefix := config.Web_Path_Prefix 182 | if web_path_prefix != "/" { 183 | web_path_prefix = fmt.Sprintf("%s/", path.Clean(web_path_prefix)) 184 | config.Web_Path_Prefix = web_path_prefix 185 | } 186 | 187 | // Generate handlers for each cluster 188 | for i := range config.Clusters { 189 | cluster := config.Clusters[i] 190 | if debug { 191 | if cluster.Client == nil { 192 | cluster.Client = &http.Client{ 193 | Transport: debugTransport{tr}, 194 | } 195 | } else { 196 | cluster.Client.Transport = debugTransport{tr} 197 | } 198 | } else { 199 | cluster.Client = &http.Client{Transport: tr} 200 | } 201 | 202 | ctx := oidc.ClientContext(context.Background(), cluster.Client) 203 | log.Printf("Creating new provider %s", cluster.Issuer) 204 | provider, err := oidc.NewProvider(ctx, cluster.Issuer) 205 | 206 | if err != nil { 207 | log.Fatalf("Failed to query provider %q: %v\n", cluster.Issuer, err) 208 | } 209 | 210 | cluster.Provider = provider 211 | 212 | log.Printf("Verifying client %s", cluster.Client_ID) 213 | 214 | verifier := provider.Verifier(&oidc.Config{ClientID: cluster.Client_ID}) 215 | 216 | cluster.Verifier = verifier 217 | 218 | if err := provider.Claims(&s); err != nil { 219 | log.Fatalf("Failed to parse provider scopes_supported: %v", err) 220 | } 221 | 222 | if len(s.ScopesSupported) == 0 { 223 | // scopes_supported is a "RECOMMENDED" discovery claim, not a required 224 | // one. If missing, assume that the provider follows the spec and has 225 | // an "offline_access" scope. 226 | cluster.OfflineAsScope = true 227 | } else { 228 | // See if scopes_supported has the "offline_access" scope. 229 | cluster.OfflineAsScope = func() bool { 230 | for _, scope := range s.ScopesSupported { 231 | if scope == oidc.ScopeOfflineAccess { 232 | return true 233 | } 234 | } 235 | return false 236 | }() 237 | } 238 | 239 | if len(cluster.Scopes) == 0 { 240 | cluster.Scopes = []string{"openid", "profile", "email", "offline_access", "groups"} 241 | } 242 | 243 | if cluster.K8s_Ca_Pem_File != "" { 244 | content, err := ioutil.ReadFile(cluster.K8s_Ca_Pem_File) 245 | if err != nil { 246 | log.Fatalf("Failed to load CA from file %s, %s", cluster.K8s_Ca_Pem_File, err) 247 | } 248 | cluster.K8s_Ca_Pem = cast.ToString(content) 249 | } 250 | 251 | if cluster.K8s_Ca_Pem == "" && cluster.K8s_Ca_Pem_Base64_Encoded != "" { 252 | p, err := base64.StdEncoding.DecodeString(cluster.K8s_Ca_Pem_Base64_Encoded) 253 | if err != nil { 254 | log.Fatalf("Failed to base64 decode ca pem: %s", err.Error()) 255 | } 256 | 257 | cluster.K8s_Ca_Pem = string(p) 258 | } 259 | 260 | cluster.Config = config 261 | 262 | base_redirect_uri, err := url.Parse(cluster.Redirect_URI) 263 | 264 | if err != nil { 265 | log.Fatalf("Parsing redirect_uri address: %v", err) 266 | } 267 | 268 | // Each cluster gets a different login and callback URL 269 | http.HandleFunc(base_redirect_uri.Path, cluster.handleCallback) 270 | log.Printf("Registered callback handler at: %s", base_redirect_uri.Path) 271 | 272 | login_uri := path.Join(config.Web_Path_Prefix, "login", cluster.Name) 273 | http.HandleFunc(login_uri, cluster.handleLogin) 274 | log.Printf("Registered login handler at: %s", login_uri) 275 | } 276 | 277 | // Index page 278 | http.HandleFunc(config.Web_Path_Prefix, config.handleIndex) 279 | 280 | // Serve static html assets 281 | fs := http.FileServer(http.Dir("html/static/")) 282 | static_uri := path.Join(config.Web_Path_Prefix, "static") + "/" 283 | log.Printf("Registered static assets handler at: %s", static_uri) 284 | 285 | http.Handle(static_uri, http.StripPrefix(static_uri, fs)) 286 | 287 | // Determine whether to use TLS or not 288 | switch listenURL.Scheme { 289 | case "http": 290 | log.Printf("Listening on %s", config.Listen) 291 | err := http.ListenAndServe(listenURL.Host, nil) 292 | log.Fatal(err) 293 | case "https": 294 | log.Printf("Listening on %s", config.Listen) 295 | err := http.ListenAndServeTLS(listenURL.Host, config.TLS_Cert, config.TLS_Key, nil) 296 | log.Fatal(err) 297 | 298 | default: 299 | log.Fatalf("Listen address %q is not using http or https", config.Listen) 300 | } 301 | } 302 | 303 | func substituteEnvVarsRecursive(copy, original reflect.Value) { 304 | switch original.Kind() { 305 | 306 | case reflect.Ptr: 307 | originalValue := original.Elem() 308 | if !originalValue.IsValid() { 309 | return 310 | } 311 | copy.Set(reflect.New(originalValue.Type())) 312 | substituteEnvVarsRecursive(copy.Elem(), originalValue) 313 | 314 | case reflect.Interface: 315 | originalValue := original.Elem() 316 | copyValue := reflect.New(originalValue.Type()).Elem() 317 | substituteEnvVarsRecursive(copyValue, originalValue) 318 | copy.Set(copyValue) 319 | 320 | case reflect.Struct: 321 | for i := 0; i < original.NumField(); i += 1 { 322 | substituteEnvVarsRecursive(copy.Field(i), original.Field(i)) 323 | } 324 | 325 | case reflect.Slice: 326 | copy.Set(reflect.MakeSlice(original.Type(), original.Len(), original.Cap())) 327 | for i := 0; i < original.Len(); i += 1 { 328 | substituteEnvVarsRecursive(copy.Index(i), original.Index(i)) 329 | } 330 | 331 | case reflect.Map: 332 | copy.Set(reflect.MakeMap(original.Type())) 333 | for _, key := range original.MapKeys() { 334 | originalValue := original.MapIndex(key) 335 | copyValue := reflect.New(originalValue.Type()).Elem() 336 | substituteEnvVarsRecursive(copyValue, originalValue) 337 | copy.SetMapIndex(key, copyValue) 338 | } 339 | 340 | case reflect.String: 341 | replacedString := substituteEnvVars(original.Interface().(string)) 342 | copy.SetString(replacedString) 343 | 344 | default: 345 | copy.Set(original) 346 | } 347 | 348 | } 349 | 350 | var RootCmd = &cobra.Command{ 351 | Use: "dex-k8s-authenticator", 352 | Short: "Dex Kubernetes Authenticator", 353 | Long: `Dex Kubernetes Authenticator provides a web-interface to generate a kubeconfig file based on a selected Kubernetes cluster. One or more clusters can be defined in the configuration file.`, 354 | Run: func(cmd *cobra.Command, args []string) { 355 | 356 | var config Config 357 | err := viper.Unmarshal(&config) 358 | if err != nil { 359 | log.Fatalf("Unable to decode configuration into struct, %v", err) 360 | } 361 | 362 | original := reflect.ValueOf(config) 363 | copy := reflect.New(original.Type()).Elem() 364 | substituteEnvVarsRecursive(copy, original) 365 | 366 | // Start the app 367 | start_app(copy.Interface().(Config)) 368 | 369 | // Fallback if no args specified 370 | cmd.HelpFunc()(cmd, args) 371 | 372 | }, 373 | } 374 | 375 | // Read in config file 376 | func initConfig() { 377 | 378 | if config_file != "" { 379 | //viper.SetConfigFile(config_file) 380 | // get the filepath 381 | abs, err := filepath.Abs(config_file) 382 | if err != nil { 383 | log.Fatalf("Error reading config file, %s", err) 384 | } 385 | 386 | // get the config name 387 | base := filepath.Base(abs) 388 | 389 | // get the path 390 | path := filepath.Dir(abs) 391 | 392 | viper.SetConfigName(strings.Split(base, ".")[0]) 393 | viper.AddConfigPath(path) 394 | viper.SetDefault("web_path_prefix", "/") 395 | 396 | config, err := ioutil.ReadFile(config_file) 397 | if err != nil { 398 | log.Fatalf("Error reading config file, %s", err) 399 | } 400 | 401 | origConfigStr := bytes.NewBuffer(config).String() 402 | 403 | if err := viper.ReadConfig(bytes.NewBufferString(origConfigStr)); err != nil { 404 | log.Fatalf("viper.ReadConfig failed to read config, %s", err.Error()) 405 | } 406 | 407 | log.Printf("Using config file: %s", viper.ConfigFileUsed()) 408 | } 409 | } 410 | 411 | // Initialization 412 | func init() { 413 | cobra.OnInitialize(initConfig) 414 | 415 | if err := viper.BindPFlags(RootCmd.Flags()); err != nil { 416 | log.Fatal(err) 417 | } 418 | 419 | RootCmd.Flags().StringVar(&config_file, "config", "", "./config.yml") 420 | RootCmd.PersistentFlags().BoolVarP(&debug, "debug", "d", false, "Enable debug logging") 421 | } 422 | 423 | // Let's go! 424 | func main() { 425 | if err := RootCmd.Execute(); err != nil { 426 | fmt.Println(err) 427 | os.Exit(-1) 428 | } 429 | } 430 | -------------------------------------------------------------------------------- /templates.go: -------------------------------------------------------------------------------- 1 | // FIXME: Dislike this file a bit - what's the take on referencing 2 | // viper config values (treat it as a global, or pass values around?) 3 | package main 4 | 5 | import ( 6 | "encoding/json" 7 | "fmt" 8 | "html/template" 9 | "log" 10 | "net/http" 11 | "strings" 12 | ) 13 | 14 | // compile all templates and cache them 15 | var templates = template.Must(template.ParseGlob("./templates/*.html")) 16 | 17 | func renderIndex(w http.ResponseWriter, config *Config) { 18 | t, _ := template.ParseFiles("./templates/index.html") 19 | err := t.Execute(w, config) 20 | if err != nil { 21 | log.Println(err) 22 | http.Error(w, http.StatusText(500), 500) 23 | } 24 | } 25 | 26 | type templateData struct { 27 | IDToken string 28 | RefreshToken string 29 | RedirectURL string 30 | Claims string 31 | Username string 32 | Issuer string 33 | ClusterName string 34 | ShortDescription string 35 | ClientSecret string 36 | ClientID string 37 | K8sMasterURI string 38 | K8sCaURI string 39 | K8sCaPem string 40 | IDPCaURI string 41 | IDPCaPem string 42 | LogoURI string 43 | Web_Path_Prefix string 44 | StaticContextName bool 45 | KubectlVersion string 46 | Namespace string 47 | } 48 | 49 | func (cluster *Cluster) renderToken(w http.ResponseWriter, 50 | idToken, 51 | refreshToken string, 52 | idpCaURI string, 53 | idpCaPem string, 54 | logoURI string, 55 | webPathPrefix string, 56 | kubectlVersion string, 57 | claims []byte) { 58 | 59 | var data map[string]interface{} 60 | err := json.Unmarshal(claims, &data) 61 | if err != nil { 62 | panic(err) 63 | } 64 | 65 | unix_username := "user" 66 | if data["email"] != nil { 67 | email := data["email"].(string) 68 | unix_username = strings.Split(email, "@")[0] 69 | } 70 | 71 | token_data := templateData{ 72 | IDToken: idToken, 73 | RefreshToken: refreshToken, 74 | RedirectURL: cluster.Redirect_URI, 75 | Claims: string(claims), 76 | Username: unix_username, 77 | Issuer: data["iss"].(string), 78 | ClusterName: cluster.Name, 79 | ShortDescription: cluster.Short_Description, 80 | ClientSecret: cluster.Client_Secret, 81 | ClientID: cluster.Client_ID, 82 | K8sMasterURI: cluster.K8s_Master_URI, 83 | K8sCaURI: cluster.K8s_Ca_URI, 84 | K8sCaPem: cluster.K8s_Ca_Pem, 85 | IDPCaURI: idpCaURI, 86 | IDPCaPem: idpCaPem, 87 | LogoURI: logoURI, 88 | Web_Path_Prefix: webPathPrefix, 89 | StaticContextName: cluster.Static_Context_Name, 90 | Namespace: cluster.Namespace, 91 | KubectlVersion: kubectlVersion} 92 | 93 | if err := templates.ExecuteTemplate(w, "kubeconfig.html", token_data); err != nil { 94 | log.Println(err) 95 | http.Error(w, http.StatusText(500), 500) 96 | } 97 | } 98 | 99 | // renderHTMLError renders an HTML page that presents an HTTP error. 100 | func (cluster *Cluster) renderHTMLError(w http.ResponseWriter, errorMsg string, code int) { 101 | w.Header().Set("Content-Type", "text/html; charset=utf-8") 102 | w.Header().Set("X-Content-Type-Options", "nosniff") 103 | w.WriteHeader(code) 104 | 105 | if err := templates.ExecuteTemplate(w, "error.html", map[string]string{ 106 | "Logo_Uri": cluster.Config.Logo_Uri, 107 | "Web_Path_Prefix": cluster.Config.Web_Path_Prefix, 108 | "Code": fmt.Sprintf("%d", code), 109 | "Error_Description": errorMsg, 110 | }); err != nil { 111 | log.Println(err) 112 | http.Error(w, http.StatusText(500), 500) 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /templates/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Error - {{ .Code }} 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | {{ if .Logo_Uri }} 19 |
20 | 21 |
22 | {{ end }} 23 |
24 | 25 |
26 | 27 |
28 |

Error - {{ .Code }}

29 | {{ if .Error_Description }} 30 |

An error has ocurred with the following details:

31 |

32 | {{ .Error_Description }} 33 |

34 | {{ end }} 35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /templates/id-token-tab.html: -------------------------------------------------------------------------------- 1 | {{ define "id-token-content" }} 2 | 3 |

The ID Token signed by dex and returned as part of the OAuth2 response - represented as a Json Web Token (JWT).

4 | 5 |
6 | 9 |
{{ .IDToken }}
10 |
11 | {{ end }} 12 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Generate Kubernetes Token 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | {{ if .Logo_Uri }} 19 |
20 | 21 |
22 | {{ end }} 23 |
24 | 25 |
26 | 27 |
28 |

Generate Kubernetes Token

29 |
30 |

31 | Select which cluster you require a token for: 32 |

33 | 34 | {{ range $cluster := .Clusters }} 35 | 36 |
37 |

{{$cluster.Description}}

38 | 39 | 43 | 44 |
45 | {{ end }} 46 |
47 |
48 |
49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /templates/kubeconfig.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Kubernetes Configuration 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | {{ if .LogoURI }} 20 |
21 | 22 |
23 | {{ end }} 24 |
25 | 26 |
27 |
28 |
29 | Login Again 30 |
31 |

Generated Kubernetes Token - {{ .ShortDescription }}

32 | 33 |
34 | 35 |

Please check that you have been assigned to the expected Groups, then follow the instructions 36 | based on your OS. 37 |

38 | 39 |
40 |
{{ .Claims }}
41 |
42 | 43 |
44 | 45 | 46 | 47 | 48 |
49 | 50 |
51 | {{ template "linux-tab-content" . }} 52 |
53 | 54 |
55 | {{ template "mac-tab-content" . }} 56 |
57 | 58 |
59 | {{ template "windows-tab-content" . }} 60 |
61 | 62 |
63 | {{ template "id-token-content" . }} 64 |
65 | 66 |
67 |
68 | 69 | 70 | 71 | 72 | 73 | 82 | 83 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /templates/linux-mac-common.html: -------------------------------------------------------------------------------- 1 | {{ define "linux-mac-common" }} 2 | {{ if .IDPCaURI }} 3 |

Copy IDP CA Certificate From URL

4 | 5 |

Copy this CA Certificate and download it to your .kube directory

6 |
7 | 8 | 11 |
curl --create-dirs -s {{ .IDPCaURI }} -o ${HOME}/.kube/certs/{{ .ClusterName }}/idp-ca.crt
12 |
13 | {{ end }} 14 | 15 | {{ if .IDPCaPem }} 16 |

Copy IDP CA Certificate From PEM

17 | 18 |

Put the CA Certificate into your .kube directory

19 | 20 |
21 | 22 | 25 |
mkdir -p ${HOME}/.kube/certs/{{ .ClusterName }}/ && cat << EOF > ${HOME}/.kube/certs/{{ .ClusterName }}/idp-ca.crt
 26 | {{ .IDPCaPem }}
 27 | EOF
28 |
29 | {{ end }} 30 | 31 | 32 | {{ if .K8sCaURI }} 33 |

Copy Kubernetes CA Certificate From URL

34 | 35 |

Copy this CA Certificate and download it to your .kube directory

36 |
37 | 38 | 41 |
curl --create-dirs -s {{ .K8sCaURI }} -o ${HOME}/.kube/certs/{{ .ClusterName }}/k8s-ca.crt
42 |
43 | {{ end }} 44 | 45 | {{ if .K8sCaPem }} 46 |

Copy Kubernetes CA Certificate From PEM

47 | 48 |

Put the CA Certificate into your .kube directory

49 | 50 |
51 | 52 | 55 |
mkdir -p ${HOME}/.kube/certs/{{ .ClusterName }}/ && cat << EOF > ${HOME}/.kube/certs/{{ .ClusterName }}/k8s-ca.crt
 56 | {{ .K8sCaPem }}
 57 | EOF
58 |
59 | {{ end }} 60 | 61 |

Run configuration commands

62 | 63 |

These commands will update ~/.kube/config

64 | 65 |
66 | 67 | 70 |
kubectl config set-cluster {{ .ClusterName }} \
 71 |   {{- if or .K8sCaPem .K8sCaURI }}
 72 |     --certificate-authority=${HOME}/.kube/certs/{{ .ClusterName}}/k8s-ca.crt \
 73 |   {{- end }}
 74 |     --server={{ .K8sMasterURI }}
75 |
76 | 77 |
78 | 79 | 82 |
kubectl config set-credentials {{ .Username }}-{{ .ClusterName }} \
 83 |     --auth-provider=oidc \
 84 |     --auth-provider-arg="idp-issuer-url={{ .Issuer }}" \
 85 |     --auth-provider-arg="client-id={{ .ClientID }}" \
 86 |     --auth-provider-arg="client-secret={{ .ClientSecret }}" \
 87 |     --auth-provider-arg="refresh-token={{ .RefreshToken }}" \
 88 |     --auth-provider-arg="id-token={{ .IDToken }}"
 89 |   {{- if or (.IDPCaURI) (.IDPCaPem) }} \
 90 |     --auth-provider-arg=idp-certificate-authority=${HOME}/.kube/certs/{{ .ClusterName }}/idp-ca.crt
 91 |   {{- end }}
92 |
93 | 94 |
95 | 96 | 99 |
kubectl config set-context {{ if not .StaticContextName }}{{ .Username }}-{{ end }}{{ .ClusterName }} \
100 |     --cluster={{ .ClusterName }}{{ if .Namespace }} --namespace={{ .Namespace }}{{ end }} \
101 |     --user={{ .Username}}-{{.ClusterName }}
102 |
103 | 104 |
105 | 106 | 109 |
kubectl config use-context {{ if not .StaticContextName }}{{ .Username }}-{{ end }}{{ .ClusterName}}
110 |
111 | 112 | {{ end }} 113 | -------------------------------------------------------------------------------- /templates/linux-tab.html: -------------------------------------------------------------------------------- 1 | {{ define "linux-tab-content" }} 2 |

Install and Set Up kubectl

3 | 17 | {{ template "linux-mac-common" . }} 18 | {{ end }} 19 | -------------------------------------------------------------------------------- /templates/mac-tab.html: -------------------------------------------------------------------------------- 1 | {{ define "mac-tab-content" }} 2 |

Install and Set Up kubectl

3 | 17 | {{ template "linux-mac-common" . }} 18 | {{ end }} -------------------------------------------------------------------------------- /templates/windows-tab.html: -------------------------------------------------------------------------------- 1 | {{ define "windows-tab-content" }} 2 |

Install and Set Up kubectl

3 | 18 | 19 | {{ if .IDPCaURI }} 20 |

Copy IDP CA Certificate From URL

21 | 22 |

Copy this CA Certificate and download it to your .kube directory

23 |
24 | 25 | 28 | 29 |
curl --create-dirs -s {{ .IDPCaURI }} -o ${HOME}/.kube/certs/{{ .ClusterName }}/idp-ca.crt
30 |
31 | {{ end }} 32 | 33 | {{ if .IDPCaPem }} 34 |

Copy IDP CA From Pem

35 | 36 |

Put the CA Certificate into your .kube directory

37 |
38 | 39 | 42 | 43 |
mkdir -p ${HOME}/.kube/certs/{{ .ClusterName }}/ && cat << EOF > ${HOME}/.kube/certs/{{ .ClusterName }}/idp-ca.crt
 44 | {{ .IDPCaPem}}
 45 | EOF
46 |
47 | {{ end }} 48 | 49 | 50 | {{ if .K8sCaURI }} 51 |

Copy Kubernetes CA Certificate From URL

52 | 53 |

Copy this CA Certificate and download it to your .kube directory

54 |
55 | 56 | 59 |
curl --create-dirs -s {{ .K8sCaURI }} -o ${HOME}/.kube/certs/{{ .ClusterName }}/k8s-ca.crt
60 |
61 | {{ end }} 62 | 63 | {{ if .K8sCaPem }} 64 |

Copy Kubernetes CA Certificate From PEM

65 | 66 |

Put the CA Certificate into your .kube directory

67 | 68 |
69 | 70 | 73 |
mkdir -p ${HOME}/.kube/certs/{{ .ClusterName }}/ && cat << EOF > ${HOME}/.kube/certs/{{ .ClusterName }}/k8s-ca.crt
 74 | {{ .K8sCaPem }}
 75 | EOF
76 |
77 | {{ end }} 78 | 79 |

Run configuration commands

80 | 81 |

These commands will update ~/.kube/config

82 | 83 |
84 | 85 | 88 |
kubectl config set-cluster {{ .ClusterName }}
 89 |     {{- if or .K8sCaPem .K8sCaURI }} --certificate-authority=${HOME}/.kube/certs/{{ .ClusterName}}/k8s-ca.crt
 90 |     {{- end }} --server={{ .K8sMasterURI }}
91 |
92 | 93 |
94 | 95 | 98 |
kubectl config set-credentials {{ .Username }}-{{ .ClusterName }} --auth-provider=oidc --auth-provider-arg=idp-issuer-url={{ .Issuer }} --auth-provider-arg=client-id={{ .ClientID }} --auth-provider-arg=client-secret={{ .ClientSecret }} --auth-provider-arg=refresh-token={{ .RefreshToken }} --auth-provider-arg=id-token={{ .IDToken }}
 99 |   {{- if or (.IDPCaURI) (.IDPCaPem) }} --auth-provider-arg=idp-certificate-authority=${HOME}/.kube/certs/{{ .ClusterName }}/idp-ca.crt
100 |   {{- end }}
101 |
102 |
103 | 104 | 107 |
kubectl config set-context {{ if not .StaticContextName }}{{ .Username }}-{{ end }}{{ .ClusterName }} --cluster={{ .ClusterName }} --user={{ .Username}}-{{.ClusterName }}
108 |
109 | 110 |
111 | 114 |
kubectl config use-context {{ if not .StaticContextName }}{{ .Username }}-{{ end }}{{ .ClusterName}}
115 |
116 | 117 | {{ end }} 118 | -------------------------------------------------------------------------------- /tests/e2e/helm/dex-k8s-auth-overrides.yaml: -------------------------------------------------------------------------------- 1 | image: 2 | repository: mintel/dex-k8s-authenticator 3 | tag: ${CI_TAG} 4 | pullPolicy: Never 5 | 6 | service: 7 | type: NodePort 8 | port: 5555 9 | nodePort: 30000 10 | 11 | dexK8sAuthenticator: 12 | port: 5555 13 | clusters: 14 | - name: my-cluster 15 | short_description: "My Cluster" 16 | description: "Example Cluster Long Description..." 17 | client_secret: pUBnBOY80SnXgjibTYM9ZWNzY2xreNGQok 18 | issuer: http://${NODE_IP}:30001 19 | k8s_master_uri: https://my-cluster.example.com 20 | client_id: my-cluster 21 | redirect_uri: http://${NODE_IP}:30000/callback/my-cluster 22 | k8s_ca_uri: https://url-to-your-ca.crt 23 | 24 | ingress: 25 | enabled: true 26 | path: / 27 | hosts: 28 | - dex-k8s-authenticator.${NODE_IP}.nip.io 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /tests/e2e/helm/dex-overrides.yaml: -------------------------------------------------------------------------------- 1 | # Default values for dex 2 | 3 | service: 4 | type: NodePort 5 | port: 5556 6 | nodePort: 30001 7 | 8 | ingress: 9 | enabled: true 10 | path: / 11 | hosts: 12 | - dex.${NODE_IP}.nip.io 13 | 14 | config: |- 15 | issuer: http://${NODE_IP}:30001 16 | 17 | storage: 18 | type: kubernetes 19 | config: 20 | inCluster: true 21 | 22 | web: 23 | http: 0.0.0.0:5556 24 | 25 | frontend: 26 | theme: "coreos" 27 | issuer: "Example Co" 28 | issuerUrl: "https://example.com" 29 | logoUrl: https://example.com/images/logo-250x25.png 30 | 31 | expiry: 32 | signingKeys: "6h" 33 | idTokens: "24h" 34 | 35 | logger: 36 | level: debug 37 | format: json 38 | 39 | oauth2: 40 | responseTypes: ["code", "token", "id_token"] 41 | skipApprovalScreen: true 42 | 43 | staticClients: 44 | - id: my-cluster 45 | name: "my-cluster" 46 | secret: "pUBnBOY80SnXgjibTYM9ZWNzY2xreNGQok" 47 | redirectURIs: 48 | - http://${NODE_IP}:30000/callback/my-cluster 49 | enablePasswordDB: True 50 | staticPasswords: 51 | - email: "admin@example.com" 52 | # bcrypt hash of the string "password" 53 | hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" 54 | username: "admin" 55 | userID: "08a8684b-db88-4b73-90a9-3cd1661f5466" 56 | --------------------------------------------------------------------------------