diff --git a/.github/workflows/hybrid-pr-build.yaml b/.github/workflows/hybrid-pr-build.yaml new file mode 100644 index 0000000..98e8148 --- /dev/null +++ b/.github/workflows/hybrid-pr-build.yaml @@ -0,0 +1,22 @@ +name: Hybrid PR Build + +on: + pull_request: + paths: + - 'hybrid/**' + +defaults: + run: + working-directory: hybrid + +env: + GO_VERSION: 1.19 + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 + - uses: actions/setup-go@b22fbbc2921299758641fab08929b4ac52b32923 # v3.2.0 + with: + go-version: ${{ env.GO_VERSION }} diff --git a/.github/workflows/scripts/split.sh b/.github/workflows/scripts/split.sh new file mode 100644 index 0000000..e0b0dec --- /dev/null +++ b/.github/workflows/scripts/split.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +set -e + +if [ -z "$NUM_RUNNERS" ]; then + echo "split.sh: NUM_RUNNERS environment variable must be set" + exit 1 +fi + +if [ -z "$THIS_RUNNER" ]; then + echo "split.sh: THIS_RUNNER environment variable must be set" + exit 1 +fi + +declare -a job_set +current_runner=1 +for FILE in test/integration/suites/*; do + job_set[$current_runner]+="${FILE##test/integration/} " + + ((current_runner++)) + if [ $current_runner -gt "$NUM_RUNNERS" ]; then + current_runner=1 + fi +done + +echo "${job_set[$THIS_RUNNER]}" \ No newline at end of file diff --git a/hybrid/Makefile b/hybrid/Makefile new file mode 100644 index 0000000..0b0dc20 --- /dev/null +++ b/hybrid/Makefile @@ -0,0 +1,55 @@ +.ONESHELL: + +EKS_DIR ?= ./dev/kubernetes +BINARIES ?= hybrid_server hybrid_agent +OSES ?= linux +ARCHITECTURES ?= amd64 arm64 +VERSION ?= latest +DOCKER_HUB ?= ${DOCKER_HUB} +DOCKER_TAG_AGENT ?= hybrid-attestor-server:latest hybrid-attestor-agent:latest +BUILD_DIR ?= ./build +PLATFORMS ?= $(foreach os, $(OSES), $(foreach architecture, $(ARCHITECTURES), --platform $(os)/$(architecture))) + +BUILD_TARGETS := $(foreach binary, $(BINARIES), $(foreach os, $(OSES), $(foreach architecture, $(ARCHITECTURES), $(binary)-$(os)-$(architecture)))) +DOCKER_TARGETS := $(foreach binary, $(BINARIES), $(binary)-docker) + +target_words = $(subst -, ,$@) +target_binary = $(word 1, $(target_words)) +target_os = $(word 2, $(target_words)) +target_architecture = $(word 3, $(target_words)) +target_software_type = $(word 2, $(subst _, ,$(target_binary))) + +target_binary_hyphens = $(subst _,-attestor-,$(target_binary)) + +build: $(BUILD_TARGETS) +$(BUILD_TARGETS): + CGO_ENABLED=0 GOOS=$(target_os) GOARCH=$(target_architecture) go build -ldflags="-s -w -extldflags -static" -o $(BUILD_DIR)/$(target_os)/$(target_architecture)/$(target_binary) cmd/$(target_binary)/main.go + +test: test-unit test-integration + go test ./... + +test-unit: + go test ./... + +test-integration: + bash ./test/integration/run.sh + +docker: $(DOCKER_TARGETS) +$(DOCKER_TARGETS): + docker build -f ./dev/docker/$(target_software_type).Dockerfile $(PLATFORMS) --build-arg BINARY=$(target_binary) -t $(DOCKER_HUB)/$(target_binary_hyphens):$(VERSION) . + docker push $(DOCKER_HUB)/$(target_binary_hyphens):$(VERSION) + +docker-build: + CGO_ENABLED=0 GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build -ldflags="-s -w -extldflags -static" -o ${BINARY} cmd/${BINARY}/main.go + +deploy-spire-eks: + kubectl delete --all daemonsets.app --namespace=spire + kubectl delete --all statefulset.app --namespace=spire + kubectl delete --ignore-not-found namespace spire + envsubst < $(EKS_DIR)/server.yaml | kubectl apply -f - + envsubst < $(EKS_DIR)/agent.yaml | kubectl apply -f - + +clean: + rm -rf $(BUILD_DIR) + +.PHONY: $(BUILD_TARGETS) $(DOCKER_TARGETS) build test docker clean diff --git a/hybrid/README.md b/hybrid/README.md new file mode 100644 index 0000000..95e0039 --- /dev/null +++ b/hybrid/README.md @@ -0,0 +1,80 @@ +[![PR Build](https://github.com/HewlettPackard/roven/actions/workflows/hybrid-pr-build.yaml/badge.svg)](https://github.com/HewlettPackard/roven/actions/workflows/hybrid-pr-build.yaml) + +# Hybrid Node Attestor +The `hybrid` node attestor plugin for SPIRE is an external plugin, that combines the power of any built-in plugin supported by spire. With this approach you can use any combination of the built-in plugins in order to attest the node. For example, you can mix the k8s_psat and the aws_iid plugins to attest that the agent node is running on an AWS EKS or an EC2 instance with a self managed k8s cluster. + +## SpiffeID +The hybrid plugin will always return the SpiffeID generated by the first plugin of the list supplied to the server. The order of the plugins supplied for both server and agent does not matter. + +## Basic deployment +The hybrid plugin works as any external plugin would. It is designed to work as an external plugin, acting as a plugin aggregator. +To deploy it, first build and update the configuration for both SPIRE Server and Agent passing the list of built-in plugins that will be used in combination ir order to attest the node. + +## Building +Start by building the binaries + +`make build` + +## Configuring +In order to use the hybrid plugin with the SPIRE instance, add in the SPIRE configuration file as an external plugin, informing the name, `plugin_cmd` and in the section plugins, inside `plugin_data`, add the built-in plugins configuration. Here's an example: + + **Server** + ``` + plugins{ + NodeAttestor "hybrid" { + plugin_cmd = "path/to/builded/file" + plugin_data { + plugins { + first_selected_plugin { + [plugin_config] + } + second_selected_plugin { + [plugin_config] + } + [...] + } + } + } + } +``` + +**Agent** + ``` + plugins { + NodeAttestor "hybrid" { + plugin_cmd = "path/to/builded/file" + plugin_data { + plugins { + first_selected_plugin { + [plugin_config] + } + second_selected_plugin { + [plugin_config] + } + } + } + } + } + ``` + +### Deploying the hybrid using aws_iid and k8s_psat node attestors +To combine those two plugins, you have to be running the agent and server in an kubernetes instance inside aws. + +Start off by creating an ec2 instance and deploying a kind kubernetes cluster or creating an eks cluster. + +After that, run the following make command while in hybrid root folder: + +`make build` + +Then, to construct the docker images and push to any repo desired, set the environment variable DOCKER_HUB that should point to the docker image registry and it's used by the MAKEFILE and run the following command: + +`make docker` + +With the hybrid node attestor built and the docker image constructed, you now have to deploy it in the running kubernetes cluster in the aws. +Configure kubectl to point to the aws cluster. +Change the credentials in the server.yaml and agent.yaml that are in the hybrid/dev/kubernetes and run the following command: + +`make deploy-agent-server-eks` + +You should now be able to see the running agent/server node attestor. First set the corresponding environment variables with their required values ($AWS_SECRET_ACCESS_KEY, $AWS_ACCESS_KEY_ID, $AWS_ASSUME_ROLE, $AWS_ACCOUNT_ID) + diff --git a/hybrid/SECURITY.md b/hybrid/SECURITY.md new file mode 100644 index 0000000..44dc518 --- /dev/null +++ b/hybrid/SECURITY.md @@ -0,0 +1,14 @@ +# Security Policy + +## Supported Versions + +Versions of the project that are currently being supported with security updates: + +| Version | Supported | +| ------- | ------------------ | +| 1.0.0 | :white_check_mark: | + + +## Reporting a Vulnerability + +If you've found a vulnerability or a potential vulnerability in this plugin please let us know at juliano.fantozzi@hpe.com. We'll send a confirmation email to acknowledge your report, and we'll send an additional email when we've identified the issue positively or negatively. \ No newline at end of file diff --git a/hybrid/cmd/hybrid_agent/main.go b/hybrid/cmd/hybrid_agent/main.go new file mode 100644 index 0000000..94a1818 --- /dev/null +++ b/hybrid/cmd/hybrid_agent/main.go @@ -0,0 +1,17 @@ +package main + +import ( + hybrid_agent "github.com/hewlettpackard/hybrid/pkg/agent" + + "github.com/spiffe/spire-plugin-sdk/pluginmain" + nodeattestorv1 "github.com/spiffe/spire-plugin-sdk/proto/spire/plugin/agent/nodeattestor/v1" + configv1 "github.com/spiffe/spire-plugin-sdk/proto/spire/service/common/config/v1" +) + +func main() { + p := hybrid_agent.New() + pluginmain.Serve( + nodeattestorv1.NodeAttestorPluginServer(p), + configv1.ConfigServiceServer(p), + ) +} diff --git a/hybrid/cmd/hybrid_server/main.go b/hybrid/cmd/hybrid_server/main.go new file mode 100644 index 0000000..4002471 --- /dev/null +++ b/hybrid/cmd/hybrid_server/main.go @@ -0,0 +1,17 @@ +package main + +import ( + hybrid_server "github.com/hewlettpackard/hybrid/pkg/server" + + "github.com/spiffe/spire-plugin-sdk/pluginmain" + nodeattestorv1 "github.com/spiffe/spire-plugin-sdk/proto/spire/plugin/server/nodeattestor/v1" + configv1 "github.com/spiffe/spire-plugin-sdk/proto/spire/service/common/config/v1" +) + +func main() { + p := hybrid_server.New() + pluginmain.Serve( + nodeattestorv1.NodeAttestorPluginServer(p), + configv1.ConfigServiceServer(p), + ) +} diff --git a/hybrid/dev/docker/agent.Dockerfile b/hybrid/dev/docker/agent.Dockerfile new file mode 100644 index 0000000..ba79734 --- /dev/null +++ b/hybrid/dev/docker/agent.Dockerfile @@ -0,0 +1,3 @@ +FROM gcr.io/spiffe-io/spire-agent:1.4.4 AS spire-agent-psat-iid +COPY ./build/linux/amd64/hybrid_agent /usr/local/bin/agentattestor +RUN chmod +x /usr/local/bin/agentattestor diff --git a/hybrid/dev/docker/server.Dockerfile b/hybrid/dev/docker/server.Dockerfile new file mode 100644 index 0000000..e12d482 --- /dev/null +++ b/hybrid/dev/docker/server.Dockerfile @@ -0,0 +1,3 @@ +FROM gcr.io/spiffe-io/spire-server:1.4.4 AS spire-server-psat-iid +COPY ./build/linux/amd64/hybrid_server /usr/local/bin/serverattestor +RUN chmod +x /usr/local/bin/serverattestor diff --git a/hybrid/dev/kubernetes/agent.yaml b/hybrid/dev/kubernetes/agent.yaml new file mode 100644 index 0000000..4b12af6 --- /dev/null +++ b/hybrid/dev/kubernetes/agent.yaml @@ -0,0 +1,263 @@ +--- +# Source: spire-agent/templates/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: spire-agent + namespace: spire +--- +# Source: spire-agent/templates/configmaps.yaml +# ConfigMap containing the SPIRE agent configuration. +apiVersion: v1 +kind: ConfigMap +metadata: + name: spire-agent + namespace: spire +data: + agent.conf: | + agent { + data_dir = "/run/spire" + log_level = "DEBUG" + server_address = "spire-server" + server_port = "8081" + socket_path = "/run/secrets/workload-spiffe-uds/socket" + trust_bundle_path = "/run/spire/bundle/root-cert.pem" + trust_domain = "cluster.local" + + } + + plugins { + NodeAttestor "hybrid" { + plugin_cmd = "/usr/local/bin/agentattestor" + plugin_data { + plugins { + k8s_psat { + cluster = "hybrid-node-attestor" + } + aws_iid { + accountId = "$AWS_ACCOUNT_ID" + } + } + } + } + + KeyManager "memory" { + plugin_data { + } + } + + WorkloadAttestor "k8s" { + plugin_data { + # Defaults to the secure kubelet port by default. + # Minikube does not have a cert in the cluster CA bundle that + # can authenticate the kubelet cert, so skip validation. + skip_kubelet_verification = true + } + } + + WorkloadAttestor "unix" { + plugin_data { + } + } + } + + health_checks { + listener_enabled = true + bind_address = "0.0.0.0" + bind_port = "8080" + live_path = "/live" + ready_path = "/ready" + } +--- +# Source: spire-agent/templates/roles.yaml +# Required cluster role to allow spire-agent to query k8s API server +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: spire-agent-cluster-role +rules: + - apiGroups: [""] + resources: ["pods","nodes","nodes/proxy"] + verbs: ["get"] +--- +# Source: spire-agent/templates/roles.yaml +# Binds above cluster role to spire-agent service account +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: spire-agent-cluster-role-binding +subjects: + - kind: ServiceAccount + name: spire-agent + namespace: spire +roleRef: + kind: ClusterRole + name: spire-agent-cluster-role + apiGroup: rbac.authorization.k8s.io +--- +# Source: spire-agent/templates/daemonset.yaml +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: spire-agent + namespace: spire + labels: + app: spire-agent +spec: + selector: + matchLabels: + app: spire-agent + template: + metadata: + namespace: spire + labels: + app: spire-agent + spec: + hostPID: true + hostNetwork: true + dnsPolicy: ClusterFirstWithHostNet + serviceAccountName: spire-agent + initContainers: + - name: init + # This is a small image with wait-for-it, choose whatever image + # you prefer that waits for a service to be up. This image is built + # from https://github.com/lqhl/wait-for-it + image: gcr.io/spiffe-io/wait-for-it + args: ["-t", "30", "spire-server:8081"] + containers: + - name: spire-agent + image: public.ecr.aws/n9c4h4j5/hybrid-attestor-agent:latest + args: ["-config", "/run/spire/config/agent.conf"] + volumeMounts: + - name: spire-config + mountPath: /run/spire/config + readOnly: true + - name: spire-bundle + mountPath: /run/spire/bundle + - name: spire-agent-socket-dir + mountPath: /run/secrets/workload-spiffe-uds + - name: spire-token + mountPath: /var/run/secrets/tokens + livenessProbe: + httpGet: + path: /live + port: 8080 + failureThreshold: 2 + initialDelaySeconds: 15 + periodSeconds: 60 + timeoutSeconds: 3 + readinessProbe: + httpGet: + path: /ready + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 5 + # This is the container which runs the SPIFFE CSI driver. + - name: spiffe-csi-driver + image: ghcr.io/spiffe/spiffe-csi-driver:0.2.0 + imagePullPolicy: IfNotPresent + args: [ + "-workload-api-socket-dir", "/spire-agent-socket", + "-csi-socket-path", "/spiffe-csi/csi.sock", + ] + env: + # The CSI driver needs a unique node ID. The node name can be + # used for this purpose. + - name: MY_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + volumeMounts: + # The volume containing the SPIRE agent socket. The SPIFFE CSI + # driver will mount this directory into containers. + - mountPath: /spire-agent-socket + name: spire-agent-socket-dir + readOnly: true + # The volume that will contain the CSI driver socket shared + # with the kubelet and the driver registrar. + - mountPath: /spiffe-csi + name: spiffe-csi-socket-dir + # The volume containing mount points for containers. + - mountPath: /var/lib/kubelet/pods + mountPropagation: Bidirectional + name: mountpoint-dir + securityContext: + privileged: true + # This container runs the CSI Node Driver Registrar which takes care + # of all the little details required to register a CSI driver with + # the kubelet. + - name: node-driver-registrar + image: k8s.gcr.io/sig-storage/csi-node-driver-registrar:v2.4.0 + imagePullPolicy: IfNotPresent + args: [ + "-csi-address", "/spiffe-csi/csi.sock", + "-kubelet-registration-path", "/var/lib/kubelet/plugins/csi.spiffe.io/csi.sock", + ] + volumeMounts: + # The registrar needs access to the SPIFFE CSI driver socket + - mountPath: /spiffe-csi + name: spiffe-csi-socket-dir + # The registrar needs access to the Kubelet plugin registration + # directory + - name: kubelet-plugin-registration-dir + mountPath: /registration + volumes: + - name: spire-config + configMap: + name: spire-agent + - name: spire-bundle + configMap: + name: trust-bundle + - name: spire-token + projected: + sources: + - serviceAccountToken: + path: spire-agent + expirationSeconds: 7200 + audience: spire-server + # This volume is used to share the workload api socket between the + # CSI driver and SPIRE agent + - name: spire-agent-socket-dir + hostPath: + path: /run/spire/socket-dir + type: DirectoryOrCreate + # This volume is where the socket for kubelet->driver communication lives + - name: spiffe-csi-socket-dir + hostPath: + path: /var/lib/kubelet/plugins/csi.spiffe.io + type: DirectoryOrCreate + # This volume is where the SPIFFE CSI driver mounts volumes + - name: mountpoint-dir + hostPath: + path: /var/lib/kubelet/pods + type: Directory + # This volume is where the node-driver-registrar registers the plugin + # with kubelet + - name: kubelet-plugin-registration-dir + hostPath: + path: /var/lib/kubelet/plugins_registry + type: Directory +--- +# Source: spire-agent/templates/daemonset.yaml +apiVersion: storage.k8s.io/v1 +kind: CSIDriver +metadata: + name: "csi.spiffe.io" +spec: + # We only support ephemeral, inline volumes. We don't need a controller to + # provision and attach volumes. + attachRequired: false + + # We want the pod information so that the CSI driver can verify that an + # ephemeral mount was requested. + podInfoOnMount: true + + # We don't want (or need) K8s to change ownership on the contents of the mount + # when it is moutned into the pod, since the Workload API is completely open + # (i.e. 0777). + # Note, this was added in Kubernetes 1.19, so omit + fsGroupPolicy: None + + # We only support ephemeral volumes. Note that this requires Kubernetes 1.16 + volumeLifecycleModes: # added in Kubernetes 1.16, this field is beta + - Ephemeral diff --git a/hybrid/dev/kubernetes/kustomization.yaml b/hybrid/dev/kubernetes/kustomization.yaml new file mode 100644 index 0000000..4932107 --- /dev/null +++ b/hybrid/dev/kubernetes/kustomization.yaml @@ -0,0 +1,8 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: spire + +resources: +- server.yaml +- agent.yaml diff --git a/hybrid/dev/kubernetes/server.yaml b/hybrid/dev/kubernetes/server.yaml new file mode 100644 index 0000000..6fb1e0d --- /dev/null +++ b/hybrid/dev/kubernetes/server.yaml @@ -0,0 +1,763 @@ +--- +# Source: spire-server/templates/namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: spire +--- +# Source: spire-server/templates/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: spire-server + namespace: spire +--- +# Source: spire-server/templates/configmaps.yaml +# ConfigMap containing the latest trust bundle for the trust domain. It is +# updated by SPIRE using the k8sbundle notifier plugin. SPIRE agents mount +# this config map and use the certificate to bootstrap trust with the SPIRE +# server during attestation. +apiVersion: v1 +kind: ConfigMap +metadata: + name: trust-bundle + namespace: spire +--- +# Source: spire-server/templates/configmaps.yaml +# ConfigMap containing the SPIRE server configuration. +apiVersion: v1 +kind: ConfigMap +metadata: + name: spire-server + namespace: spire +data: + server.conf: | + server { + bind_address = "0.0.0.0" + bind_port = "8081" + socket_path = "/run/spire/sockets/api.sock" + trust_domain = "cluster.local" + data_dir = "/run/spire/server/data" + log_level = "DEBUG" + ca_key_type = "rsa-2048" + + default_svid_ttl = "1h" + ca_subject = { + country = ["US"], + organization = ["SPIFFE"], + common_name = "", + } + } + + plugins { + DataStore "sql" { + plugin_data { + database_type = "sqlite3" + connection_string = "/run/spire/data/datastore.sqlite3" + } + } + + NodeAttestor "hybrid" { + plugin_cmd = "/usr/local/bin/serverattestor" + plugin_data { + custom_spiffe_id = ["k8s_psat", "aws_iid"] + plugins { + k8s_psat { + clusters = { + "hybrid-node-attestor" = { + use_token_review_api_validation = true + service_account_allow_list = ["spire:spire-agent"] + } + } + } + aws_iid { + access_key_id = "$AWS_ACCESS_KEY_ID" + secret_access_key = "$AWS_SECRET_ACCESS_KEY" + assume_role = "$AWS_ASSUME_ROLE" + } + } + } + } + + KeyManager "disk" { + plugin_data { + keys_path = "/run/spire/data/keys.json" + } + } + + Notifier "k8sbundle" { + plugin_data { + namespace = "spire" + config_map = "trust-bundle" + config_map_key = "root-cert.pem" + } + } + } + + health_checks { + listener_enabled = true + bind_address = "0.0.0.0" + bind_port = "8080" + live_path = "/live" + ready_path = "/ready" + } +--- +# Source: spire-server/templates/controller.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: spire-controller-manager-config + namespace: spire +data: + spireControllerManagerConfig.yaml: | + apiVersion: spire.spiffe.io/v1alpha1 + kind: ControllerManagerConfig + metrics: + bindAddress: 127.0.0.1:8082 + healthProbe: + bindAddress: 127.0.0.1:8083 + leaderElection: + leaderElect: true + resourceName: 98c9c988.spiffe.io + resourceNamespace: spire + clusterName: demo-cluster + trustDomain: cluster.local + ignoreNamespaces: ["kube-system", "kube-public", "local-path-storage", "spire"] +--- +# Source: spire-server/templates/controller.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.4.1 + name: clusterspiffeids.spire.spiffe.io +spec: + group: spire.spiffe.io + names: + kind: ClusterSPIFFEID + listKind: ClusterSPIFFEIDList + plural: clusterspiffeids + singular: clusterspiffeid + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: ClusterSPIFFEID is the Schema for the clusterspiffeids API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ClusterSPIFFEIDSpec defines the desired state of ClusterSPIFFEID + properties: + admin: + description: Admin indicates whether or not the SVID can be used to + access the SPIRE administrative APIs. Extra care should be taken + to only apply this SPIFFE ID to admin workloads. + type: boolean + dnsNameTemplates: + description: DNSNameTemplate represents templates for extra DNS names + that are applicable to SVIDs minted for this ClusterSPIFFEID. The + node and pod spec are made available to the template under .NodeSpec, + .PodSpec respectively. + items: + type: string + type: array + federatesWith: + description: FederatesWith is a list of trust domain names that workloads + that obtain this SPIFFE ID will federate with. + items: + type: string + type: array + namespaceSelector: + description: NamespaceSelector selects the namespaces that are targetted + by this CRD. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector requirement is a selector that + contains values, a key, and an operator that relates the key + and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship to + a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. + type: object + type: object + podSelector: + description: PodSelector selects the pods that are targetted by this + CRD. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector requirement is a selector that + contains values, a key, and an operator that relates the key + and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship to + a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. + type: object + type: object + spiffeIDTemplate: + description: SPIFFEID is the SPIFFE ID template. The node and pod + spec are made available to the template under .NodeSpec, .PodSpec + respectively. + type: string + ttl: + description: TTL indicates an upper-bound time-to-live for SVIDs minted + for this ClusterSPIFFEID. If unset, a default will be chosen. + type: string + workloadSelectorTemplates: + description: WorkloadSelectorTemplates are templates to produce arbitrary + workload selectors that apply to a given workload before it will + receive this SPIFFE ID. The rendered value is interpreted by SPIRE + and are of the form type:value, where the value may, and often does, + contain semicolons, .e.g., k8s:container-image:docker/hello-world + The node and pod spec are made available to the template under .NodeSpec, + .PodSpec respectively. + items: + type: string + type: array + required: + - spiffeIDTemplate + type: object + status: + description: ClusterSPIFFEIDStatus defines the observed state of ClusterSPIFFEID + properties: + stats: + description: Stats produced by the last entry reconciliation run + properties: + entriesMasked: + description: How many entries were masked by entries for other + ClusterSPIFFEIDs. This happens when one or more ClusterSPIFFEIDs + produce an entry for the same pod with the same set of workload + selectors. + type: integer + entriesToSet: + description: How many entries are to be set for this ClusterSPIFFEID. + In nominal conditions, this should reflect the number of pods + selected, but not always if there were problems encountered + rendering an entry for the pod (RenderFailures) or entries are + masked (EntriesMasked). + type: integer + entryFailures: + description: How many entries were unable to be set due to failures + to create or update the entries via the SPIRE Server API. + type: integer + namespacesIgnored: + description: How many (selected) namespaces were ignored (based + on configuration). + type: integer + namespacesSelected: + description: How many namespaces were selected. + type: integer + podEntryRenderFailures: + description: How many failures were encountered rendering an entry + selected pods. This could be due to either a bad template in + the ClusterSPIFFEID or Pod metadata that when applied to the + template did not produce valid entry values. + type: integer + podsSelected: + description: How many pods were selected out of the namespaces. + type: integer + type: object + type: object + type: object + served: true + storage: true +--- +# Source: spire-server/templates/controller.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.4.1 + name: clusterfederatedtrustdomains.spire.spiffe.io +spec: + group: spire.spiffe.io + names: + kind: ClusterFederatedTrustDomain + listKind: ClusterFederatedTrustDomainList + plural: clusterfederatedtrustdomains + singular: clusterfederatedtrustdomain + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .spec.trustDomain + name: Trust Domain + type: string + - jsonPath: .spec.bundleEndpointURL + name: Endpoint URL + type: string + - jsonPath: .spec.bundleEndpointProfile + name: Endpoint Profile + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: ClusterFederatedTrustDomain is the Schema for the clusterfederatedtrustdomains + API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ClusterFederatedTrustDomainSpec defines the desired state + of ClusterFederatedTrustDomain + properties: + bundleEndpointProfile: + description: BundleEndpointProfile is the profile for the bundle endpoint. + properties: + endpointSPIFFEID: + description: EndpointSPIFFEID is the SPIFFE ID of the bundle endpoint. + It is required for the "https_spiffe" profile. + type: string + type: + description: Type is the type of the bundle endpoint profile. + enum: + - https_spiffe + - https_web + type: string + required: + - type + type: object + bundleEndpointURL: + description: BundleEndpointURL is the URL of the bundle endpoint. + It must be an HTTPS URL and cannot contain userinfo (i.e. username/password). + type: string + trustDomain: + description: TrustDomain is the name of the trust domain to federate + with (e.g. example.org) + pattern: '[a-z0-9._-]{1,255}' + type: string + trustDomainBundle: + description: TrustDomainBundle is the initial contents of the bundle + for the referenced trust domain. This field is optional. + type: string + required: + - bundleEndpointProfile + - bundleEndpointURL + - trustDomain + type: object + status: + description: ClusterFederatedTrustDomainStatus defines the observed state + of ClusterFederatedTrustDomain + type: object + type: object + served: true + storage: true +--- +# Source: spire-server/templates/controller.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: manager-role +rules: + - apiGroups: [""] + resources: ["namespaces"] + verbs: ["get", "list", "watch"] + - apiGroups: ["admissionregistration.k8s.io"] + resources: ["validatingwebhookconfigurations"] + verbs: ["get", "list", "patch", "watch"] + - apiGroups: [""] + resources: ["nodes"] + verbs: ["get", "list", "watch"] + - apiGroups: [""] + resources: ["pods"] + verbs: ["get", "list", "watch"] + - apiGroups: ["spire.spiffe.io"] + resources: ["clusterfederatedtrustdomains"] + verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] + - apiGroups: ["spire.spiffe.io"] + resources: ["clusterfederatedtrustdomains/finalizers"] + verbs: ["update"] + - apiGroups: ["spire.spiffe.io"] + resources: ["clusterfederatedtrustdomains/status"] + verbs: ["get", "patch", "update"] + - apiGroups: ["spire.spiffe.io"] + resources: ["clusterspiffeids"] + verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] + - apiGroups: ["spire.spiffe.io"] + resources: ["clusterspiffeids/finalizers"] + verbs: ["update"] + - apiGroups: ["spire.spiffe.io"] + resources: ["clusterspiffeids/status"] + verbs: ["get", "patch", "update"] +--- +# Source: spire-server/templates/roles.yaml +# Required cluster role to allow spire-server to query k8s API server +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: spire-server-cluster-role + namespace: spire +rules: + - apiGroups: + - "" + resources: + - nodes + verbs: + - get + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - get + - create +--- +# Source: spire-server/templates/controller.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: manager-role +subjects: + - kind: ServiceAccount + name: spire-server + namespace: spire +--- +# Source: spire-server/templates/roles.yaml +# Binds above cluster role to spire-server service account +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: spire-server-cluster-role-binding + namespace: spire +subjects: + - kind: ServiceAccount + name: spire-server + namespace: spire +roleRef: + kind: ClusterRole + name: spire-server-cluster-role + apiGroup: rbac.authorization.k8s.io +--- +# Source: spire-server/templates/controller.yaml +# permissions to do leader election. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: leader-election-role + namespace: spire +rules: + - apiGroups: [""] + resources: ["configmaps"] + verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] + - apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] + - apiGroups: [""] + resources: ["events"] + verbs: ["create", "patch"] +--- +# Source: spire-server/templates/roles.yaml +# Role for the SPIRE server +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + namespace: spire + name: spire-server-role +rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - apiGroups: + - "" + resourceNames: + - trust-bundle + resources: + - configmaps + verbs: + - get + - patch +--- +# Source: spire-server/templates/controller.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: leader-election-rolebinding + namespace: spire +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: leader-election-role +subjects: + - kind: ServiceAccount + name: spire-server + namespace: spire +--- +# Source: spire-server/templates/roles.yaml +# RoleBinding granting the spire-server-role to the SPIRE server +# service account. +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: spire-server-role-binding + namespace: spire +subjects: + - kind: ServiceAccount + name: spire-server + namespace: spire +roleRef: + kind: Role + name: spire-server-role + apiGroup: rbac.authorization.k8s.io +--- +# Source: spire-server/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: spire-server + namespace: spire +spec: + type: NodePort + ports: + - name: api + port: 8081 + protocol: TCP + targetPort: 8081 + selector: + app: spire-server +--- +# Source: spire-server/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: spire-server-bundle-endpoint + namespace: spire +spec: + type: NodePort + ports: + - name: api + port: 8443 + protocol: TCP + selector: + app: spire-server +--- +# Source: spire-server/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: spire-controller-manager-webhook-service + namespace: spire +spec: + type: NodePort + ports: + - port: 443 + protocol: TCP + targetPort: 9443 + selector: + app: spire-server +--- +# Source: spire-server/templates/statefulset.yaml +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: spire-server + namespace: spire + labels: + app: spire-server +spec: + replicas: 1 + selector: + matchLabels: + app: spire-server + serviceName: spire-server + template: + metadata: + namespace: spire + labels: + app: spire-server + annotations: + deployment.kubernetes.io/desired-replicas: "1" + deployment.kubernetes.io/max-replicas: "2" + spec: + serviceAccountName: spire-server + shareProcessNamespace: true + containers: + - name: spire-server + image: public.ecr.aws/n9c4h4j5/hybrid-attestor-server:latest + args: ["-config", "/run/spire/config/server.conf"] + livenessProbe: + httpGet: + path: /live + port: 8080 + failureThreshold: 2 + initialDelaySeconds: 15 + periodSeconds: 60 + timeoutSeconds: 3 + readinessProbe: + httpGet: + path: /ready + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 5 + ports: + - containerPort: 8081 + volumeMounts: + - name: spire-config + mountPath: /run/spire/config + readOnly: true + - name: spire-data + mountPath: /run/spire/data + readOnly: false + - name: spire-registration-socket + mountPath: /run/spire/sockets + readOnly: false + - name: spire-controller-manager + image: ghcr.io/spiffe/spire-controller-manager:0.2.1 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 9443 + args: + - "--config=spireControllerManagerConfig.yaml" + volumeMounts: + - name: spire-registration-socket + mountPath: /spire-server + readOnly: true + - name: spire-controller-manager-config + mountPath: /spireControllerManagerConfig.yaml + subPath: spireControllerManagerConfig.yaml + volumes: + - name: spire-config + configMap: + name: spire-server + - name: spire-registration-socket + hostPath: + path: /run/spire/server-sockets + type: DirectoryOrCreate + - name: spire-controller-manager-config + configMap: + name: spire-controller-manager-config + volumeClaimTemplates: + - metadata: + name: spire-data + namespace: spire + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +--- +# Source: spire-server/templates/controller.yaml +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: spire-controller-manager-webhook +webhooks: + - admissionReviewVersions: ["v1"] + clientConfig: + service: + name: spire-controller-manager-webhook-service + namespace: spire + path: /validate-spire-spiffe-io-v1alpha1-clusterfederatedtrustdomain + failurePolicy: Fail + name: vclusterfederatedtrustdomain.kb.io + rules: + - apiGroups: ["spire.spiffe.io"] + apiVersions: ["v1alpha1"] + operations: ["CREATE", "UPDATE"] + resources: ["clusterfederatedtrustdomains"] + sideEffects: None + - admissionReviewVersions: ["v1"] + clientConfig: + service: + name: spire-controller-manager-webhook-service + namespace: spire + path: /validate-spire-spiffe-io-v1alpha1-clusterspiffeid + failurePolicy: Fail + name: vclusterspiffeid.kb.io + rules: + - apiGroups: ["spire.spiffe.io"] + apiVersions: ["v1alpha1"] + operations: ["CREATE", "UPDATE"] + resources: ["clusterspiffeids"] + sideEffects: None diff --git a/hybrid/go.mod b/hybrid/go.mod new file mode 100644 index 0000000..8bdd61d --- /dev/null +++ b/hybrid/go.mod @@ -0,0 +1,130 @@ +module github.com/hewlettpackard/hybrid + +go 1.19 + +replace github.com/spiffe/spire => github.com/spiffe/spire v1.2.1-0.20221027032717-3cbfb6277d23 + +require ( + github.com/hashicorp/go-hclog v1.3.1 + github.com/hashicorp/hcl v1.0.1-0.20190430135223-99e2f22d1c94 + github.com/spiffe/go-spiffe/v2 v2.0.1-0.20220414143532-2ed460a8b9d3 + github.com/spiffe/spire v0.0.0-00010101000000-000000000000 + github.com/spiffe/spire-plugin-sdk v1.4.4 + github.com/stretchr/testify v1.8.1 + google.golang.org/grpc v1.50.1 +) + +require ( + cloud.google.com/go/compute v1.10.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v1.0.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 // indirect + github.com/Azure/go-autorest v14.2.0+incompatible // indirect + github.com/Azure/go-autorest/autorest v0.11.27 // indirect + github.com/Azure/go-autorest/autorest/adal v0.9.20 // indirect + github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect + github.com/Azure/go-autorest/logger v0.2.1 // indirect + github.com/Azure/go-autorest/tracing v0.6.0 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v0.5.1 // indirect + github.com/DataDog/datadog-go v3.2.0+incompatible // indirect + github.com/Microsoft/go-winio v0.6.0 // indirect + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect + github.com/armon/go-metrics v0.4.1 // indirect + github.com/aws/aws-sdk-go-v2 v1.17.1 // indirect + github.com/aws/aws-sdk-go-v2/config v1.17.4 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.12.17 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.14 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.25 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.19 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.3.21 // indirect + github.com/aws/aws-sdk-go-v2/service/ec2 v1.63.0 // indirect + github.com/aws/aws-sdk-go-v2/service/iam v1.18.16 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.18 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.11.20 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.2 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.17.0 // indirect + github.com/aws/smithy-go v1.13.4 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful/v3 v3.8.0 // indirect + github.com/fatih/color v1.13.0 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.19.5 // indirect + github.com/go-openapi/swag v0.19.14 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt v3.2.2+incompatible // indirect + github.com/golang-jwt/jwt/v4 v4.2.0 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/gnostic v0.5.7-v3refs // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect + github.com/googleapis/gax-go/v2 v2.6.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-plugin v1.4.5 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect + github.com/imdario/mergo v0.3.13 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect + github.com/mailru/easyjson v0.7.6 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/mitchellh/go-testing-interface v1.0.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/oklog/run v1.0.0 // indirect + github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_golang v1.13.0 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.37.0 // indirect + github.com/prometheus/procfs v0.8.0 // indirect + github.com/sirupsen/logrus v1.9.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spiffe/spire-api-sdk v1.2.5-0.20221020001527-5895a0279944 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + github.com/uber-go/tally/v4 v4.1.3 // indirect + github.com/zeebo/errs v1.3.0 // indirect + go.opencensus.io v0.23.0 // indirect + go.uber.org/atomic v1.10.0 // indirect + golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect + golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect + golang.org/x/net v0.0.0-20221014081412-f15817d10f9b // indirect + golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect + golang.org/x/sys v0.0.0-20220907062415-87db552b00fd // indirect + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect + golang.org/x/text v0.4.0 // indirect + golang.org/x/time v0.1.0 // indirect + golang.org/x/tools v0.1.12 // indirect + google.golang.org/api v0.101.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20221018160656-63c7b68cfc55 // indirect + google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/square/go-jose.v2 v2.6.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/api v0.25.3 // indirect + k8s.io/apimachinery v0.25.3 // indirect + k8s.io/client-go v0.25.3 // indirect + k8s.io/klog/v2 v2.70.1 // indirect + k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect + k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect + sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) diff --git a/hybrid/go.sum b/hybrid/go.sum new file mode 100644 index 0000000..4620f33 --- /dev/null +++ b/hybrid/go.sum @@ -0,0 +1,848 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v1.10.0 h1:aoLIYaA1fX3ywihqpBk2APQKOo20nXsp1GEZQbx5Jk4= +cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4 h1:pqrAR74b6EoR4kcxF7L7Wg2B8Jgil9UUZtMvxhEFqWo= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 h1:QkAcEIAKbNL4KoFr4SathZPhDhF4mVwpBMFlYjyAqy8= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0/go.mod h1:bhXu1AjYL+wutSL/kpSq6s7733q2Rb0yuot9Zgfqa/0= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 h1:jp0dGvZ7ZK0mgqnTSClMxa5xuRL7NZgHameVYF6BurY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v1.0.0 h1:/Di3vB4sNeQ+7A8efjUVENvyB945Wruvstucqp7ZArg= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v1.0.0/go.mod h1:gM3K25LQlsET3QR+4V74zxCsFAy0r6xMNN9n80SZn+4= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.0.0 h1:lMW1lD/17LUA5z1XTURo7LcVG2ICBPlyMHjIUrcFZNQ= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 h1:QM6sE5k2ZT/vI5BEe0r7mqjsUSnhVBFbOsVkEuaEfiA= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0/go.mod h1:243D9iHbcQXoFUtgHJwL7gl2zx1aDuDMjvBZVGr2uW0= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 h1:ECsQtyERDVz3NP3kvDOTLvbQhqWp/x9EsGKtb4ogUr8= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0/go.mod h1:s1tW/At+xHqjNFvWU4G0c0Qv33KOhvbGNj0RCTQDV8s= +github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A= +github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U= +github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= +github.com/Azure/go-autorest/autorest/adal v0.9.20 h1:gJ3E98kMpFB1MFqQCvA1yFab8vthOeD4VlFRQULxahg= +github.com/Azure/go-autorest/autorest/adal v0.9.20/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= +github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= +github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= +github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/AzureAD/microsoft-authentication-library-for-go v0.5.1 h1:BWe8a+f/t+7KY7zH2mqygeUD0t8hNFXe08p1Pb3/jKE= +github.com/AzureAD/microsoft-authentication-library-for-go v0.5.1/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/GoogleCloudPlatform/cloudsql-proxy v1.33.0 h1:yVfnW2IL8ta7g5q7cPh6CHH5ukyP+Jfk1XCAGo7uF20= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= +github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI= +github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= +github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/aws/aws-sdk-go-v2 v1.16.13/go.mod h1:xSyvSnzh0KLs5H4HJGeIEsNYemUWdNIl0b/rP6SIsLU= +github.com/aws/aws-sdk-go-v2 v1.16.16/go.mod h1:SwiyXi/1zTUZ6KIAmLK5V5ll8SiURNUYOqTerZPaF9k= +github.com/aws/aws-sdk-go-v2 v1.17.0/go.mod h1:SwiyXi/1zTUZ6KIAmLK5V5ll8SiURNUYOqTerZPaF9k= +github.com/aws/aws-sdk-go-v2 v1.17.1 h1:02c72fDJr87N8RAC2s3Qu0YuvMRZKNZJ9F+lAehCazk= +github.com/aws/aws-sdk-go-v2 v1.17.1/go.mod h1:JLnGeGONAyi2lWXI1p0PCIOIy333JMVK1U7Hf0aRFLw= +github.com/aws/aws-sdk-go-v2/config v1.17.4 h1:9HY1wbShqObySCHP2Z07blfrSWVX+nVxCZmUuLZKcG8= +github.com/aws/aws-sdk-go-v2/config v1.17.4/go.mod h1:ul+ru+huVpfduF9XRmGUq82T8T3K+nIFQuF6F+L+548= +github.com/aws/aws-sdk-go-v2/credentials v1.12.17 h1:htUjIJOQcvIUR0jC4eLkdis1DfaLL4EUbIKUFqh2WFA= +github.com/aws/aws-sdk-go-v2/credentials v1.12.17/go.mod h1:jd1mvJulXY7ccHvcSiJceYhv06yWIIRkJnwWEA4IX+g= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.14 h1:NZwZFtxXGOEIiCd8jWN55lexakug543CaO68bTpoLwg= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.14/go.mod h1:5CU57SyF5jZLSIw4OOll0PG83ThXwNdkRFOc0EltD/0= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.20/go.mod h1:gdZ5gRUaxThXIZyZQ8MTtgYBk2jbHgp05BO3GcD9Cwc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.23/go.mod h1:2DFxAQ9pfIRy0imBCJv+vZ2X6RKxves6fbnEuSry6b4= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.24/go.mod h1:ghMzB/j2wRbPx5/4jPYxJdOtCG2ggrtY01j8K7FMBDA= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.25 h1:nBO/RFxeq/IS5G9Of+ZrgucRciie2qpLy++3UGZ+q2E= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.25/go.mod h1:Zb29PYkf42vVYQY6pvSyJCJcFHlPIiY+YKdPtwnvMkY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.14/go.mod h1:GEV9jaDPIgayiU+uevxwozcvUOjc+P4aHE2BeSjm2vE= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.17/go.mod h1:pRwaTYCJemADaqCbUAxltMoHKata7hmB5PjEXeu0kfg= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.18/go.mod h1:fkQKYK/jUhCL/wNS1tOPrlYhr9vqutjCz4zZC1wBE1s= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.19 h1:oRHDrwCTVT8ZXi4sr9Ld+EXk7N/KGssOr2ygNeojEhw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.19/go.mod h1:6Q0546uHDp421okhmmGfbxzq2hBqbXFNpi4k+Q1JnQA= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.21 h1:lpwSbLKYTuABo6SyUoC25xAmfO3/TehGS2SmD1EtOL0= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.21/go.mod h1:Q0pktZjvRZk77TBto6yAvUAi7fcse1bdcMctBDVGgBw= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.63.0 h1:9ailn+011zwUJdS8RuamANJVAyX+aoUyTaBrw0CHRdE= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.63.0/go.mod h1:0+6fPoY0SglgzQUs2yml7X/fup12cMlVumJufh5npRQ= +github.com/aws/aws-sdk-go-v2/service/iam v1.18.16 h1:m/WtVqEvgwDiUPIW2dtnF2hDE1O62MEflz9ClOlCXAs= +github.com/aws/aws-sdk-go-v2/service/iam v1.18.16/go.mod h1:w8wndcRxwILFQAzwkUKyEDz4LDHEBSR78KRdaNjUKQA= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.14/go.mod h1:8qOLjqMzY/S1kh3myDXA1yxK5eD4uN8aOJgKpgvc4OM= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.17/go.mod h1:4nYOrY41Lrbk2170/BGkcJKBhws9Pfn8MG3aGqjjeFI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.18 h1:5oiCDEOHnYkk7uTVI8Wv6ftdFfb6YlUUNzkeePVIPjY= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.18/go.mod h1:QtCDHDOXunxeihz7iU15e09u9gRIeaa5WeE6FZVnGUo= +github.com/aws/aws-sdk-go-v2/service/sso v1.11.20 h1:3raP0UC9rvRyY4/cc4o4F3jTrNo94AYiarNUGNnq6dU= +github.com/aws/aws-sdk-go-v2/service/sso v1.11.20/go.mod h1:hPsROgDdgY/NQ1gPt7VJWG0GjSnalDC0DkkMfGEw2gc= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.2 h1:/SYpdjjAtraymql+/r719OgjxezdanAQiLb/NMxDb04= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.2/go.mod h1:5cxfDYtY2mDOlmesy4yycb6lwyy1U/iAUOHKhQLKw/E= +github.com/aws/aws-sdk-go-v2/service/sts v1.16.16/go.mod h1:Y9iBgT1w2vHtYzJEkwD6FqILjDSsvbxcW/+wIYxyse4= +github.com/aws/aws-sdk-go-v2/service/sts v1.17.0 h1:9S0HcZUxKcU3HdN+M6GgLIvdbg9as5aOoHrvwRsPNYU= +github.com/aws/aws-sdk-go-v2/service/sts v1.17.0/go.mod h1:9pZN58zQc5a4Dkdnhu/rI1lNBui1vP5B0giGCuUt2b0= +github.com/aws/smithy-go v1.13.1/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +github.com/aws/smithy-go v1.13.3/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +github.com/aws/smithy-go v1.13.4 h1:/RN2z1txIJWeXeOkzX+Hk/4Uuvv7dWtCjbmVJcrskyk= +github.com/aws/smithy-go v1.13.4/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/cactus/go-statsd-client/statsd v0.0.0-20200423205355-cb0885a1018c/go.mod h1:l/bIBLeOl9eX+wxJAzxS4TveKRtAqlyDpHjhkfO0MEI= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/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/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +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= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= +github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= +github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= +github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gofrs/uuid v4.3.0+incompatible h1:CaSVZxm5B+7o45rtab4jC2G37WGYX1zQfuU2i6DSvnc= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU= +github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= +github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +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.4.1/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.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/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.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +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/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.2.0 h1:y8Yozv7SZtlU//QXbezB6QkpuE6jMD2/gfzk4AftXjs= +github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.6.0 h1:SXk3ABtQYDT/OH8jAyvEOQ58mgawq5C4o/4/89qN2ZU= +github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.15.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.3.1 h1:vDwF1DFNZhntP4DAjuTpOw3uEgMUpXh1pB5fW9DqHpo= +github.com/hashicorp/go-hclog v1.3.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-plugin v1.4.0/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= +github.com/hashicorp/go-plugin v1.4.5 h1:oTE/oQR4eghggRg8VY7PAz3dr++VwDNBGCcOfIvHpBo= +github.com/hashicorp/go-plugin v1.4.5/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/hcl v1.0.1-0.20190430135223-99e2f22d1c94 h1:LaH4JWe6Q7ICdxL5raxQjSRw7Pj8uTtAENrjejIYZIg= +github.com/hashicorp/hcl v1.0.1-0.20190430135223-99e2f22d1c94/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= +github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= +github.com/jhump/protoreflect v1.9.0 h1:npqHz788dryJiR/l6K/RUQAyh2SwV91+d1dnh4RjO9w= +github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +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/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +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/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +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 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +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.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= +github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= +github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= +github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= +github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spiffe/go-spiffe/v2 v2.0.1-0.20220414143532-2ed460a8b9d3 h1:FpqM5PfWHs4Ze36HwzMpRefrv8kkmxFgtG9Qc6hL7Dc= +github.com/spiffe/go-spiffe/v2 v2.0.1-0.20220414143532-2ed460a8b9d3/go.mod h1:ifsAYiK9MOyuGYFUHUQ3K47dj+k/gd4IcWhlCyDJZEU= +github.com/spiffe/spire v1.2.1-0.20221027032717-3cbfb6277d23 h1:v2VC81hP02FfuoikP9kPGOkHBHMprHVXjnMklENVtyc= +github.com/spiffe/spire v1.2.1-0.20221027032717-3cbfb6277d23/go.mod h1:+Qol6MM32QOA5yFJ3Zi9s++SxK+QLHt0s/AtpCUJDV0= +github.com/spiffe/spire-api-sdk v1.2.5-0.20221020001527-5895a0279944 h1:yoKYON+goNlajhkpKSfwVPB1qvmeh9MmWDyj5zc4C7o= +github.com/spiffe/spire-api-sdk v1.2.5-0.20221020001527-5895a0279944/go.mod h1:4uuhFlN6KBWjACRP3xXwrOTNnvaLp1zJs8Lribtr4fI= +github.com/spiffe/spire-plugin-sdk v1.4.4 h1:1CwNRola+XsRVWfVGsHhAzqd++phKVqMmOz9qWVv7mE= +github.com/spiffe/spire-plugin-sdk v1.4.4/go.mod h1:4KW5J6abGIAyUS8IL7Fi0NOfoWR6jA5LufKPnIdm9FE= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +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.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/twmb/murmur3 v1.1.5/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/uber-go/tally/v4 v4.1.3 h1:dKhkrkVSSJK0AxZCv/MmK5JrWmH+MPG3dgZCbxWk2Yc= +github.com/uber-go/tally/v4 v4.1.3/go.mod h1:aXeSTDMl4tNosyf6rdU8jlgScHyjEGGtfJ/uwCIf/vM= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zeebo/errs v1.2.2/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= +github.com/zeebo/errs v1.3.0 h1:hmiaKqgYZzcVgRL1Vkc1Mn2914BbzB0IBxs+ebeutGs= +github.com/zeebo/errs v1.3.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= +go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +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-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +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.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +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-20181114220301-adae6a3d119a/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= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +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-20201110031124-69a78807bb2b/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-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b h1:tvrvnPFcdzp294diPnrdZZZ8XUt2Tyj7svb7X52iDuU= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/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-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/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-20210616045830-e2b7044e8c71/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-20210927094055-39ccf1dd6fa6/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-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/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-20220907062415-87db552b00fd h1:AZeIEzg+8RCELJYq8w+ODLVxFgLMMigSwO/ffKPEd9U= +golang.org/x/sys v0.0.0-20220907062415-87db552b00fd/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +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.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 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= +golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +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= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.101.0 h1:lJPPeEBIRxGpGLwnBTam1NPEM8Z2BmmXEd3z812pjwM= +google.golang.org/api v0.101.0/go.mod h1:CjxAAWWt3A3VrUE2IGDY2bgK5qhoG/OkyWVlYcP05MY= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20221018160656-63c7b68cfc55 h1:U1u4KB2kx6KR/aJDjQ97hZ15wQs8ZPvDcGcRynBhkvg= +google.golang.org/genproto v0.0.0-20221018160656-63c7b68cfc55/go.mod h1:45EK0dUbEZ2NHjCeAd2LXmyjAgGUGrpGROgjhC3ADck= +google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY= +google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc/examples v0.0.0-20201130180447-c456688b1860/go.mod h1:Ly7ZA/ARzg8fnPU9TyZIxoz33sEUuWX7txiqs8lPTgE= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +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.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +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 h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= +gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/validator.v2 v2.0.0-20200605151824-2b28d334fa05/go.mod h1:o4V0GXN9/CAmCsvJ0oXYZvrZOe7syiDZSN1GWGZTGzc= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +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.5/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= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.25.3 h1:Q1v5UFfYe87vi5H7NU0p4RXC26PPMT8KOpr1TLQbCMQ= +k8s.io/api v0.25.3/go.mod h1:o42gKscFrEVjHdQnyRenACrMtbuJsVdP+WVjqejfzmI= +k8s.io/apimachinery v0.25.3 h1:7o9ium4uyUOM76t6aunP0nZuex7gDf8VGwkR5RcJnQc= +k8s.io/apimachinery v0.25.3/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= +k8s.io/client-go v0.25.3 h1:oB4Dyl8d6UbfDHD8Bv8evKylzs3BXzzufLiO27xuPs0= +k8s.io/client-go v0.25.3/go.mod h1:t39LPczAIMwycjcXkVc+CB+PZV69jQuNx4um5ORDjQA= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ= +k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA= +k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= +k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4= +k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/hybrid/pkg/agent/agent.go b/hybrid/pkg/agent/agent.go new file mode 100644 index 0000000..1341e3b --- /dev/null +++ b/hybrid/pkg/agent/agent.go @@ -0,0 +1,158 @@ +package hybrid_agent + +import ( + "bytes" + "context" + "reflect" + "strings" + + hclog "github.com/hashicorp/go-hclog" + "github.com/hashicorp/hcl" + "github.com/hashicorp/hcl/hcl/printer" + + "github.com/hewlettpackard/hybrid/pkg/common" + agentstorev1 "github.com/spiffe/spire-plugin-sdk/proto/spire/hostservice/server/agentstore/v1" + nodeattestorv1 "github.com/spiffe/spire-plugin-sdk/proto/spire/plugin/agent/nodeattestor/v1" + configv1 "github.com/spiffe/spire-plugin-sdk/proto/spire/service/common/config/v1" + "github.com/spiffe/spire/pkg/agent/plugin/nodeattestor/awsiid" + "github.com/spiffe/spire/pkg/agent/plugin/nodeattestor/azuremsi" + "github.com/spiffe/spire/pkg/agent/plugin/nodeattestor/gcpiit" + "github.com/spiffe/spire/pkg/agent/plugin/nodeattestor/k8spsat" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type HybridPluginAgent struct { + pluginList []common.Types + agentstorev1.UnimplementedAgentStoreServer + nodeattestorv1.UnsafeNodeAttestorServer + configv1.UnsafeConfigServer + logger hclog.Logger + interceptor AgentInterceptorInterface + initStatus error +} + +func New() *HybridPluginAgent { + interceptor := new(HybridPluginAgentInterceptor) + return &HybridPluginAgent{interceptor: interceptor} +} + +func (p *HybridPluginAgent) SetLogger(logger hclog.Logger) { + p.logger = logger + +} +func (p *HybridPluginAgent) AidAttestation(stream nodeattestorv1.NodeAttestor_AidAttestationServer) error { + if len(p.pluginList) == 0 || p.initStatus != nil { + return status.Errorf(codes.InvalidArgument, "Plugin initialization error") + } + + p.interceptor.setCustomStream(stream) + p.interceptor.SetContext(stream.Context()) + p.interceptor.SetLogger(p.logger) + + for i := 0; i < len(p.pluginList); i++ { + elem := reflect.ValueOf(p.pluginList[i].Plugin) + result := elem.MethodByName("AidAttestation").Call([]reflect.Value{reflect.ValueOf(p.interceptor)}) + err := result[0].Interface() + if err != nil { + return status.Errorf(codes.Internal, "An error ocurred when during AidAttestation.") + } + } + + return p.interceptor.SendCombined() +} + +func (p *HybridPluginAgent) Configure(ctx context.Context, req *configv1.ConfigureRequest) (*configv1.ConfigureResponse, error) { + pluginData, _ := p.decodeStringAndTransformToAstNode(req.HclConfiguration) + + pluginNames, pluginsData := p.parseReceivedData(pluginData) + + p.pluginList, p.initStatus = p.initPlugins(pluginNames) + + if len(p.pluginList) == 0 || p.initStatus != nil { + return nil, p.initStatus + } + + for i := 0; i < len(p.pluginList); i++ { + elem := reflect.ValueOf(p.pluginList[i].Plugin) + req.HclConfiguration = pluginsData[p.pluginList[i].PluginName] + + result := elem.MethodByName("Configure").Call([]reflect.Value{reflect.ValueOf(ctx), reflect.ValueOf(req)}) + err := result[1] + + if !err.IsNil() { + return &configv1.ConfigureResponse{}, status.Errorf(codes.Internal, "Error configuring one of the supplied plugins.") + } + } + + return &configv1.ConfigureResponse{}, nil +} + +func (p *HybridPluginAgent) decodeStringAndTransformToAstNode(hclData string) (common.Generics, error) { + var genericData common.GenericPluginSuper + if err := hcl.Decode(&genericData, hclData); err != nil { + return nil, err + } + + var data bytes.Buffer + printer.DefaultConfig.Fprint(&data, genericData.Plugins) + + var astNodeData common.Generics + + if err := hcl.Decode(&astNodeData, data.String()); err != nil { + return nil, err + } + + return astNodeData, nil +} + +func (p *HybridPluginAgent) parseReceivedData(data common.Generics) ([]string, map[string]string) { + str := map[string]string{} + plugins := []string{} + for key := range data { + var data_ bytes.Buffer + printer.DefaultConfig.Fprint(&data_, data[key]) + str[key] = strings.Replace(strings.Replace(data_.String(), "{", "", -1), "}", "", -1) + plugins = append(plugins, key) + } + return plugins, str +} + +func (p *HybridPluginAgent) initPlugins(pluginList []string) ([]common.Types, error) { + attestors := make([]common.Types, 0) + + for i := 0; i < len(pluginList); i++ { + var plugin common.Types + switch pluginList[i] { + case "aws_iid": + plugin.PluginName = "aws_iid" + plugin.Plugin = awsiid.New() + case "k8s_psat": + plugin.PluginName = "k8s_psat" + plugin.Plugin = k8spsat.New() + case "azure_msi": + plugin.PluginName = "azure_msi" + plugin.Plugin = azuremsi.New() + case "gcp_iit": + plugin.PluginName = "gcp_iit" + plugin.Plugin = gcpiit.New() + default: + plugin.PluginName = "" + plugin.Plugin = nil + } + + attestors = append(attestors, plugin) + } + + for i := 0; i < len(attestors); i++ { + if attestors[i].Plugin == nil { + return nil, status.Error(codes.FailedPrecondition, "Some of the supplied plugins are not supported or are invalid") + } + } + + if len(attestors) == 0 { + return nil, status.Error(codes.FailedPrecondition, "No plugins supplied") + } + + return attestors, nil +} diff --git a/hybrid/pkg/agent/agent_interceptor.go b/hybrid/pkg/agent/agent_interceptor.go new file mode 100644 index 0000000..4d2babc --- /dev/null +++ b/hybrid/pkg/agent/agent_interceptor.go @@ -0,0 +1,97 @@ +package hybrid_agent + +import ( + "context" + "encoding/json" + + hclog "github.com/hashicorp/go-hclog" + nodeattestorv1 "github.com/spiffe/spire-plugin-sdk/proto/spire/plugin/agent/nodeattestor/v1" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type AgentInterceptorInterface interface { + Recv() (*nodeattestorv1.Challenge, error) + Send(challenge *nodeattestorv1.PayloadOrChallengeResponse) error + setCustomStream(stream nodeattestorv1.NodeAttestor_AidAttestationServer) + SetContext(ctx context.Context) + Context() context.Context + SetLogger(logger hclog.Logger) + SendCombined() error +} + +type HybridPluginAgentInterceptor struct { + ctx context.Context + stream nodeattestorv1.NodeAttestor_AidAttestationServer + nodeattestorv1.NodeAttestor_AidAttestationServer + logger hclog.Logger + payload [][]byte +} + +func (m *HybridPluginAgentInterceptor) setCustomStream(stream nodeattestorv1.NodeAttestor_AidAttestationServer) { + m.stream = stream +} + +func (m *HybridPluginAgentInterceptor) Recv() (*nodeattestorv1.Challenge, error) { + return m.stream.Recv() +} + +func (m *HybridPluginAgentInterceptor) Send(challenge *nodeattestorv1.PayloadOrChallengeResponse) error { + payload := challenge.GetPayload() + m.payload = append(m.payload, payload) + + return nil +} + +func (m *HybridPluginAgentInterceptor) SetContext(ctx context.Context) { + m.ctx = ctx +} + +func (m *HybridPluginAgentInterceptor) Context() context.Context { + return m.ctx +} + +func (m *HybridPluginAgentInterceptor) SetLogger(logger hclog.Logger) { + m.logger = logger +} + +func (m *HybridPluginAgentInterceptor) SendCombined() error { + data, _ := m.unmarshalPayloadData(m.payload) + + combinedPayloads, _ := m.combineAndMarshalUnmarshaledPayloads(data) + + payload := &nodeattestorv1.PayloadOrChallengeResponse{ + Data: &nodeattestorv1.PayloadOrChallengeResponse_Payload{ + Payload: []byte(combinedPayloads), + }, + } + + return m.stream.Send(payload) +} + +func (m *HybridPluginAgentInterceptor) combineAndMarshalUnmarshaledPayloads(data []map[string]interface{}) ([]byte, error) { + jsonData := data[0] + for i := 1; i < len(data); i++ { + for k, v := range data[i] { + jsonData[k] = v + } + } + + return json.Marshal(jsonData) +} + +func (m *HybridPluginAgentInterceptor) unmarshalPayloadData(payloadData [][]byte) ([]map[string]interface{}, error) { + var data []map[string]interface{} + + var jsonData map[string]interface{} + + for i := 0; i < len(payloadData); i++ { + jsonData = nil + if err := json.Unmarshal(payloadData[i], &jsonData); err != nil { + return nil, status.Errorf(codes.InvalidArgument, "Failed to unmarshal data payload: %v", err) + } + data = append(data, jsonData) + } + + return data, nil +} diff --git a/hybrid/pkg/agent/agent_test.go b/hybrid/pkg/agent/agent_test.go new file mode 100644 index 0000000..7b61bd5 --- /dev/null +++ b/hybrid/pkg/agent/agent_test.go @@ -0,0 +1,376 @@ +package hybrid_agent + +import ( + "bytes" + "context" + "errors" + "fmt" + "testing" + + hclog "github.com/hashicorp/go-hclog" + "github.com/hewlettpackard/hybrid/pkg/common" + "github.com/spiffe/go-spiffe/v2/spiffeid" + nodeattestorv1 "github.com/spiffe/spire-plugin-sdk/proto/spire/plugin/agent/nodeattestor/v1" + configv1 "github.com/spiffe/spire-plugin-sdk/proto/spire/service/common/config/v1" + "github.com/spiffe/spire/pkg/agent/plugin/nodeattestor/awsiid" + "github.com/spiffe/spire/pkg/agent/plugin/nodeattestor/azuremsi" + "github.com/spiffe/spire/pkg/agent/plugin/nodeattestor/gcpiit" + "github.com/spiffe/spire/pkg/agent/plugin/nodeattestor/k8spsat" + "github.com/spiffe/spire/pkg/common/catalog" + "github.com/spiffe/spire/test/plugintest" + grpc "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/stretchr/testify/require" +) + +func BuiltIn() catalog.BuiltIn { + return builtin(&HybridPluginAgent{}) +} + +func builtin(p *HybridPluginAgent) catalog.BuiltIn { + return catalog.MakeBuiltIn("hybrid-node-attestor", + nodeattestorv1.NodeAttestorPluginServer(p), + configv1.ConfigServiceServer(p), + ) +} + +var pluginsString = `plugins { + k8s_psat { + cluster = "hybrid-node-attestor" + } + aws_iid { + accountId = 728109058939 + } + }` + +var pluginsStringInvalidData = `plugins { + k8s_psat { + } + aws_iida { + } + }` + +var pluginsStringErrorData = `plugins { +k8s_psat { +} +aws_iid { + accountId { + error=true + } +} +}` + +var pluginsStringEmptyData = `plugins {}` +var payloadOneData = `{"cluster":"hybrid-node-attestor_fake","token":"part1.part2.part3-part4-part5--part6-part7"}` +var payloadTwoData = `{"document":"{\n \"accountId\" : \"123456789_TEST\",\n \"architecture\" : \"x86_64\",\n \"availabilityZone\" : \"us-east-2a\",\n \"billingProducts\" : null,\n \"devpayProductCodes\" : null,\n \"marketplaceProductCodes\" : null,\n \"imageId\" : \"ami-010203040506\",\n \"instanceId\" : \"i-010203040506\",\n \"instanceType\" : \"m5.large\",\n \"kernelId\" : null,\n \"pendingTime\" : \"2022-09-22T03:22:21Z\",\n \"privateIp\" : \"192.168.77.116\",\n \"ramdiskId\" : null,\n \"region\" : \"us-east-2\",\n \"version\" : \"2017-09-30\"\n}","signature":"eO4+90PuN8bZaIJjpBe1/mAzPhvSrrhLATwPFaOPzK5ZSUpsbVOuK2tXjMYkx+ora7mcaL0G45li\nbZLGUIee+DF/YZ8/5RuNf1Z8yn+5e2AqLvNhIsF5IOVZWk8yDvl/jBJCcW8GaRblldWdMoDiC2OA\nqVyRjyJCXUySNu0JADE="}` +var payloadThreeData = `{1,2,3,4,}` + +// ------------------------------------------------------------------------------------------------------------------------ + +func TestNew(t *testing.T) { + hybridPlugin := New() + require.NotNil(t, hybridPlugin, "New should return a non-nil value") + require.IsType(t, &HybridPluginAgent{}, hybridPlugin, "New should return a HybridPluginAgent") +} + +func TestMethodsThatParseHclConfig(t *testing.T) { + interceptor := new(InterceptorWrapper) + plugin := HybridPluginAgent{interceptor: interceptor} + + pluginAstNode, err := plugin.decodeStringAndTransformToAstNode(pluginsString) + + require.NoError(t, err, "Error decoding test string") + require.Len(t, pluginAstNode, 2, "Could not transform HCL string configuration: %w", err) + require.Contains(t, pluginAstNode, "k8s_psat", "Could not access k8s_psat plugin by index on ast node") + require.Contains(t, pluginAstNode, "aws_iid", "Could not access aws_iid plugin by index on ast node") + + pluginNames, pluginsData := plugin.parseReceivedData(pluginAstNode) + + require.Len(t, pluginNames, 2, "Could not parse plugin names") + require.Contains(t, pluginNames, "k8s_psat", "Could not access k8s_psat plugin by index after parsing") + require.Contains(t, pluginNames, "aws_iid", "Could not access aws_iid plugin by index after parsing") + + require.Len(t, pluginsData, 2, "Could not parse plugin data") + require.Contains(t, pluginsData, "k8s_psat", "Could not access k8s_psat plugin by index after parsing") + require.Equal(t, "\n cluster = \"hybrid-node-attestor\"\n", pluginsData["k8s_psat"], "k8s_psat plugin data was not extracted properly") + require.Contains(t, pluginsData, "aws_iid", "Could not access aws_iid plugin by index after parsing") + require.Equal(t, "\n accountId = 728109058939\n", pluginsData["aws_iid"], "aws_iid plugin data was not extracted properly") +} + +func TestSupportedPluginsInitialization(t *testing.T) { + interceptor := new(InterceptorWrapper) + plugin := HybridPluginAgent{interceptor: interceptor} + + plugins, err := plugin.initPlugins([]string{"aws_iid", "k8s_psat", "azure_msi", "gcp_iit"}) + + require.NoError(t, err, "Error initializing supported plugins: %w", err) + require.IsType(t, &awsiid.IIDAttestorPlugin{}, plugins[0].Plugin, "Could not initialize aws_iid plugin") + require.IsType(t, &k8spsat.AttestorPlugin{}, plugins[1].Plugin, "Could not initialize k8s_psat plugin") + require.IsType(t, &azuremsi.MSIAttestorPlugin{}, plugins[2].Plugin, "Could not initialize azure_msi plugin") + require.IsType(t, &gcpiit.IITAttestorPlugin{}, plugins[3].Plugin, "Could not initialize gcp_iit plugin") + + plugins, err = plugin.initPlugins([]string{"aws_iid_test", "k8s_psat_test"}) + require.Error(t, err, "Error initializing supported plugins: %w", err) + require.Len(t, plugins, 0, "Plugin list length should be 0 on unknown plugin names") + +} + +func TestHybridPluginConfiguration(t *testing.T) { + var errConfig error + coreConfig := catalog.CoreConfig{ + TrustDomain: spiffeid.RequireTrustDomainFromString("example.org"), + } + + interceptor := new(InterceptorWrapper) + plugin := HybridPluginAgent{interceptor: interceptor} + + plugintest.Load(t, builtin(&plugin), nil, + plugintest.CaptureConfigureError(&errConfig), + plugintest.CoreConfig(coreConfig), + plugintest.Configure(pluginsString), + ) + require.NoError(t, errConfig, "Error configuring plugin: %w", errConfig) + require.Len(t, plugin.pluginList, 2, "Plugins used by Hybrid node attestor failed to start.") + + interceptor = new(InterceptorWrapper) + plugin = HybridPluginAgent{interceptor: interceptor} + + plugintest.Load(t, builtin(&plugin), nil, + plugintest.CaptureConfigureError(&errConfig), + plugintest.CoreConfig(coreConfig), + plugintest.Configure(pluginsStringInvalidData), + ) + require.EqualError(t, errConfig, "rpc error: code = FailedPrecondition desc = Some of the supplied plugins are not supported or are invalid", "Error configuring plugin: %w", errConfig) + require.Len(t, plugin.pluginList, 0, "All plugins used by Hybrid node attestor should fail on config with unsupported plugins.") + + interceptor = new(InterceptorWrapper) + plugin = HybridPluginAgent{interceptor: interceptor} + + plugintest.Load(t, builtin(&plugin), nil, + plugintest.CaptureConfigureError(&errConfig), + plugintest.CoreConfig(coreConfig), + plugintest.Configure(pluginsStringEmptyData), + ) + + require.EqualError(t, errConfig, "rpc error: code = FailedPrecondition desc = No plugins supplied", "Error configuring plugin: %w", errConfig) + require.Len(t, plugin.pluginList, 0, "Hybrid node attestor should load no plugins on empty config.") + + interceptor = new(InterceptorWrapper) + plugin = HybridPluginAgent{interceptor: interceptor} + + plugintest.Load(t, builtin(&plugin), nil, + plugintest.CaptureConfigureError(&errConfig), + plugintest.CoreConfig(coreConfig), + plugintest.Configure(pluginsStringErrorData), + ) + require.EqualError(t, errConfig, "rpc error: code = Internal desc = Error configuring one of the supplied plugins.", "Error configuring plugin: %w", errConfig) +} + +func TestHybridPluginAgentInterceptorAndAidAttestation(t *testing.T) { + combinedPayloads := []byte("") + stream := StreamMock{CombinedPayloads: &combinedPayloads} + interceptor := new(HybridPluginAgentInterceptor) + + interceptor.setCustomStream(&stream) + customStream, _ := interceptor.Recv() + require.Equal(t, []byte("customStream"), customStream.Challenge, "Could not set custom stream on interceptor") + + interceptor.SetContext(context.WithValue(context.Background(), "testkey", "testval")) + customContext := interceptor.Context() + require.Equal(t, "testval", customContext.Value("testkey"), "Could not set interceptor context") + + interceptor.SetLogger(hclog.Default().Named("test_logger")) + require.Equal(t, "test_logger", interceptor.logger.Name(), "Could not set interceptor logger") + + payloadEmpty := interceptor.payload + + payloadOne := nodeattestorv1.PayloadOrChallengeResponse{ + Data: &nodeattestorv1.PayloadOrChallengeResponse_Payload{ + Payload: []byte(payloadOneData), + }, + } + interceptor.Send(&payloadOne) + + payloadTwo := nodeattestorv1.PayloadOrChallengeResponse{ + Data: &nodeattestorv1.PayloadOrChallengeResponse_Payload{ + Payload: []byte(payloadTwoData), + }, + } + interceptor.Send(&payloadTwo) + + if payloadEmpty == nil { + if len(interceptor.payload) > 0 && bytes.Compare(interceptor.payload[0], payloadOne.GetPayload()) != 0 { + t.Error("Could not intercept Payload message") + } + } + + var combinedByteArray [][]byte + combinedByteArray = append(combinedByteArray, []byte(payloadOneData)) + combinedByteArray = append(combinedByteArray, []byte(payloadTwoData)) + unmarshaledPayload, err_ := interceptor.unmarshalPayloadData(combinedByteArray) + + typeOf := new([]map[string]interface{}) + require.IsType(t, typeOf, &unmarshaledPayload, "Could not unmarshal payload data") + + combined, _ := interceptor.combineAndMarshalUnmarshaledPayloads(unmarshaledPayload) + + require.IsType(t, []byte{}, combined, "Could not marshal combined payload data") + + stream.CombinedPayloads = &combined + err := interceptor.SendCombined() + require.NoError(t, err, "Error sending combined payload data") + + combinedByteArray = append(combinedByteArray, []byte(payloadThreeData)) + unmarshaledPayload, err_ = interceptor.unmarshalPayloadData(combinedByteArray) + expectedError := status.Error(codes.InvalidArgument, "Failed to unmarshal data payload: invalid character '1' looking for beginning of object key string") + require.EqualError(t, err_, expectedError.Error(), "Error unmarshaling payload data") + + pluginOne := new(FakePlugin) + pluginTwo := new(FakePlugin) + pluginList := []common.Types{ + common.Types{PluginName: "k8s_psat", Plugin: pluginOne}, + common.Types{PluginName: "aws_iid", Plugin: pluginTwo}, + } + interceptorFake := new(InterceptorWrapper) + hybridPlugin := HybridPluginAgent{pluginList: pluginList, logger: hclog.Default(), interceptor: interceptorFake} + + aidAttestation := hybridPlugin.AidAttestation(stream) + require.NoError(t, aidAttestation, "AidAttestation of hybrid plugin fails") + interceptorFake.SetReturnError(true) + + aidAttestation = hybridPlugin.AidAttestation(stream) + require.Error(t, aidAttestation, "AidAttestation of hybrid plugin fails") + + hybridPlugin.SetLogger(hclog.Default().Named("test_logger2")) + require.Equal(t, "test_logger2", hybridPlugin.logger.Name(), "Could not set hybrid plugin logger") + + expectedError = status.Error(codes.InvalidArgument, "Plugin initialization error") + hybridPlugin.initStatus = expectedError + aidAttestation = hybridPlugin.AidAttestation(stream) + require.EqualError(t, aidAttestation, expectedError.Error(), "AidAttestation of hybrid plugin fails") + + pluginOne = new(FakePlugin) + pluginTwo = new(FakePlugin) + pluginTwo.returnError = true + pluginList = []common.Types{ + common.Types{PluginName: "k8s_psat", Plugin: pluginOne}, + common.Types{PluginName: "aws_iid", Plugin: pluginTwo}, + } + interceptorFake = new(InterceptorWrapper) + hybridPlugin = HybridPluginAgent{pluginList: pluginList, logger: hclog.Default(), interceptor: interceptorFake} + + aidAttestation = hybridPlugin.AidAttestation(stream) + require.EqualError(t, aidAttestation, "rpc error: code = Internal desc = An error ocurred when during AidAttestation.", "Error calling plugin: %w", aidAttestation) +} + +// ------------------------------------------------------------------------------------------------------------------------ + +type FakePlugin struct { + returnError bool +} + +func (f *FakePlugin) SetReturnError(state bool) { + f.returnError = state +} + +func (f *FakePlugin) AidAttestation(stream nodeattestorv1.NodeAttestor_AidAttestationServer) error { + if f.returnError { + return status.Error(codes.InvalidArgument, "AidAttestation error") + } + return nil +} + +func (f *FakePlugin) Configure(ctx context.Context, req *configv1.ConfigureRequest) (*configv1.ConfigureResponse, error) { + fmt.Println("configure fake ", f.returnError) + if f.returnError { + return nil, status.Errorf(codes.Internal, "Error configuring one of the supplied plugins.") + } + + return &configv1.ConfigureResponse{}, nil +} + +// ------------------------------------------------------------------------------------------------------------------------ + +type StreamMock struct { + grpc.ServerStream + CombinedPayloads *[]byte +} + +func (s StreamMock) Recv() (*nodeattestorv1.Challenge, error) { + challenge := nodeattestorv1.Challenge{Challenge: []byte("customStream")} + return &challenge, nil +} + +func (s StreamMock) Send(challenge *nodeattestorv1.PayloadOrChallengeResponse) error { + if bytes.Compare(*s.CombinedPayloads, challenge.GetPayload()) != 0 { + return errors.New("Could not send intercepted payloads") + } + return nil +} + +func (s StreamMock) Context() context.Context { + return context.Background() +} + +// ------------------------------------------------------------------------------------------------------------------------ + +type PluginWrapper struct { + nodeattestorv1.NodeAttestor_AidAttestationServer +} + +func (pw *PluginWrapper) Context() context.Context { + return context.Background() +} + +// ------------------------------------------------------------------------------------------------------------------------ + +type InterceptorWrapper struct { + returnError bool + nodeattestorv1.NodeAttestor_AidAttestationServer +} + +func (iw *InterceptorWrapper) SetReturnError(state bool) { + iw.returnError = state +} + +func (iw *InterceptorWrapper) Recv() (*nodeattestorv1.Challenge, error) { + return nil, nil +} + +func (iw *InterceptorWrapper) Send(challenge *nodeattestorv1.PayloadOrChallengeResponse) error { + return nil +} + +func (iw *InterceptorWrapper) setCustomStream(stream nodeattestorv1.NodeAttestor_AidAttestationServer) { + +} + +func (iw *InterceptorWrapper) SetContext(ctx context.Context) { + +} + +func (iw *InterceptorWrapper) Context() context.Context { + return nil +} + +func (iw *InterceptorWrapper) SetLogger(logger hclog.Logger) { + +} + +func (iw *InterceptorWrapper) SendCombined() error { + if iw.returnError { + return status.Error(codes.Internal, "Test Error") + } + + return nil +} + +func (iw *InterceptorWrapper) combineAndMarshalUnmarshaledPayloads(data []map[string]interface{}) ([]byte, error) { + return nil, nil +} + +func (iw *InterceptorWrapper) unmarshalPayloadData(payloadData [][]byte) ([]map[string]interface{}, error) { + return nil, nil +} diff --git a/hybrid/pkg/common/structs.go b/hybrid/pkg/common/structs.go new file mode 100644 index 0000000..dd5b260 --- /dev/null +++ b/hybrid/pkg/common/structs.go @@ -0,0 +1,17 @@ +package common + +import ( + "github.com/hashicorp/hcl/hcl/ast" +) + +type Types struct { + PluginName string + Plugin interface{} +} + +type Generics map[string]ast.Node + +type GenericPluginSuper struct { + Plugins ast.Node `hcl:"plugins"` + CustomSpiffeId []string `hcl:"custom_spiffe_id"` +} diff --git a/hybrid/pkg/server/server.go b/hybrid/pkg/server/server.go new file mode 100644 index 0000000..9686cbc --- /dev/null +++ b/hybrid/pkg/server/server.go @@ -0,0 +1,243 @@ +package hybrid_server + +import ( + "bytes" + "context" + "errors" + "k8s.io/utils/strings/slices" + "reflect" + "strings" + + hclog "github.com/hashicorp/go-hclog" + "github.com/hashicorp/hcl" + "github.com/hashicorp/hcl/hcl/printer" + "github.com/spiffe/spire-plugin-sdk/pluginsdk" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/hewlettpackard/hybrid/pkg/common" + agentstorev1 "github.com/spiffe/spire-plugin-sdk/proto/spire/hostservice/server/agentstore/v1" + nodeattestorv1 "github.com/spiffe/spire-plugin-sdk/proto/spire/plugin/server/nodeattestor/v1" + configv1 "github.com/spiffe/spire-plugin-sdk/proto/spire/service/common/config/v1" + "github.com/spiffe/spire/pkg/server/hostservice/agentstore" + "github.com/spiffe/spire/pkg/server/plugin/nodeattestor/awsiid" + "github.com/spiffe/spire/pkg/server/plugin/nodeattestor/azuremsi" + nodeattestorbase "github.com/spiffe/spire/pkg/server/plugin/nodeattestor/base" + "github.com/spiffe/spire/pkg/server/plugin/nodeattestor/gcpiit" + "github.com/spiffe/spire/pkg/server/plugin/nodeattestor/k8spsat" +) + +var ( + _ pluginsdk.NeedsHostServices = (*HybridPluginServer)(nil) +) + +type HybridPluginServer struct { + pluginList []common.Types + pluginNames []string + nodeattestorbase.Base + agentstorev1.UnimplementedAgentStoreServer + nodeattestorv1.UnimplementedNodeAttestorServer + configv1.UnsafeConfigServer + + logger hclog.Logger + store agentstorev1.AgentStoreServiceClient + broker pluginsdk.ServiceBroker + interceptor ServerInterceptorInterface + initStatus error + usedSpiffeIds []string +} + +func New() *HybridPluginServer { + interceptor := new(HybridPluginServerInterceptor) + return &HybridPluginServer{interceptor: interceptor} +} + +func (p *HybridPluginServer) SetLogger(logger hclog.Logger) { + p.logger = logger +} + +func (p *HybridPluginServer) BrokerHostServices(broker pluginsdk.ServiceBroker) error { + p.broker = broker + + return nil +} + +func (p *HybridPluginServer) setBrokerHostServices() error { + if !p.broker.BrokerClient(&p.store) { + return errors.New("Agent store service required") + } + + for i := 0; i < len(p.pluginList); i++ { + elem := reflect.ValueOf(p.pluginList[i].Plugin) + method := elem.MethodByName("BrokerHostServices") + if method.IsValid() { + err := elem.MethodByName("BrokerHostServices").Call([]reflect.Value{reflect.ValueOf(p.broker)}) + p.logger.Debug(err[0].String()) + } + } + + return nil +} + +func (p *HybridPluginServer) Attest(stream nodeattestorv1.NodeAttestor_AttestServer) error { + req, err := stream.Recv() + if err != nil { + return err + } + + p.interceptor.ResetInterceptor() + p.interceptor.setCustomStream(stream) + p.interceptor.SetContext(stream.Context()) + p.interceptor.SetLogger(p.logger) + p.interceptor.SetReq(req) + + for i := 0; i < len(p.pluginList); i++ { + p.interceptor.SetMultipleSpiffeIds(slices.Contains(p.usedSpiffeIds, p.pluginNames[i])) + elem := reflect.ValueOf(p.pluginList[i].Plugin) + elem.MethodByName("SetLogger").Call([]reflect.Value{reflect.ValueOf(p.logger)}) + result := elem.MethodByName("Attest").Call([]reflect.Value{reflect.ValueOf(p.interceptor)}) + if result[0].Interface() != nil { + callError, _ := status.FromError(result[0].Interface().(error)) + return status.Errorf(codes.Internal, callError.Message()) + } + } + + return p.SendResponse() +} + +func (p *HybridPluginServer) SendResponse() error { + canReattest := true + for _, n := range p.interceptor.CanReattest() { + if !n { + attested, err := agentstore.IsAttested(context.Background(), p.store, p.interceptor.SpiffeID()) + canReattest = false + switch { + case err != nil: + return err + case attested: + return status.Error(codes.PermissionDenied, "attestation data has already been used to attest an agent") + default: + } + } + } + + return p.interceptor.Stream().Send(&nodeattestorv1.AttestResponse{ + Response: &nodeattestorv1.AttestResponse_AgentAttributes{ + AgentAttributes: &nodeattestorv1.AgentAttributes{ + CanReattest: canReattest, + SpiffeId: p.interceptor.SpiffeID(), + SelectorValues: p.interceptor.CombinedSelectors(), + }, + }, + }) +} + +func (p *HybridPluginServer) Configure(ctx context.Context, req *configv1.ConfigureRequest) (*configv1.ConfigureResponse, error) { + pluginData, _ := p.decodeStringAndTransformToAstNode(req.HclConfiguration) + + pluginNames, pluginsData := p.parseReceivedData(pluginData) + + p.pluginNames = pluginNames + + p.pluginList, p.initStatus = p.initPlugins(pluginNames) + + if len(p.pluginList) == 0 || p.initStatus != nil { + return nil, p.initStatus + } + + for i := 0; i < len(p.pluginList); i++ { + elem := reflect.ValueOf(p.pluginList[i].Plugin) + req.HclConfiguration = pluginsData[p.pluginList[i].PluginName] + + result := elem.MethodByName("Configure").Call([]reflect.Value{reflect.ValueOf(ctx), reflect.ValueOf(req)}) + err := result[1] + + if !err.IsNil() { + return &configv1.ConfigureResponse{}, status.Errorf(codes.Internal, "Error configuring one of the supplied plugins(%s): %s", p.pluginList[i].PluginName, err.Interface().(error)) + } + } + + p.setBrokerHostServices() + + return &configv1.ConfigureResponse{}, nil +} + +func (p *HybridPluginServer) decodeStringAndTransformToAstNode(hclData string) (common.Generics, error) { + var genericData common.GenericPluginSuper + if err := hcl.Decode(&genericData, hclData); err != nil { + } + + var data bytes.Buffer + printer.DefaultConfig.Fprint(&data, genericData.Plugins) + + var astNodeData common.Generics + + p.usedSpiffeIds = genericData.CustomSpiffeId + + if err := hcl.Decode(&astNodeData, data.String()); err != nil { + } + + return astNodeData, nil +} + +func (p *HybridPluginServer) parseReceivedData(data common.Generics) ([]string, map[string]string) { + + str := map[string]string{} + plugins := []string{} + for key := range data { + var data_ bytes.Buffer + printer.DefaultConfig.Fprint(&data_, data[key]) + result := strings.Replace(data_.String(), "{", "", 1) + result = reverse(strings.Replace(reverse(result), "}", reverse(""), 1)) + str[key] = result + plugins = append(plugins, key) + } + + return plugins, str +} + +func reverse(s string) (result string) { + for _, v := range s { + result = string(v) + result + } + return +} + +func (p *HybridPluginServer) initPlugins(pluginList []string) ([]common.Types, error) { + attestors := make([]common.Types, 0) + + for i := 0; i < len(pluginList); i++ { + var plugin common.Types + switch pluginList[i] { + case "aws_iid": + plugin.PluginName = "aws_iid" + plugin.Plugin = awsiid.New() + case "k8s_psat": + plugin.PluginName = "k8s_psat" + plugin.Plugin = k8spsat.New() + case "azure_msi": + plugin.PluginName = "azure_msi" + plugin.Plugin = azuremsi.New() + case "gcp_iit": + plugin.PluginName = "gcp_iit" + plugin.Plugin = gcpiit.New() + default: + plugin.PluginName = "" + plugin.Plugin = nil + } + + attestors = append(attestors, plugin) + } + + for i := 0; i < len(attestors); i++ { + if attestors[i].Plugin == nil { + return nil, status.Error(codes.FailedPrecondition, "Some of the supplied plugins are not supported or are invalid") + } + } + + if len(attestors) == 0 { + return nil, status.Error(codes.FailedPrecondition, "No plugins supplied") + } + + return attestors, nil +} diff --git a/hybrid/pkg/server/server_interceptor.go b/hybrid/pkg/server/server_interceptor.go new file mode 100644 index 0000000..168ab5d --- /dev/null +++ b/hybrid/pkg/server/server_interceptor.go @@ -0,0 +1,110 @@ +package hybrid_server + +import ( + "context" + + hclog "github.com/hashicorp/go-hclog" + nodeattestorv1 "github.com/spiffe/spire-plugin-sdk/proto/spire/plugin/server/nodeattestor/v1" +) + +type ServerInterceptorInterface interface { + Recv() (*nodeattestorv1.AttestRequest, error) + Send(resp *nodeattestorv1.AttestResponse) error + setCustomStream(stream nodeattestorv1.NodeAttestor_AttestServer) + SetContext(ctx context.Context) + Context() context.Context + SetLogger(logger hclog.Logger) + SetReq(req *nodeattestorv1.AttestRequest) + CanReattest() []bool + SpiffeID() string + CombinedSelectors() []string + Stream() nodeattestorv1.NodeAttestor_AttestServer + ResetInterceptor() + SetMultipleSpiffeIds(state bool) +} + +type HybridPluginServerInterceptor struct { + ctx context.Context + stream nodeattestorv1.NodeAttestor_AttestServer + nodeattestorv1.NodeAttestor_AttestServer + logger hclog.Logger + req *nodeattestorv1.AttestRequest + Response *nodeattestorv1.AttestResponse + combinedSelectors []string + spiffeID string + canReattest []bool + multiplesSpiffeIds bool +} + +func (m *HybridPluginServerInterceptor) ResetInterceptor() { + m.ctx = nil + m.stream = nil + m.logger = nil + m.req = nil + m.Response = nil + m.combinedSelectors = nil + m.spiffeID = "" + m.canReattest = nil +} + +func (m *HybridPluginServerInterceptor) Recv() (*nodeattestorv1.AttestRequest, error) { + return m.req, nil // add error here +} + +func (m *HybridPluginServerInterceptor) setCustomStream(stream nodeattestorv1.NodeAttestor_AttestServer) { + m.stream = stream +} + +func (m *HybridPluginServerInterceptor) Send(resp *nodeattestorv1.AttestResponse) error { + switch x := resp.Response.(type) { + case *nodeattestorv1.AttestResponse_AgentAttributes: + m.combinedSelectors = append(m.combinedSelectors, x.AgentAttributes.SelectorValues...) + + if len(m.spiffeID) == 0 { + m.spiffeID = x.AgentAttributes.SpiffeId + } else if m.multiplesSpiffeIds { + m.spiffeID = m.spiffeID + "/" + x.AgentAttributes.SpiffeId + } + + m.canReattest = append(m.canReattest, x.AgentAttributes.CanReattest) + default: + } + + return nil +} + +func (m *HybridPluginServerInterceptor) SetContext(ctx context.Context) { + m.ctx = ctx +} + +func (m *HybridPluginServerInterceptor) Context() context.Context { + return m.ctx +} + +func (m *HybridPluginServerInterceptor) SetLogger(logger hclog.Logger) { + m.logger = logger +} + +func (m *HybridPluginServerInterceptor) SetReq(req *nodeattestorv1.AttestRequest) { + m.req = req +} + +func (m *HybridPluginServerInterceptor) CanReattest() []bool { + return m.canReattest +} + +func (m *HybridPluginServerInterceptor) SpiffeID() string { + return m.spiffeID +} + +func (m *HybridPluginServerInterceptor) CombinedSelectors() []string { + return m.combinedSelectors +} + +func (m *HybridPluginServerInterceptor) Stream() nodeattestorv1.NodeAttestor_AttestServer { + return m.stream +} + +func (m *HybridPluginServerInterceptor) SetMultipleSpiffeIds(state bool) { + m.multiplesSpiffeIds = state +} diff --git a/hybrid/pkg/server/server_test.go b/hybrid/pkg/server/server_test.go new file mode 100644 index 0000000..3397e6f --- /dev/null +++ b/hybrid/pkg/server/server_test.go @@ -0,0 +1,521 @@ +package hybrid_server + +import ( + "context" + "errors" + "fmt" + "testing" + + hclog "github.com/hashicorp/go-hclog" + "github.com/hewlettpackard/hybrid/pkg/common" + "github.com/spiffe/go-spiffe/v2/spiffeid" + agentstorev1 "github.com/spiffe/spire-plugin-sdk/proto/spire/hostservice/server/agentstore/v1" + nodeattestorv1 "github.com/spiffe/spire-plugin-sdk/proto/spire/plugin/server/nodeattestor/v1" + configv1 "github.com/spiffe/spire-plugin-sdk/proto/spire/service/common/config/v1" + "github.com/spiffe/spire/pkg/common/catalog" + "github.com/spiffe/spire/pkg/server/plugin/nodeattestor/awsiid" + "github.com/spiffe/spire/pkg/server/plugin/nodeattestor/azuremsi" + "github.com/spiffe/spire/pkg/server/plugin/nodeattestor/gcpiit" + "github.com/spiffe/spire/pkg/server/plugin/nodeattestor/k8spsat" + + "github.com/spiffe/spire/test/fakes/fakeagentstore" + "github.com/spiffe/spire/test/plugintest" + require "github.com/stretchr/testify/require" + grpc "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func BuiltIn() catalog.BuiltIn { + return builtin(&HybridPluginServer{}) +} + +func builtin(p *HybridPluginServer) catalog.BuiltIn { + return catalog.MakeBuiltIn("hybrid-node-attestor", + nodeattestorv1.NodeAttestorPluginServer(p), + configv1.ConfigServiceServer(p), + ) +} + +var pluginsString = `plugins { + k8s_psat { + clusters = { + "test-cluster" = { + service_account_allow_list = ["production:spire-agent"] + } + } + } + aws_iid { + access_key_id = "ACCESS_KEY_ID" + secret_access_key = "SECRET_ACCESS_KEY" + } +}` + +var pluginsStringInvalidData = `plugins { + k8s_psat { + } + aws_iid { + } + }` + +var pluginsStringEmptyData = `plugins {}` + +var coreSpireConfig configv1.CoreConfiguration = configv1.CoreConfiguration{ + TrustDomain: "example.org", +} + +func TestMethodsThatParseHclConfig(t *testing.T) { + plugin := HybridPluginServer{} + + pluginAstNode, err := plugin.decodeStringAndTransformToAstNode(pluginsString) + + require.NoError(t, err, "Error decoding test string") + require.Len(t, pluginAstNode, 2, "Could not transform HCL string configuration: %w", err) + require.Contains(t, pluginAstNode, "k8s_psat", "Could not access k8s_psat plugin by index on ast node") + require.Contains(t, pluginAstNode, "aws_iid", "Could not access aws_iid plugin by index on ast node") + + pluginNames, pluginsData := plugin.parseReceivedData(pluginAstNode) + + require.Len(t, pluginNames, 2, "Could not parse plugin names") + require.Contains(t, pluginNames, "k8s_psat", "Could not access k8s_psat plugin by index after parsing") + require.Contains(t, pluginNames, "aws_iid", "Could not access aws_iid plugin by index after parsing") + + require.Len(t, pluginsData, 2, "Could not parse plugin data") + require.Contains(t, pluginsData, "k8s_psat", "Could not access k8s_psat plugin by index after parsing") + + require.Equal(t, "\n clusters = {\n \"test-cluster\" = {\n service_account_allow_list = [\"production:spire-agent\"]\n }\n }\n", pluginsData["k8s_psat"], "k8s_psat plugin data was not extracted properly") + require.Contains(t, pluginsData, "aws_iid", "Could not access aws_iid plugin by index after parsing") + require.Equal(t, "\n access_key_id = \"ACCESS_KEY_ID\"\n secret_access_key = \"SECRET_ACCESS_KEY\"\n", pluginsData["aws_iid"], "aws_iid plugin data was not extracted properly") +} + +func TestSupportedPluginsInitialization(t *testing.T) { + interceptor := new(InterceptorWrapper) + plugin := HybridPluginServer{interceptor: interceptor} + + plugins, err := plugin.initPlugins([]string{"aws_iid", "k8s_psat", "azure_msi", "gcp_iit"}) + + require.NoError(t, err, "Error initializing supported plugins: %w", err) + require.IsType(t, &awsiid.IIDAttestorPlugin{}, plugins[0].Plugin, "Could not initialize aws_iid plugin") + require.IsType(t, &k8spsat.AttestorPlugin{}, plugins[1].Plugin, "Could not initialize k8s_psat plugin") + require.IsType(t, &azuremsi.MSIAttestorPlugin{}, plugins[2].Plugin, "Could not initialize azure_msi plugin") + require.IsType(t, &gcpiit.IITAttestorPlugin{}, plugins[3].Plugin, "Could not initialize gcp_iit plugin") + + plugins, err = plugin.initPlugins([]string{"aws_iid_test", "k8s_psat_test"}) + require.Error(t, err, "Error initializing supported plugins: %w", err) + require.Len(t, plugins, 0, "Plugin list length should be 0 on unknown plugin names") + +} + +func TestHybridPluginConfiguration(t *testing.T) { + interceptor := new(InterceptorWrapper) + plugin := HybridPluginServer{interceptor: interceptor} + + coreConfig := catalog.CoreConfig{ + TrustDomain: spiffeid.RequireTrustDomainFromString("example.org"), + } + var errConfig error + + plugintest.Load(t, builtin(&plugin), nil, + plugintest.CaptureConfigureError(&errConfig), + plugintest.HostServices(agentstorev1.AgentStoreServiceServer(fakeagentstore.New())), + plugintest.CoreConfig(coreConfig), + plugintest.Configure(pluginsString), + ) + require.NoError(t, errConfig, "Error configuring plugin: %w", errConfig) + require.Len(t, plugin.pluginList, 2, "Plugins used by Hybrid node attestor failed to start.") + + interceptor = new(InterceptorWrapper) + plugin = HybridPluginServer{interceptor: interceptor} + plugintest.Load(t, builtin(&plugin), nil, + plugintest.CaptureConfigureError(&errConfig), + plugintest.HostServices(agentstorev1.AgentStoreServiceServer(fakeagentstore.New())), + plugintest.CoreConfig(coreConfig), + plugintest.Configure(pluginsStringInvalidData), + ) + require.Error(t, errConfig, "Error configuring plugin: %v", errConfig) + + req := configv1.ConfigureRequest{HclConfiguration: pluginsStringEmptyData} + + _, errConfig = plugin.Configure(context.Background(), &req) + + error := status.Error(codes.FailedPrecondition, "No plugins supplied") + + if errConfig == nil || errConfig.Error() != error.Error() { + t.Error("Plugins used by Hybrid node attestor failed to start.") + } +} + +func TestHybridPluginServerInterceptor(t *testing.T) { + + combinedPayloads := []byte("") + stream := StreamMock{CombinedPayloads: &combinedPayloads} + interceptor := new(HybridPluginServerInterceptor) + + interceptor.SetContext(context.WithValue(context.Background(), "testkey", "testval")) + require.Equal(t, "testval", interceptor.Context().Value("testkey"), "Could not set interceptor context") + + interceptor.SetLogger(hclog.Default().Named("test_logger")) + require.Equal(t, "test_logger", interceptor.logger.Name(), "Could not set interceptor logger") + + interceptor.setCustomStream(&stream) + require.IsType(t, &StreamMock{}, interceptor.stream, "Could not set custom stream") + require.Equal(t, &stream, interceptor.Stream(), "Could not get custom stream") + + interceptor.combinedSelectors = []string{} + interceptor.spiffeID = "" + err := interceptor.Send(&nodeattestorv1.AttestResponse{ + Response: &nodeattestorv1.AttestResponse_AgentAttributes{ + AgentAttributes: &nodeattestorv1.AgentAttributes{ + SelectorValues: []string{"test", "test2"}, + SpiffeId: "spiffe://example.org/spire/agent/k8s_psat/test", + CanReattest: false, + }, + }, + }) + require.NoError(t, err, "Error sending response: %w", err) + + require.Equal(t, "spiffe://example.org/spire/agent/k8s_psat/test", interceptor.SpiffeID(), "Could not set custom response spiffeID") + + require.Equal(t, []string{"test", "test2"}, interceptor.CombinedSelectors(), "Could not set custom response selector values") + + err = interceptor.Send(&nodeattestorv1.AttestResponse{ + Response: &nodeattestorv1.AttestResponse_AgentAttributes{ + AgentAttributes: &nodeattestorv1.AgentAttributes{ + SelectorValues: []string{"test3", "test4"}, + SpiffeId: "spiffe://example.org/new/spiffeid", + CanReattest: true, + }, + }, + }) + require.NoError(t, err, "Error sending response: %w", err) + require.Equal(t, "spiffe://example.org/spire/agent/k8s_psat/test", interceptor.SpiffeID(), "SpiffeID was overwritten on second response: %q", interceptor.SpiffeID()) + require.Equal(t, []string{"test", "test2", "test3", "test4"}, interceptor.CombinedSelectors(), "Selector values not appended properly on second response: %q", interceptor.CombinedSelectors()) + + require.Equal(t, []bool{false, true}, interceptor.CanReattest(), "CanReattest was not set properly on second response: %t", interceptor.CanReattest()) + + var req nodeattestorv1.AttestRequest + req.Request = &nodeattestorv1.AttestRequest_ChallengeResponse{ + ChallengeResponse: []byte("testchallenge"), + } + interceptor.SetReq(&req) + gotReq, errConfig := interceptor.Recv() + require.NoError(t, errConfig, "Error receiving request: %w", errConfig) + require.Equal(t, []byte("testchallenge"), gotReq.GetChallengeResponse(), "Could not set interceptor request") +} + +func TestHybridPluginServerFuncsAndAttest(t *testing.T) { + + interceptorFake := new(InterceptorWrapper) + interceptorFake.returnError = nil + + hybridPlugin := HybridPluginServer{logger: hclog.Default().Named("old_log"), interceptor: interceptorFake} + + hybridPlugin.SetLogger(hclog.Default().Named("test_logger")) + require.Equal(t, "test_logger", hybridPlugin.logger.Name(), "Could not set logger for hybrid plugin") + + coreConfig := catalog.CoreConfig{ + TrustDomain: spiffeid.RequireTrustDomainFromString("example.org"), + } + var errConfig error + + plugintest.Load(t, builtin(&hybridPlugin), nil, + plugintest.CaptureConfigureError(&errConfig), + plugintest.HostServices(agentstorev1.AgentStoreServiceServer(fakeagentstore.New())), + plugintest.CoreConfig(coreConfig), + plugintest.Configure(pluginsString), + ) + require.NoError(t, errConfig, "Error configuring plugin: %w", errConfig) + require.Len(t, hybridPlugin.pluginList, 2, "Plugins used by Hybrid node attestor failed to start.") + + pluginOne := new(FakePlugin) + pluginTwo := new(FakePlugin) + pluginList := []common.Types{ + {PluginName: "k8s_psat", Plugin: pluginOne}, + {PluginName: "aws_iid", Plugin: pluginTwo}, + } + + hybridPlugin = HybridPluginServer{pluginList: pluginList, logger: hclog.Default(), interceptor: interceptorFake} + + combinedPayloads := []byte("a") + stream := StreamMock{CombinedPayloads: &combinedPayloads} + stream.returnError = nil + stream.Response = &nodeattestorv1.AttestResponse{ + Response: &nodeattestorv1.AttestResponse_AgentAttributes{ + AgentAttributes: &nodeattestorv1.AgentAttributes{ + SelectorValues: []string{"test", "test2"}, + SpiffeId: "spiffe://example.org/spire/agent/k8s_psat/test", + }, + }, + } + err := hybridPlugin.Attest(stream) + require.NoError(t, err, "Attest of hybrid plugin failed: %v", err) +} + +func TestHybridPluginErrors(t *testing.T) { + + pluginOne := new(FakePlugin) + pluginTwo := new(FakePlugin) + pluginTwo.returnError = status.Error(codes.InvalidArgument, "Plugin initialization error") + pluginList := []common.Types{ + {PluginName: "k8s_psat", Plugin: pluginOne}, + {PluginName: "aws_iid", Plugin: pluginTwo}, + } + interceptorFake := new(InterceptorWrapper) + interceptorFake.returnError = nil + hybridPlugin := HybridPluginServer{pluginList: pluginList, logger: hclog.Default(), interceptor: interceptorFake} + + combinedPayloads := []byte("a") + stream := StreamMock{CombinedPayloads: &combinedPayloads} + stream.returnError = nil + stream.Response = &nodeattestorv1.AttestResponse{ + Response: &nodeattestorv1.AttestResponse_AgentAttributes{ + AgentAttributes: &nodeattestorv1.AgentAttributes{ + SelectorValues: []string{"test", "test2"}, + SpiffeId: "spiffe://example.org/spire/agent/k8s_psat/test", + }, + }, + } + err := hybridPlugin.Attest(stream) + require.Error(t, err, "Attest of hybrid plugin should fail") + + stream.returnError = status.Error(codes.InvalidArgument, "Plugin atestation error") + + err = hybridPlugin.Attest(stream) + require.Error(t, err, "Attest of hybrid plugin should fail.", err) + + stream.returnError = status.Error(codes.InvalidArgument, "Error sending response") + err = hybridPlugin.Attest(stream) + require.EqualError(t, err, "rpc error: code = InvalidArgument desc = Error sending response", "Attest failed with unexpected error: %v", err) + +} + +func TestSendResponse(t *testing.T) { + interceptor := new(InterceptorWrapper) + plugin := HybridPluginServer{interceptor: interceptor} + + coreConfig := catalog.CoreConfig{ + TrustDomain: spiffeid.RequireTrustDomainFromString("example.org"), + } + var errConfig error + fakeStore := fakeagentstore.New() + plugintest.Load(t, builtin(&plugin), nil, + plugintest.CaptureConfigureError(&errConfig), + plugintest.HostServices(agentstorev1.AgentStoreServiceServer(fakeStore)), + plugintest.CoreConfig(coreConfig), + plugintest.Configure(pluginsString), + ) + require.NoError(t, errConfig, "Error configuring plugin: %w", errConfig) + require.Len(t, plugin.pluginList, 2, "Plugins used by Hybrid node attestor failed to start.") + pluginOne := new(FakePlugin) + pluginTwo := new(FakePlugin) + pluginTwo.returnError = status.Error(codes.InvalidArgument, "Plugin initialization error") + pluginList := []common.Types{ + {PluginName: "k8s_psat", Plugin: pluginOne}, + {PluginName: "aws_iid", Plugin: pluginTwo}, + } + + plugin.pluginList = pluginList + interceptor.canReattest = []bool{true, false} + combinedPayloads := []byte("a") + stream := StreamMock{CombinedPayloads: &combinedPayloads} + stream.returnError = nil + stream.Response = &nodeattestorv1.AttestResponse{ + Response: &nodeattestorv1.AttestResponse_AgentAttributes{ + AgentAttributes: &nodeattestorv1.AgentAttributes{ + SelectorValues: []string{"test", "test2"}, + SpiffeId: "spiffe://example.org/spire/agent/k8s_psat/test", + }, + }, + } + interceptor.setCustomStream(&stream) + interceptor.spiffeid = "spiffe://example.org/spire/agent/k8s_psat/test" + + err := plugin.SendResponse() + require.NoError(t, err, "SendResponse failed: %v", err) + + fakeStore.SetAgentInfo(&agentstorev1.AgentInfo{ + AgentId: "spiffe://example.org/spire/agent/k8s_psat/test", + }) + err = plugin.SendResponse() + require.Error(t, err, "SendResponse failed: %v", err) + + fakeStore.SetAgentErr("spiffe://example.org/spire/agent/k8s_psat/test", status.Error(codes.InvalidArgument, "Error retrieving agentInfo")) + err = plugin.SendResponse() + require.EqualError(t, err, "rpc error: code = InvalidArgument desc = unable to get agent info: Error retrieving agentInfo", "SendResponse failed unexpectedly: %v", err) + +} + +func TestNew(t *testing.T) { + hybridPlugin := New() + require.NotNil(t, hybridPlugin, "New should return a non-nil value") + require.IsType(t, &HybridPluginServer{}, hybridPlugin, "New should return a HybridPluginServer") +} + +func TestResetInterceptor(t *testing.T) { + req := nodeattestorv1.AttestRequest{Request: &nodeattestorv1.AttestRequest_Payload{Payload: nil}} + resp := nodeattestorv1.AttestResponse{Response: &nodeattestorv1.AttestResponse_AgentAttributes{ + AgentAttributes: &nodeattestorv1.AgentAttributes{ + SelectorValues: []string{"test", "test2"}, + SpiffeId: "spiffe://example.org/spire/agent/k8s_psat/test", + CanReattest: false, + }, + }} + interceptor := HybridPluginServerInterceptor{ctx: context.Background(), stream: StreamMock{}, logger: hclog.Default(), req: &req, Response: &resp, + combinedSelectors: []string{"test"}, spiffeID: "spiffe", canReattest: []bool{true}} + + if interceptor.ctx == nil && interceptor.stream == nil && interceptor.logger == nil && interceptor.req == nil && interceptor.Response == nil && + interceptor.combinedSelectors == nil && interceptor.spiffeID == "" && interceptor.canReattest == nil { + errors.New("Interceptor is empty") + } + + interceptor.ResetInterceptor() + + if interceptor.ctx != nil && interceptor.stream != nil && interceptor.logger != nil && interceptor.req != nil && interceptor.Response != nil && + interceptor.combinedSelectors != nil && interceptor.spiffeID != "" && interceptor.canReattest != nil { + errors.New("Cannot reset interceptor") + } +} + +// ------------------------------------------------------------------------------------------------------------------------ + +type FakePlugin struct { + returnError error + request *nodeattestorv1.AttestRequest + logger hclog.Logger +} + +func (f *FakePlugin) SetReturnError(err error) { + f.returnError = err +} + +func (f *FakePlugin) Attest(stream nodeattestorv1.NodeAttestor_AttestServer) error { + if f.returnError != nil { + return f.returnError + } + f.request, f.returnError = stream.Recv() + return f.returnError +} + +func (f *FakePlugin) Configure(ctx context.Context, req *configv1.ConfigureRequest) (*configv1.ConfigureResponse, error) { + fmt.Println("configure fake ", f.returnError) + if f.returnError != nil { + return nil, f.returnError + + } + + return &configv1.ConfigureResponse{}, nil +} + +func (f *FakePlugin) SetLogger(logger hclog.Logger) { + f.logger = logger +} + +// ---------------------------------------------------------------------------- + +type StreamMock struct { + grpc.ServerStream + CombinedPayloads *[]byte + Response *nodeattestorv1.AttestResponse + + returnError error +} + +func (s StreamMock) Recv() (*nodeattestorv1.AttestRequest, error) { + if s.returnError != nil { + return nil, s.returnError + } + request := nodeattestorv1.AttestRequest{Request: &nodeattestorv1.AttestRequest_Payload{Payload: *s.CombinedPayloads}} + return &request, nil +} + +func (s StreamMock) Send(challenge *nodeattestorv1.AttestResponse) error { + *s.Response = *challenge + + return s.returnError +} + +func (s StreamMock) Context() context.Context { + return context.Background() +} + +// ---------------------------------------------------------------------------- + +type PluginWrapper struct { + nodeattestorv1.NodeAttestor_AttestServer +} + +func (pw *PluginWrapper) Context() context.Context { + return context.Background() +} + +// ------------------------------------------------------------------------------------------------------------------------ + +type InterceptorWrapper struct { + returnError error + stream nodeattestorv1.NodeAttestor_AttestServer + nodeattestorv1.NodeAttestor_AttestServer + canReattest []bool + spiffeid string +} + +func (iw *InterceptorWrapper) SetReturnError(err error) { + iw.returnError = err +} + +func (iw *InterceptorWrapper) Recv() (*nodeattestorv1.AttestRequest, error) { + return nil, nil +} + +func (iw *InterceptorWrapper) Send(resp *nodeattestorv1.AttestResponse) error { + return nil +} + +func (iw *InterceptorWrapper) setCustomStream(stream nodeattestorv1.NodeAttestor_AttestServer) { + iw.stream = stream +} + +func (iw *InterceptorWrapper) SetContext(ctx context.Context) { + +} + +func (iw *InterceptorWrapper) Context() context.Context { + return nil +} + +func (iw *InterceptorWrapper) SetLogger(logger hclog.Logger) { + +} + +func (iw *InterceptorWrapper) SendCombined() error { + if iw.returnError != nil { + return status.Errorf(codes.Internal, "Test Error: %v", iw.returnError) + } + + return nil +} + +func (iw *InterceptorWrapper) CanReattest() []bool { + return iw.canReattest +} + +func (iw *InterceptorWrapper) GetPayloads() [][]byte { + return nil +} +func (iw *InterceptorWrapper) CombinedSelectors() []string { + return nil +} + +func (iw *InterceptorWrapper) SetReq(req *nodeattestorv1.AttestRequest) { +} + +func (iw *InterceptorWrapper) SpiffeID() string { + return iw.spiffeid +} + +func (iw *InterceptorWrapper) Stream() nodeattestorv1.NodeAttestor_AttestServer { + return iw.stream +} + +func (iw *InterceptorWrapper) ResetInterceptor() { + +} diff --git a/hybrid/test/integration/common b/hybrid/test/integration/common new file mode 100644 index 0000000..6ea2fac --- /dev/null +++ b/hybrid/test/integration/common @@ -0,0 +1,40 @@ +#!/bin/bash + +log-debug() { + echo "${norm}$(timestamp) $*" +} + +fail-now() { + echo "${red}$(timestamp) $*${norm}" + exit 1 +} + +timestamp() { + date -u "+[%Y-%m-%dT%H:%M:%SZ]" +} + +create-ns() { + kind_namespace=$1 + log-debug "Creating namespace..." + kubectl create namespace ${kind_namespace} +} + +delete-ns() { + log-debug "Deleting namespace..." + kubectl delete --ignore-not-found namespace $1 > /dev/null +} + +cleanup-logs() { + if [ -z "${GOOD}" ]; then + log-debug "Dumping statefulset/spire-server logs..." + kubectl -nspire logs statefulset/spire-server --all-containers + log-debug "Dumping daemonset/spire-agent logs..." + kubectl -nspire logs daemonset/spire-agent --all-containers + fi + delete-ns + if [ -n "${GOOD}" ]; then + log-debug "Success." + else + log-debug "Failed." + fi +} \ No newline at end of file diff --git a/hybrid/test/integration/suites/node-attestation-hybrid/00-setup b/hybrid/test/integration/suites/node-attestation-hybrid/00-setup new file mode 100755 index 0000000..38ac135 --- /dev/null +++ b/hybrid/test/integration/suites/node-attestation-hybrid/00-setup @@ -0,0 +1,5 @@ +#!/bin/bash + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +delete-ns "spire" \ No newline at end of file diff --git a/hybrid/test/integration/suites/node-attestation-hybrid/01-start-server-agent b/hybrid/test/integration/suites/node-attestation-hybrid/01-start-server-agent new file mode 100755 index 0000000..abd61d8 --- /dev/null +++ b/hybrid/test/integration/suites/node-attestation-hybrid/01-start-server-agent @@ -0,0 +1,7 @@ +#!/bin/bash + +DIR=../../../../ +create-ns "spire" +make -C $DIR build +make -C $DIR docker +make -C $DIR deploy-spire-eks \ No newline at end of file diff --git a/hybrid/test/integration/suites/node-attestation-hybrid/02-test=node-attestation b/hybrid/test/integration/suites/node-attestation-hybrid/02-test=node-attestation new file mode 100755 index 0000000..ff470d8 --- /dev/null +++ b/hybrid/test/integration/suites/node-attestation-hybrid/02-test=node-attestation @@ -0,0 +1,20 @@ +#!/bin/bash + +LOGLINE="Agent attestation request completed" +for ((i=0;i<12;i++)); do + if ! kubectl -nspire rollout status statefulset/spire-server; then + sleep 1 + continue + fi + if ! kubectl -nspire rollout status daemonset/spire-agent; then + sleep 1 + continue + fi + if ! kubectl -nspire logs statefulset/spire-server -c spire-server | grep -e "$LOGLINE" ; then + sleep 1 + continue + fi + log-debug "Node attested." + GOOD=1 + exit 0 +done \ No newline at end of file diff --git a/hybrid/test/integration/suites/node-attestation-hybrid/README.md b/hybrid/test/integration/suites/node-attestation-hybrid/README.md new file mode 100644 index 0000000..b40215d --- /dev/null +++ b/hybrid/test/integration/suites/node-attestation-hybrid/README.md @@ -0,0 +1,10 @@ +# Hybrid Node Attestation Suite + +## Description + +Basic tests of the hybrid node attestation APIs + +## Executing Test +``` +make integration SUITES=suites/node-attestation-hybrid +``` \ No newline at end of file diff --git a/hybrid/test/integration/suites/node-attestation-hybrid/teardown b/hybrid/test/integration/suites/node-attestation-hybrid/teardown new file mode 100755 index 0000000..ca2a7a6 --- /dev/null +++ b/hybrid/test/integration/suites/node-attestation-hybrid/teardown @@ -0,0 +1,3 @@ +#!/bin/bash + +trap cleanup-logs EXIT \ No newline at end of file diff --git a/hybrid/test/integration/test-one.sh b/hybrid/test/integration/test-one.sh new file mode 100755 index 0000000..ea1421a --- /dev/null +++ b/hybrid/test/integration/test-one.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +ROOTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +COMMON="${ROOTDIR}/common" + +# shellcheck source=./common +source "${COMMON}" + +[ -n "$1" ] || fail-now "must pass the test suite directory as the first argument" +[ -d "$1" ] || fail-now "$1 does not exist or is not a directory" + +TESTDIR="$( cd "$1" && pwd )" +TESTNAME="$(basename "${TESTDIR}")" + +log-debug "running \"${TESTNAME}\" test suite..." + +[ -x "${TESTDIR}"/teardown ] || fail-now "missing required teardown script or it is not executable" +[ -f "${TESTDIR}"/README.md ] || fail-now "missing required README.md file" + + +################################################# +# Execute the test suite +################################################# +run-step() { + local script="$1" + if [ ! -x "$script" ]; then + log-warn "skipping \"$script\"; not executable" + return + fi + log-debug "executing $(basename "$script")..." + + bash -s <