feat: initial implementation of ibm cis webhook

This commit is contained in:
James Reeve 2023-12-13 19:40:08 -05:00
parent 5131c1f665
commit 9420237c3c
No known key found for this signature in database
31 changed files with 696 additions and 546 deletions

View file

@ -0,0 +1,63 @@
name: Release Helm Chart
on:
push:
branches:
- master
tags:
- "**"
pull_request:
branches:
- master
jobs:
release:
if: github.event_name == 'push' # This ensures release only runs on push events, not on pull_request events
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Install yq - portable yaml processor
uses: mikefarah/yq@v4.30.5
- name: Install Helm
uses: azure/setup-helm@v1
with:
version: v3.4.0
- name: Lint chart
run: helm lint deploy/cert-manager-ibm-cis-webhook
- name: Fetch charts dependencies
run: |
cd deploy/cert-manager-ibm-cis-webhook
helm dependency build
- name: Package Helm Chart
run: |
helm package deploy/cert-manager-ibm-cis-webhook
- name: Check Out Helm Chart Repository
uses: actions/checkout@v2
with:
repository: ibm-skills-network/charts
token: ${{ secrets.PUBLIC_HELM_CHART_REPO_PUBLISH_TOKEN }}
path: charts-repo
ref: gh-pages
- name: Copy Packaged Chart to Charts Repo
run: |
cp cert-manager-ibm-cis-webhook*.tgz charts-repo/
- name: Update Helm Chart Repository Index
run: |
cd charts-repo
git config user.name "$GITHUB_ACTOR"
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
helm repo index . --url https://ibm-skills-network.github.io/charts/ --merge index.yaml
git add .
git commit -m "Update Helm chart for my-chart"
git push

46
.github/workflows/release-image.yml vendored Normal file
View file

@ -0,0 +1,46 @@
name: Build and Publish Docker Image
on:
push:
branches:
- '*'
tags:
- '*'
jobs:
build-and-publish:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Check Out Repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
if: startsWith(github.ref, 'refs/tags/')
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Tag Name
if: startsWith(github.ref, 'refs/tags/')
id: tag_name
run: echo "::set-output name=TAG_NAME::${GITHUB_REF##*/}"
- name: Build Docker Image
uses: docker/build-push-action@v5
with:
context: .
file: Dockerfile
push: ${{ startsWith(github.ref, 'refs/tags/') }}
tags: |
ghcr.io/${{ github.repository }}:latest
ghcr.io/${{ github.repository }}:1
${{ steps.tag_name.outputs.TAG_NAME != '' && format('ghcr.io/{0}:{1}', github.repository, steps.tag_name.outputs.TAG_NAME) }}

31
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,31 @@
name: Test
on:
push:
branches:
- master
jobs:
test:
concurrency:
group: test
runs-on: ubuntu-latest
steps:
- name: Check Out Repository
uses: actions/checkout@v2
- name: Run tests
run: |
cat > testdata/ibm-cloud-cis/config.json <<EOF
{
"ibmCloudCisCrns": [ "$TEST_CIS_INSTANCE_CRN" ]
}
EOF
cat testdata/ibm-cloud-cis/config.json
make test
env:
TEST_ZONE_NAME: ${{ vars.TEST_ZONE_NAME }}
TEST_DNS_RECORD: ${{ vars.TEST_DNS_RECORD }}
TEST_CIS_INSTANCE_CRN: ${{ vars.TEST_CIS_INSTANCE_CRN }}
IBMCLOUD_API_KEY: ${{ secrets.IBMCLOUD_API_KEY }}

1
.tool-versions Normal file
View file

@ -0,0 +1 @@
golang 1.21.5

View file

@ -11,11 +11,11 @@ RUN go mod download
FROM build_deps AS build
COPY . .
COPY main.go .
RUN CGO_ENABLED=0 go build -o webhook -ldflags '-w -extldflags "-static"' .
FROM alpine:3.18
FROM alpine:3.18 as final
RUN apk add --no-cache ca-certificates

View file

@ -2,14 +2,14 @@ GO ?= $(shell which go)
OS ?= $(shell $(GO) env GOOS)
ARCH ?= $(shell $(GO) env GOARCH)
IMAGE_NAME := "webhook"
IMAGE_NAME := "icr.io/skills-network/cert-manager-webhook-ibm-cis"
IMAGE_TAG := "latest"
OUT := $(shell pwd)/_out
KUBEBUILDER_VERSION=1.28.0
HELM_FILES := $(shell find deploy/example-webhook)
HELM_FILES := $(shell find deploy/cert-manager-ibm-cis-webhook)
test: _test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH)/etcd _test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH)/kube-apiserver _test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH)/kubectl
TEST_ASSET_ETCD=_test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH)/etcd \
@ -29,17 +29,17 @@ clean:
.PHONY: build
build:
docker build -t "$(IMAGE_NAME):$(IMAGE_TAG)" .
docker buildx build -t "$(IMAGE_NAME):$(IMAGE_TAG)" . --platform linux/amd64
.PHONY: rendered-manifest.yaml
rendered-manifest.yaml: $(OUT)/rendered-manifest.yaml
$(OUT)/rendered-manifest.yaml: $(HELM_FILES) | $(OUT)
helm template \
--name example-webhook \
--name cert-manager-ibm-cis-webhook \
--set image.repository=$(IMAGE_NAME) \
--set image.tag=$(IMAGE_TAG) \
deploy/example-webhook > $@
deploy/cert-manager-ibm-cis-webhook > $@
_test $(OUT) _test/kubebuilder-$(KUBEBUILDER_VERSION)-$(OS)-$(ARCH):
mkdir -p $@

View file

@ -1,58 +1,57 @@
<p align="center">
<img src="https://raw.githubusercontent.com/cert-manager/cert-manager/d53c0b9270f8cd90d908460d69502694e1838f5f/logo/logo-small.png" height="256" width="256" alt="cert-manager project logo" />
</p>
# Cert Manager IBM Cloud Internet Services Webhook Solver
# ACME webhook example
The ACME issuer type supports an optional 'webhook' solver, which can be used
Cert Manager's ACME (automated certificate management environment) issuer type supports an optional 'webhook' solver, which can be used
to implement custom DNS01 challenge solving logic.
This is useful if you need to use cert-manager with a DNS provider that is not
officially supported in cert-manager core.
IBM Cloud Internet Services is not officially supported in cert-manager core, so if you want to automatically provision certificates with cert-manager using DNS challenges, you can use this repository to do so.
## Why not in core?
## Usage
As the project & adoption has grown, there has been an influx of DNS provider
pull requests to our core codebase. As this number has grown, the test matrix
has become un-maintainable and so, it's not possible for us to certify that
providers work to a sufficient level.
### Prerequisites
By creating this 'interface' between cert-manager and DNS providers, we allow
users to quickly iterate and test out new integrations, and then packaging
those up themselves as 'extensions' to cert-manager.
You must have cert-manager already installed in your cluster.
We can also then provide a standardised 'testing framework', or set of
conformance tests, which allow us to validate the a DNS provider works as
expected.
See [installation instructions here](https://cert-manager.io/docs/installation/).
## Creating your own webhook
### Installation
Webhook's themselves are deployed as Kubernetes API services, in order to allow
administrators to restrict access to webhooks with Kubernetes RBAC.
You can install this webhook using helm:
This is important, as otherwise it'd be possible for anyone with access to your
webhook to complete ACME challenge validations and obtain certificates.
To make the set up of these webhook's easier, we provide a template repository
that can be used to get started quickly.
### Creating your own repository
### Running the test suite
All DNS providers **must** run the DNS01 provider conformance testing suite,
else they will have undetermined behaviour when used with cert-manager.
**It is essential that you configure and run the test suite when creating a
DNS01 webhook.**
An example Go test file has been provided in [main_test.go](https://github.com/cert-manager/webhook-example/blob/master/main_test.go).
You can run the test suite with:
```bash
$ TEST_ZONE_NAME=example.com. make test
```shell
helm install cert-manager-ibm-cis-webhook --set ibmCloudApiKey="<your IBM Cloud API key>"
```
The example file has a number of areas you must fill in and replace with your
own options in order for tests to pass.
### Issuer
Create or update an `Issuer` (or `ClusterIssuer`) to reference the newly installed solver:
```yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: acme-dns-issuer
spec:
acme:
email: you@your.email.domain.com
privateKeySecretRef:
name: letsencrypt
server: https://acme-v02.api.letsencrypt.org/directory
solvers:
- dns01:
webhook:
config:
ibmCloudCisCrns:
- 'crn:v1:bluemix:public:internet-svcs:global:a/***:***::'
groupName: acme.skills.network
solverName: ibm-cloud-cis
selector:
dnsZones:
- your.site.domain.com
```
After update your issuer, cert-manager should be able to automatically complete challenges for your certificates on IBM CIS-managed domains.
## Contributing
Contributions are welcome.
Please see [docs/CONTRIBUTING.md](./docs/CONTRIBUTING.md) to get started.

4
chart-releaser.yaml Normal file
View file

@ -0,0 +1,4 @@
owner: ibm-skills-network
git-repo: charts
# package-path: deploy/cert-manager-ibm-cis-webhook/
# package-path: charts

View file

@ -1,5 +1,5 @@
apiVersion: v1
appVersion: "1.0"
description: A Helm chart for Kubernetes
name: example-webhook
version: 0.1.0
name: cert-manager-ibm-cis-webhook
version: 1.0.0

View file

@ -2,7 +2,7 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "example-webhook.name" -}}
{{- define "cert-manager-ibm-cis-webhook.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
@ -11,7 +11,7 @@ Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "example-webhook.fullname" -}}
{{- define "cert-manager-ibm-cis-webhook.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
@ -27,22 +27,22 @@ If release name contains chart name it will be used as a full name.
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "example-webhook.chart" -}}
{{- define "cert-manager-ibm-cis-webhook.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "example-webhook.selfSignedIssuer" -}}
{{ printf "%s-selfsign" (include "example-webhook.fullname" .) }}
{{- define "cert-manager-ibm-cis-webhook.selfSignedIssuer" -}}
{{ printf "%s-selfsign" (include "cert-manager-ibm-cis-webhook.fullname" .) }}
{{- end -}}
{{- define "example-webhook.rootCAIssuer" -}}
{{ printf "%s-ca" (include "example-webhook.fullname" .) }}
{{- define "cert-manager-ibm-cis-webhook.rootCAIssuer" -}}
{{ printf "%s-ca" (include "cert-manager-ibm-cis-webhook.fullname" .) }}
{{- end -}}
{{- define "example-webhook.rootCACertificate" -}}
{{ printf "%s-ca" (include "example-webhook.fullname" .) }}
{{- define "cert-manager-ibm-cis-webhook.rootCACertificate" -}}
{{ printf "%s-ca" (include "cert-manager-ibm-cis-webhook.fullname" .) }}
{{- end -}}
{{- define "example-webhook.servingCertificate" -}}
{{ printf "%s-webhook-tls" (include "example-webhook.fullname" .) }}
{{- define "cert-manager-ibm-cis-webhook.servingCertificate" -}}
{{ printf "%s-webhook-tls" (include "cert-manager-ibm-cis-webhook.fullname" .) }}
{{- end -}}

View file

@ -3,17 +3,17 @@ kind: APIService
metadata:
name: v1alpha1.{{ .Values.groupName }}
labels:
app: {{ include "example-webhook.name" . }}
chart: {{ include "example-webhook.chart" . }}
app: {{ include "cert-manager-ibm-cis-webhook.name" . }}
chart: {{ include "cert-manager-ibm-cis-webhook.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
annotations:
cert-manager.io/inject-ca-from: "{{ .Release.Namespace }}/{{ include "example-webhook.servingCertificate" . }}"
cert-manager.io/inject-ca-from: "{{ .Release.Namespace }}/{{ include "cert-manager-ibm-cis-webhook.servingCertificate" . }}"
spec:
group: {{ .Values.groupName }}
groupPriorityMinimum: 1000
versionPriority: 15
service:
name: {{ include "example-webhook.fullname" . }}
name: {{ include "cert-manager-ibm-cis-webhook.fullname" . }}
namespace: {{ .Release.Namespace }}
version: v1alpha1

View file

@ -1,26 +1,30 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "example-webhook.fullname" . }}
name: {{ include "cert-manager-ibm-cis-webhook.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
app: {{ include "example-webhook.name" . }}
chart: {{ include "example-webhook.chart" . }}
app: {{ include "cert-manager-ibm-cis-webhook.name" . }}
chart: {{ include "cert-manager-ibm-cis-webhook.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ include "example-webhook.name" . }}
app: {{ include "cert-manager-ibm-cis-webhook.name" . }}
release: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ include "example-webhook.name" . }}
app: {{ include "cert-manager-ibm-cis-webhook.name" . }}
release: {{ .Release.Name }}
spec:
serviceAccountName: {{ include "example-webhook.fullname" . }}
serviceAccountName: {{ include "cert-manager-ibm-cis-webhook.fullname" . }}
imagePullSecrets:
{{- range .Values.imagePullSecrets }}
- name: {{ .name | quote }}
{{- end }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
@ -31,6 +35,8 @@ spec:
env:
- name: GROUP_NAME
value: {{ .Values.groupName | quote }}
- name: IBMCLOUD_API_KEY
value: {{ .Values.ibmCloudApiKey | quote }}
ports:
- name: https
containerPort: 443
@ -54,7 +60,7 @@ spec:
volumes:
- name: certs
secret:
secretName: {{ include "example-webhook.servingCertificate" . }}
secretName: {{ include "cert-manager-ibm-cis-webhook.servingCertificate" . }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{ toYaml . | indent 8 }}

View file

@ -0,0 +1,76 @@
---
# Create a selfsigned Issuer, in order to create a root CA certificate for
# signing webhook serving certificates
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: {{ include "cert-manager-ibm-cis-webhook.selfSignedIssuer" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
app: {{ include "cert-manager-ibm-cis-webhook.name" . }}
chart: {{ include "cert-manager-ibm-cis-webhook.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
selfSigned: {}
---
# Generate a CA Certificate used to sign certificates for the webhook
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: {{ include "cert-manager-ibm-cis-webhook.rootCACertificate" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
app: {{ include "cert-manager-ibm-cis-webhook.name" . }}
chart: {{ include "cert-manager-ibm-cis-webhook.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
secretName: {{ include "cert-manager-ibm-cis-webhook.rootCACertificate" . }}
duration: 43800h # 5y
issuerRef:
name: {{ include "cert-manager-ibm-cis-webhook.selfSignedIssuer" . }}
commonName: "ca.cert-manager-ibm-cis-webhook.cert-manager"
isCA: true
---
# Create an Issuer that uses the above generated CA certificate to issue certs
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: {{ include "cert-manager-ibm-cis-webhook.rootCAIssuer" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
app: {{ include "cert-manager-ibm-cis-webhook.name" . }}
chart: {{ include "cert-manager-ibm-cis-webhook.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
ca:
secretName: {{ include "cert-manager-ibm-cis-webhook.rootCACertificate" . }}
---
# Finally, generate a serving certificate for the webhook to use
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: {{ include "cert-manager-ibm-cis-webhook.servingCertificate" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
app: {{ include "cert-manager-ibm-cis-webhook.name" . }}
chart: {{ include "cert-manager-ibm-cis-webhook.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
secretName: {{ include "cert-manager-ibm-cis-webhook.servingCertificate" . }}
duration: 8760h # 1y
issuerRef:
name: {{ include "cert-manager-ibm-cis-webhook.rootCAIssuer" . }}
dnsNames:
- {{ include "cert-manager-ibm-cis-webhook.fullname" . }}
- {{ include "cert-manager-ibm-cis-webhook.fullname" . }}.{{ .Release.Namespace }}
- {{ include "cert-manager-ibm-cis-webhook.fullname" . }}.{{ .Release.Namespace }}.svc

View file

@ -1,11 +1,11 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "example-webhook.fullname" . }}
name: {{ include "cert-manager-ibm-cis-webhook.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
app: {{ include "example-webhook.name" . }}
chart: {{ include "example-webhook.chart" . }}
app: {{ include "cert-manager-ibm-cis-webhook.name" . }}
chart: {{ include "cert-manager-ibm-cis-webhook.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
---
@ -15,11 +15,11 @@ metadata:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ include "example-webhook.fullname" . }}:webhook-authentication-reader
name: {{ include "cert-manager-ibm-cis-webhook.fullname" . }}:webhook-authentication-reader
namespace: kube-system
labels:
app: {{ include "example-webhook.name" . }}
chart: {{ include "example-webhook.chart" . }}
app: {{ include "cert-manager-ibm-cis-webhook.name" . }}
chart: {{ include "cert-manager-ibm-cis-webhook.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
roleRef:
@ -29,7 +29,7 @@ roleRef:
subjects:
- apiGroup: ""
kind: ServiceAccount
name: {{ include "example-webhook.fullname" . }}
name: {{ include "cert-manager-ibm-cis-webhook.fullname" . }}
namespace: {{ .Release.Namespace }}
---
# apiserver gets the auth-delegator role to delegate auth decisions to
@ -37,10 +37,10 @@ subjects:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ include "example-webhook.fullname" . }}:auth-delegator
name: {{ include "cert-manager-ibm-cis-webhook.fullname" . }}:auth-delegator
labels:
app: {{ include "example-webhook.name" . }}
chart: {{ include "example-webhook.chart" . }}
app: {{ include "cert-manager-ibm-cis-webhook.name" . }}
chart: {{ include "cert-manager-ibm-cis-webhook.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
roleRef:
@ -50,17 +50,17 @@ roleRef:
subjects:
- apiGroup: ""
kind: ServiceAccount
name: {{ include "example-webhook.fullname" . }}
name: {{ include "cert-manager-ibm-cis-webhook.fullname" . }}
namespace: {{ .Release.Namespace }}
---
# Grant cert-manager permission to validate using our apiserver
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ include "example-webhook.fullname" . }}:domain-solver
name: {{ include "cert-manager-ibm-cis-webhook.fullname" . }}:domain-solver
labels:
app: {{ include "example-webhook.name" . }}
chart: {{ include "example-webhook.chart" . }}
app: {{ include "cert-manager-ibm-cis-webhook.name" . }}
chart: {{ include "cert-manager-ibm-cis-webhook.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
rules:
@ -74,16 +74,16 @@ rules:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ include "example-webhook.fullname" . }}:domain-solver
name: {{ include "cert-manager-ibm-cis-webhook.fullname" . }}:domain-solver
labels:
app: {{ include "example-webhook.name" . }}
chart: {{ include "example-webhook.chart" . }}
app: {{ include "cert-manager-ibm-cis-webhook.name" . }}
chart: {{ include "cert-manager-ibm-cis-webhook.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ include "example-webhook.fullname" . }}:domain-solver
name: {{ include "cert-manager-ibm-cis-webhook.fullname" . }}:domain-solver
subjects:
- apiGroup: ""
kind: ServiceAccount

View file

@ -0,0 +1,7 @@
apiVersion: v1
kind: Secret
metadata:
name: ibmcis-credentials
type: Opaque
stringData:
api-token: {{ .Values.ibmCloudApiKey | quote }}

View file

@ -1,11 +1,11 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "example-webhook.fullname" . }}
name: {{ include "cert-manager-ibm-cis-webhook.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
app: {{ include "example-webhook.name" . }}
chart: {{ include "example-webhook.chart" . }}
app: {{ include "cert-manager-ibm-cis-webhook.name" . }}
chart: {{ include "cert-manager-ibm-cis-webhook.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
@ -16,5 +16,5 @@ spec:
protocol: TCP
name: https
selector:
app: {{ include "example-webhook.name" . }}
app: {{ include "cert-manager-ibm-cis-webhook.name" . }}
release: {{ .Release.Name }}

View file

@ -6,16 +6,21 @@
# solve the DNS01 challenge.
# This group name should be **unique**, hence using your own company's domain
# here is recommended.
groupName: acme.mycompany.com
groupName: acme.skills.network
ibmCloudApiKey: ""
certManager:
namespace: cert-manager
serviceAccountName: cert-manager
image:
repository: mycompany/webhook-image
repository: icr.io/skills-network/cert-manager-webhook-ibm-cis
tag: latest
pullPolicy: IfNotPresent
imagePullSecrets: []
# e.g.
# imagePullSecrets:
# - name: icr-io
nameOverride: ""
fullnameOverride: ""

View file

@ -1,76 +0,0 @@
---
# Create a selfsigned Issuer, in order to create a root CA certificate for
# signing webhook serving certificates
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: {{ include "example-webhook.selfSignedIssuer" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
app: {{ include "example-webhook.name" . }}
chart: {{ include "example-webhook.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
selfSigned: {}
---
# Generate a CA Certificate used to sign certificates for the webhook
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: {{ include "example-webhook.rootCACertificate" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
app: {{ include "example-webhook.name" . }}
chart: {{ include "example-webhook.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
secretName: {{ include "example-webhook.rootCACertificate" . }}
duration: 43800h # 5y
issuerRef:
name: {{ include "example-webhook.selfSignedIssuer" . }}
commonName: "ca.example-webhook.cert-manager"
isCA: true
---
# Create an Issuer that uses the above generated CA certificate to issue certs
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: {{ include "example-webhook.rootCAIssuer" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
app: {{ include "example-webhook.name" . }}
chart: {{ include "example-webhook.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
ca:
secretName: {{ include "example-webhook.rootCACertificate" . }}
---
# Finally, generate a serving certificate for the webhook to use
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: {{ include "example-webhook.servingCertificate" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
app: {{ include "example-webhook.name" . }}
chart: {{ include "example-webhook.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
secretName: {{ include "example-webhook.servingCertificate" . }}
duration: 8760h # 1y
issuerRef:
name: {{ include "example-webhook.rootCAIssuer" . }}
dnsNames:
- {{ include "example-webhook.fullname" . }}
- {{ include "example-webhook.fullname" . }}.{{ .Release.Namespace }}
- {{ include "example-webhook.fullname" . }}.{{ .Release.Namespace }}.svc

22
docs/CONTRIBUTING.md Normal file
View file

@ -0,0 +1,22 @@
# CONTRIBUTING
## Install Pre-requisites
1. Install asdf if not already installed. See [asdf's installation instructions here](https://asdf-vm.com/guide/getting-started.html).
1. Install go using [asdf](https://asdf-vm.com/):
```bash
asdf plugin add golang
asdf install
```
## Run Tests
You will need to provide your own IBM Cloud Internet Services instance to run the tests.
Once you have provisioned the instance, update [testdata/ibm-cloud-cis/config.json](./../testdata/ibm-cloud-cis/config.json) with your instance's CRN.
You can run the test suite with:
```bash
IBMCLOUD_API_KEY=<your_api_key> TEST_ZONE_NAME=example.com TEST_DNS_RECORD=test.example.com make test
```

View file

@ -1,69 +0,0 @@
package example
import (
"fmt"
"github.com/miekg/dns"
)
func (e *exampleSolver) handleDNSRequest(w dns.ResponseWriter, req *dns.Msg) {
msg := new(dns.Msg)
msg.SetReply(req)
switch req.Opcode {
case dns.OpcodeQuery:
for _, q := range msg.Question {
if err := e.addDNSAnswer(q, msg, req); err != nil {
msg.SetRcode(req, dns.RcodeServerFailure)
break
}
}
}
w.WriteMsg(msg)
}
func (e *exampleSolver) addDNSAnswer(q dns.Question, msg *dns.Msg, req *dns.Msg) error {
switch q.Qtype {
// Always return loopback for any A query
case dns.TypeA:
rr, err := dns.NewRR(fmt.Sprintf("%s 5 IN A 127.0.0.1", q.Name))
if err != nil {
return err
}
msg.Answer = append(msg.Answer, rr)
return nil
// TXT records are the only important record for ACME dns-01 challenges
case dns.TypeTXT:
e.RLock()
record, found := e.txtRecords[q.Name]
e.RUnlock()
if !found {
msg.SetRcode(req, dns.RcodeNameError)
return nil
}
rr, err := dns.NewRR(fmt.Sprintf("%s 5 IN TXT %s", q.Name, record))
if err != nil {
return err
}
msg.Answer = append(msg.Answer, rr)
return nil
// NS and SOA are for authoritative lookups, return obviously invalid data
case dns.TypeNS:
rr, err := dns.NewRR(fmt.Sprintf("%s 5 IN NS ns.example-acme-webook.invalid.", q.Name))
if err != nil {
return err
}
msg.Answer = append(msg.Answer, rr)
return nil
case dns.TypeSOA:
rr, err := dns.NewRR(fmt.Sprintf("%s 5 IN SOA %s 20 5 5 5 5", "ns.example-acme-webook.invalid.", "ns.example-acme-webook.invalid."))
if err != nil {
return err
}
msg.Answer = append(msg.Answer, rr)
return nil
default:
return fmt.Errorf("unimplemented record type %v", q.Qtype)
}
}

View file

@ -1,68 +0,0 @@
// package example contains a self-contained example of a webhook that passes the cert-manager
// DNS conformance tests
package example
import (
"fmt"
"os"
"sync"
"github.com/cert-manager/cert-manager/pkg/acme/webhook"
acme "github.com/cert-manager/cert-manager/pkg/acme/webhook/apis/acme/v1alpha1"
"github.com/miekg/dns"
"k8s.io/client-go/rest"
)
type exampleSolver struct {
name string
server *dns.Server
txtRecords map[string]string
sync.RWMutex
}
func (e *exampleSolver) Name() string {
return e.name
}
func (e *exampleSolver) Present(ch *acme.ChallengeRequest) error {
e.Lock()
e.txtRecords[ch.ResolvedFQDN] = ch.Key
e.Unlock()
return nil
}
func (e *exampleSolver) CleanUp(ch *acme.ChallengeRequest) error {
e.Lock()
delete(e.txtRecords, ch.ResolvedFQDN)
e.Unlock()
return nil
}
func (e *exampleSolver) Initialize(kubeClientConfig *rest.Config, stopCh <-chan struct{}) error {
go func(done <-chan struct{}) {
<-done
if err := e.server.Shutdown(); err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err.Error())
}
}(stopCh)
go func() {
if err := e.server.ListenAndServe(); err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err.Error())
os.Exit(1)
}
}()
return nil
}
func New(port string) webhook.Solver {
e := &exampleSolver{
name: "example",
txtRecords: make(map[string]string),
}
e.server = &dns.Server{
Addr: ":" + port,
Net: "udp",
Handler: dns.HandlerFunc(e.handleDNSRequest),
}
return e
}

View file

@ -1,96 +0,0 @@
package example
import (
"crypto/rand"
"math/big"
"testing"
acme "github.com/cert-manager/cert-manager/pkg/acme/webhook/apis/acme/v1alpha1"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
)
func TestExampleSolver_Name(t *testing.T) {
port, _ := rand.Int(rand.Reader, big.NewInt(50000))
port = port.Add(port, big.NewInt(15534))
solver := New(port.String())
assert.Equal(t, "example", solver.Name())
}
func TestExampleSolver_Initialize(t *testing.T) {
port, _ := rand.Int(rand.Reader, big.NewInt(50000))
port = port.Add(port, big.NewInt(15534))
solver := New(port.String())
done := make(chan struct{})
err := solver.Initialize(nil, done)
assert.NoError(t, err, "Expected Initialize not to error")
close(done)
}
func TestExampleSolver_Present_Cleanup(t *testing.T) {
port, _ := rand.Int(rand.Reader, big.NewInt(50000))
port = port.Add(port, big.NewInt(15534))
solver := New(port.String())
done := make(chan struct{})
err := solver.Initialize(nil, done)
assert.NoError(t, err, "Expected Initialize not to error")
validTestData := []struct {
hostname string
record string
}{
{"test1.example.com.", "testkey1"},
{"test2.example.com.", "testkey2"},
{"test3.example.com.", "testkey3"},
}
for _, test := range validTestData {
err := solver.Present(&acme.ChallengeRequest{
Action: acme.ChallengeActionPresent,
Type: "dns-01",
ResolvedFQDN: test.hostname,
Key: test.record,
})
assert.NoError(t, err, "Unexpected error while presenting %v", t)
}
// Resolve test data
for _, test := range validTestData {
msg := new(dns.Msg)
msg.Id = dns.Id()
msg.RecursionDesired = true
msg.Question = make([]dns.Question, 1)
msg.Question[0] = dns.Question{dns.Fqdn(test.hostname), dns.TypeTXT, dns.ClassINET}
in, err := dns.Exchange(msg, "127.0.0.1:"+port.String())
assert.NoError(t, err, "Presented record %s not resolvable", test.hostname)
assert.Len(t, in.Answer, 1, "RR response is of incorrect length")
assert.Equal(t, []string{test.record}, in.Answer[0].(*dns.TXT).Txt, "TXT record returned did not match presented record")
}
// Cleanup test data
for _, test := range validTestData {
err := solver.CleanUp(&acme.ChallengeRequest{
Action: acme.ChallengeActionCleanUp,
Type: "dns-01",
ResolvedFQDN: test.hostname,
Key: test.record,
})
assert.NoError(t, err, "Unexpected error while cleaning up %v", t)
}
// Resolve test data
for _, test := range validTestData {
msg := new(dns.Msg)
msg.Id = dns.Id()
msg.RecursionDesired = true
msg.Question = make([]dns.Question, 1)
msg.Question[0] = dns.Question{dns.Fqdn(test.hostname), dns.TypeTXT, dns.ClassINET}
in, err := dns.Exchange(msg, "127.0.0.1:"+port.String())
assert.NoError(t, err, "Presented record %s not resolvable", test.hostname)
assert.Len(t, in.Answer, 0, "RR response is of incorrect length")
assert.Equal(t, dns.RcodeNameError, in.Rcode, "Expexted NXDOMAIN")
}
close(done)
}

12
go.mod
View file

@ -1,8 +1,9 @@
module github.com/cert-manager/webhook-example
module github.com/ibm-skills-network/cert-manager-webhook-ibm-cis
go 1.20
require (
github.com/IBM-Cloud/bluemix-go v0.0.0-20231207111833-cf0937e108a4
github.com/cert-manager/cert-manager v1.12.6
github.com/miekg/dns v1.1.50
github.com/stretchr/testify v1.8.4
@ -13,7 +14,7 @@ require (
require (
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
@ -37,7 +38,7 @@ require (
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/cel-go v0.12.6 // indirect
github.com/google/gnostic v0.6.9 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
@ -57,6 +58,7 @@ require (
github.com/prometheus/client_model v0.4.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.7.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stoewer/go-strcase v1.2.0 // indirect
@ -76,7 +78,7 @@ require (
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/crypto v0.15.0 // indirect
golang.org/x/mod v0.10.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.18.0 // indirect
golang.org/x/oauth2 v0.10.0 // indirect
golang.org/x/sync v0.3.0 // indirect
@ -84,7 +86,7 @@ require (
golang.org/x/term v0.14.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.9.1 // indirect
golang.org/x/tools v0.12.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect

190
go.sum
View file

@ -5,14 +5,16 @@ cloud.google.com/go/compute v1.21.0 h1:JNBsyXVoOoNJtTQcnEY5uYpZIbeCTYIeDe0Xh1byS
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
github.com/IBM-Cloud/bluemix-go v0.0.0-20231207111833-cf0937e108a4 h1:UlbO9OE3LGGSDKOk3CrJs2KGVYT5rhj8x37ewBtGVzI=
github.com/IBM-Cloud/bluemix-go v0.0.0-20231207111833-cf0937e108a4/go.mod h1:jIGLnIfj+uBv2ALz3rVHzNbNwt0V/bEWNeJKECa8Q+k=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves=
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@ -28,6 +30,9 @@ github.com/cert-manager/cert-manager v1.12.6/go.mod h1:9eiWlqHtsG5H5A77mG6K9mmjS
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
@ -60,24 +65,31 @@ github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2Vvl
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo=
github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA=
github.com/go-openapi/errors v0.20.3/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8=
github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/strfmt v0.21.7/go.mod h1:adeGTkxE44sPyLk0JV235VQAO/ZXUr8KAzYjclFs3ew=
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
@ -103,6 +115,7 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/cel-go v0.12.6 h1:kjeKudqV0OygrAqA9fX6J55S8gj+Jre2tckIm5RoG4M=
github.com/google/cel-go v0.12.6/go.mod h1:Jk7ljRzLBhkmiAwBoUxB1sZSCVBAzkqPF25olK/iRDw=
@ -113,12 +126,16 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
@ -131,6 +148,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
@ -143,6 +162,7 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
@ -157,15 +177,60 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU=
github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0=
github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo=
github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw=
github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo=
github.com/onsi/ginkgo/v2 v2.8.1/go.mod h1:N1/NbDngAFcSLdyZ+/aYTYGSlq9qMCS/cNKGJjy+csc=
github.com/onsi/ginkgo/v2 v2.9.0/go.mod h1:4xkjoL/tZv4SMWeww56BU5kAt19mVB47gTWxmrTcxyk=
github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo=
github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0=
github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo=
github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc=
github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM=
github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg=
github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM=
github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
github.com/onsi/gomega v1.27.1/go.mod h1:aHX5xOykVYzWOV4WqQy0sy8BQptgukenXpCXfadcIAw=
github.com/onsi/gomega v1.27.3/go.mod h1:5vG284IBtfDAmDyrK+eGyZmUgUlmi+Wngqo557cZ6Gw=
github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ=
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4=
github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ=
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -183,7 +248,8 @@ github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
@ -197,20 +263,29 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/etcd/api/v3 v3.5.7 h1:sbcmosSVesNrWOJ58ZQFitHMdncusIifYcrBfwrlJSY=
go.etcd.io/etcd/api/v3 v3.5.7/go.mod h1:9qew1gCdDDLu+VwmeG+iFpL+QlpHTo7iubavdVDgCAA=
@ -222,6 +297,7 @@ go.etcd.io/etcd/client/v3 v3.5.7/go.mod h1:sOWmj9DZUMyAngS7QQwCyAXXAL6WhgTOPLNS/
go.etcd.io/etcd/pkg/v3 v3.5.7 h1:obOzeVwerFwZ9trMWapU/VjDcYUJb5OfgC1zqEGWO/0=
go.etcd.io/etcd/raft/v3 v3.5.7 h1:aN79qxLmV3SvIq84aNTliYGmjwsW6NqJSnqmI1HLJKc=
go.etcd.io/etcd/server/v3 v3.5.7 h1:BTBD8IJUV7YFgsczZMHhMTS67XuA4KpRquL0MFOJGRk=
go.mongodb.org/mongo-driver v1.11.3/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 h1:RsQi0qJ2imFfCvZabqzM9cNXBG8k6gXMv1A0cXRmH6A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0/go.mod h1:vsh3ySueQCiKPxFLvjWC4Z135gIa34TQ/NSqkDTZYUM=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0 h1:KfYpVmrjI7JuToy5k8XV3nkapjWx48k4E4JOtVstzQI=
@ -253,6 +329,12 @@ go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -263,10 +345,18 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@ -274,11 +364,30 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -292,22 +401,62 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -315,6 +464,16 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
@ -326,11 +485,20 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -372,19 +540,25 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=

229
main.go
View file

@ -4,143 +4,182 @@ import (
"encoding/json"
"fmt"
"os"
"strings"
log "github.com/sirupsen/logrus"
extapi "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"github.com/cert-manager/cert-manager/pkg/acme/webhook/apis/acme/v1alpha1"
"github.com/cert-manager/cert-manager/pkg/acme/webhook/cmd"
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
cis "github.com/IBM-Cloud/bluemix-go/api/cis/cisv1"
cissession "github.com/IBM-Cloud/bluemix-go/session"
)
var GroupName = os.Getenv("GROUP_NAME")
var IbmCloudApiKey = os.Getenv("IBMCLOUD_API_KEY")
func main() {
if GroupName == "" {
panic("GROUP_NAME must be specified")
log.Fatal("GROUP_NAME must be specified")
}
// This will register our custom DNS provider with the webhook serving
// library, making it available as an API under the provided GroupName.
// You can register multiple DNS provider implementations with a single
// webhook, where the Name() method will be used to disambiguate between
// the different implementations.
cmd.RunWebhookServer(GroupName,
&customDNSProviderSolver{},
)
if IbmCloudApiKey == "" {
log.Fatal("IBMCLOUD_API_KEY must be specified")
}
cmd.RunWebhookServer(GroupName, &ibmCloudCisProviderSolver{})
}
// customDNSProviderSolver implements the provider-specific logic needed to
// 'present' an ACME challenge TXT record for your own DNS provider.
// To do so, it must implement the `github.com/cert-manager/cert-manager/pkg/acme/webhook.Solver`
// interface.
type customDNSProviderSolver struct {
// If a Kubernetes 'clientset' is needed, you must:
// 1. uncomment the additional `client` field in this structure below
// 2. uncomment the "k8s.io/client-go/kubernetes" import at the top of the file
// 3. uncomment the relevant code in the Initialize method below
// 4. ensure your webhook's service account has the required RBAC role
// assigned to it for interacting with the Kubernetes APIs you need.
//client kubernetes.Clientset
type ibmCloudCisProviderSolver struct {
client *kubernetes.Clientset
ibmCloudCisApi cis.CisServiceAPI
}
// customDNSProviderConfig is a structure that is used to decode into when
// solving a DNS01 challenge.
// This information is provided by cert-manager, and may be a reference to
// additional configuration that's needed to solve the challenge for this
// particular certificate or issuer.
// This typically includes references to Secret resources containing DNS
// provider credentials, in cases where a 'multi-tenant' DNS solver is being
// created.
// If you do *not* require per-issuer or per-certificate configuration to be
// provided to your webhook, you can skip decoding altogether in favour of
// using CLI flags or similar to provide configuration.
// You should not include sensitive information here. If credentials need to
// be used by your provider here, you should reference a Kubernetes Secret
// resource and fetch these credentials using a Kubernetes clientset.
type customDNSProviderConfig struct {
// Change the two fields below according to the format of the configuration
// to be decoded.
// These fields will be set by users in the
// `issuer.spec.acme.dns01.providers.webhook.config` field.
//Email string `json:"email"`
//APIKeySecretRef v1alpha1.SecretKeySelector `json:"apiKeySecretRef"`
type ibmCloudCisDnsProviderConfig struct {
IbmCloudCisCrns []string `json:"ibmCloudCisCrns"`
APIKeySecretRef cmmeta.SecretKeySelector `json:"apiKeySecretRef"`
}
// Name is used as the name for this DNS solver when referencing it on the ACME
// Issuer resource.
// This should be unique **within the group name**, i.e. you can have two
// solvers configured with the same Name() **so long as they do not co-exist
// within a single webhook deployment**.
// For example, `cloudflare` may be used as the name of a solver.
func (c *customDNSProviderSolver) Name() string {
return "my-custom-solver"
func (c *ibmCloudCisProviderSolver) Name() string {
return "ibm-cloud-cis"
}
// Present is responsible for actually presenting the DNS record with the
// DNS provider.
// This method should tolerate being called multiple times with the same value.
// cert-manager itself will later perform a self check to ensure that the
// solver has correctly configured the DNS provider.
func (c *customDNSProviderSolver) Present(ch *v1alpha1.ChallengeRequest) error {
func (c *ibmCloudCisProviderSolver) Present(ch *v1alpha1.ChallengeRequest) error {
cfg, err := loadConfig(ch.Config)
if err != nil {
return err
}
// TODO: do something more useful with the decoded configuration
fmt.Printf("Decoded configuration %v", cfg)
zonesApi := c.ibmCloudCisApi.Zones()
for _, crn := range cfg.IbmCloudCisCrns {
myZones, err := zonesApi.ListZones(crn)
if err != nil {
log.WithError(err).WithField("crn", crn).Error("Error listing zones")
continue
}
longestMatchZone := findLongestMatchingZone(myZones, ch.ResolvedFQDN)
if longestMatchZone != nil {
if err := c.createDNSChallengeRecord(crn, longestMatchZone.Id, ch); err != nil {
return err
}
}
}
// TODO: add code that sets a record in the DNS provider's console
return nil
}
// CleanUp should delete the relevant TXT record from the DNS provider console.
// If multiple TXT records exist with the same record name (e.g.
// _acme-challenge.example.com) then **only** the record with the same `key`
// value provided on the ChallengeRequest should be cleaned up.
// This is in order to facilitate multiple DNS validations for the same domain
// concurrently.
func (c *customDNSProviderSolver) CleanUp(ch *v1alpha1.ChallengeRequest) error {
// TODO: add code that deletes a record from the DNS provider's console
func findLongestMatchingZone(zones []cis.Zone, fqdn string) *cis.Zone {
var longestMatchZone *cis.Zone
var longestMatchLength int
for _, zone := range zones {
zoneNameWithDot := zone.Name + "."
if strings.HasSuffix(fqdn, zoneNameWithDot) && len(zoneNameWithDot) > longestMatchLength {
longestMatchLength = len(zoneNameWithDot)
longestMatchZone = &zone
}
}
return longestMatchZone
}
func (c *ibmCloudCisProviderSolver) createDNSChallengeRecord(crn, zoneID string, ch *v1alpha1.ChallengeRequest) error {
dnsAPI := c.ibmCloudCisApi.Dns()
_, err := dnsAPI.CreateDns(crn, zoneID, cis.DnsBody{
Name: ch.ResolvedFQDN,
DnsType: "TXT",
Content: ch.Key,
})
if err != nil {
log.WithError(err).WithFields(log.Fields{"crn": crn, "zoneID": zoneID}).Error("Error creating DNS01 challenge")
return err
}
log.WithFields(log.Fields{"fqdn": ch.ResolvedFQDN, "key": ch.Key}).Info("DNS01 challenge created")
return nil
}
// Initialize will be called when the webhook first starts.
// This method can be used to instantiate the webhook, i.e. initialising
// connections or warming up caches.
// Typically, the kubeClientConfig parameter is used to build a Kubernetes
// client that can be used to fetch resources from the Kubernetes API, e.g.
// Secret resources containing credentials used to authenticate with DNS
// provider accounts.
// The stopCh can be used to handle early termination of the webhook, in cases
// where a SIGTERM or similar signal is sent to the webhook process.
func (c *customDNSProviderSolver) Initialize(kubeClientConfig *rest.Config, stopCh <-chan struct{}) error {
///// UNCOMMENT THE BELOW CODE TO MAKE A KUBERNETES CLIENTSET AVAILABLE TO
///// YOUR CUSTOM DNS PROVIDER
func (c *ibmCloudCisProviderSolver) CleanUp(ch *v1alpha1.ChallengeRequest) error {
cfg, err := loadConfig(ch.Config)
if err != nil {
return err
}
//cl, err := kubernetes.NewForConfig(kubeClientConfig)
//if err != nil {
// return err
//}
//
//c.client = cl
zonesApi := c.ibmCloudCisApi.Zones()
for _, crn := range cfg.IbmCloudCisCrns {
myZones, err := zonesApi.ListZones(crn)
if err != nil {
log.WithError(err).WithField("crn", crn).Error("Error listing zones")
continue
}
longestMatchZone := findLongestMatchingZone(myZones, ch.ResolvedFQDN)
if longestMatchZone != nil {
if err := c.deleteMatchingTXTRecords(crn, longestMatchZone.Id, ch); err != nil {
log.WithError(err).Error("Error deleting TXT record")
}
}
}
///// END OF CODE TO MAKE KUBERNETES CLIENTSET AVAILABLE
return nil
}
// loadConfig is a small helper function that decodes JSON configuration into
// the typed config struct.
func loadConfig(cfgJSON *extapi.JSON) (customDNSProviderConfig, error) {
cfg := customDNSProviderConfig{}
// handle the 'base case' where no configuration has been provided
func (c *ibmCloudCisProviderSolver) deleteMatchingTXTRecords(crn, zoneID string, ch *v1alpha1.ChallengeRequest) error {
dnsAPI := c.ibmCloudCisApi.Dns()
myDnsrecs, err := dnsAPI.ListDns(crn, zoneID)
if err != nil {
return err
}
for _, myDnsrec := range myDnsrecs {
if myDnsrec.DnsType == "TXT" && (myDnsrec.Name+".") == ch.ResolvedFQDN && myDnsrec.Content == ch.Key {
if err := dnsAPI.DeleteDns(crn, zoneID, myDnsrec.Id); err != nil {
return err
}
}
}
return nil
}
func (c *ibmCloudCisProviderSolver) Initialize(kubeClientConfig *rest.Config, stopCh <-chan struct{}) error {
cl, err := kubernetes.NewForConfig(kubeClientConfig)
if err != nil {
return err
}
ibmSession, err := cissession.New()
if err != nil {
return fmt.Errorf("IBM Cloud session failed: %w", err)
}
ibmCloudCisApi, err := cis.New(ibmSession)
if err != nil {
return err
}
c.ibmCloudCisApi = ibmCloudCisApi
c.client = cl
return nil
}
func loadConfig(cfgJSON *extapi.JSON) (ibmCloudCisDnsProviderConfig, error) {
cfg := ibmCloudCisDnsProviderConfig{}
if cfgJSON == nil {
return cfg, nil
return cfg, fmt.Errorf("config JSON is nil")
}
if err := json.Unmarshal(cfgJSON.Raw, &cfg); err != nil {
return cfg, fmt.Errorf("error decoding solver config: %v", err)
return cfg, fmt.Errorf("error decoding solver config: %w", err)
}
return cfg, nil
}

View file

@ -5,37 +5,22 @@ import (
"testing"
acmetest "github.com/cert-manager/cert-manager/test/acme"
"github.com/cert-manager/webhook-example/example"
)
var (
zone = os.Getenv("TEST_ZONE_NAME")
zone = os.Getenv("TEST_ZONE_NAME")
dnsRecord = os.Getenv("TEST_DNS_RECORD")
)
func TestRunsSuite(t *testing.T) {
// The manifest path should contain a file named config.json that is a
// snippet of valid configuration that should be included on the
// ChallengeRequest passed as part of the test cases.
//
// Uncomment the below fixture when implementing your custom DNS provider
//fixture := acmetest.NewFixture(&customDNSProviderSolver{},
// acmetest.SetResolvedZone(zone),
// acmetest.SetAllowAmbientCredentials(false),
// acmetest.SetManifestPath("testdata/my-custom-solver"),
// acmetest.SetBinariesPath("_test/kubebuilder/bin"),
//)
solver := example.New("59351")
solver := &ibmCloudCisProviderSolver{}
fixture := acmetest.NewFixture(solver,
acmetest.SetResolvedZone("example.com."),
acmetest.SetManifestPath("testdata/my-custom-solver"),
acmetest.SetDNSServer("127.0.0.1:59351"),
acmetest.SetStrict(true),
acmetest.SetDNSName(dnsRecord),
acmetest.SetResolvedZone(zone),
acmetest.SetManifestPath("testdata/ibm-cloud-cis"),
acmetest.SetUseAuthoritative(false),
)
//need to uncomment and RunConformance delete runBasic and runExtended once https://github.com/cert-manager/cert-manager/pull/4835 is merged
//fixture.RunConformance(t)
fixture.RunBasic(t)
fixture.RunExtended(t)
fixture.RunConformance(t)
}

3
testdata/ibm-cloud-cis/config.json vendored Normal file
View file

@ -0,0 +1,3 @@
{
"ibmCloudCisCrns": [ "crn:v1:bluemix:public:internet-svcs:global:a/f13f7985d6f4e8621dc8a18df501122f:f01bbe09-6580-4926-a690-b0a93a71210a::" ]
}

View file

@ -1,3 +0,0 @@
# Solver testdata directory
TODO

View file

@ -1 +0,0 @@
{}