From 875e4fc365881cf667fd2441950f30fa3eb719cf Mon Sep 17 00:00:00 2001 From: Jan Knipper <9881823+jknipper@users.noreply.github.com> Date: Tue, 16 Apr 2024 16:08:29 +0200 Subject: [PATCH] Delete vice-president directory The operator is not used anymore and replaced by cert-manager/digicert-issuer. --- vice-president/.dockerignore | 2 - vice-president/Dockerfile | 16 - vice-president/LICENSE | 201 ------- vice-president/Makefile | 43 -- vice-president/README.md | 99 ---- vice-president/cmd/main.go | 86 --- .../etc/vice-president/vice-president.conf | 11 - .../example/vice-presidential-ingress.yaml | 20 - vice-president/go.mod | 37 -- vice-president/go.sum | 265 --------- vice-president/p12_to_key_and_cert.sh | 7 - vice-president/pkg/config/config.go | 58 -- vice-president/pkg/config/options.go | 76 --- vice-president/pkg/k8sutils/framework.go | 398 ------------- vice-president/pkg/k8sutils/ingress.go | 96 ---- vice-president/pkg/k8sutils/ingress_test.go | 69 --- vice-president/pkg/k8sutils/secret.go | 82 --- vice-president/pkg/k8sutils/util.go | 27 - vice-president/pkg/log/log.go | 88 --- vice-president/pkg/president/certificate.go | 286 ---------- .../pkg/president/certificate_test.go | 135 ----- vice-president/pkg/president/constants.go | 89 --- .../fixtures/approveCertificateResponse.xml | 28 - .../pkg/president/fixtures/chain.pem | 38 -- .../fixtures/enrollCertificateResponse.xml | 5 - .../pkg/president/fixtures/example.key | 27 - .../pkg/president/fixtures/example.kubeconfig | 20 - .../pkg/president/fixtures/example.pem | 38 -- .../fixtures/example.vicepresidentconfig | 14 - .../pkg/president/fixtures/intermediate.pem | 17 - .../pkg/president/fixtures/mySecret.json | 17 - .../fixtures/pickupCertificateResponse.xml | 27 - .../fixtures/renewCertificateResponse.xml | 5 - vice-president/pkg/president/metrics.go | 185 ------ vice-president/pkg/president/operator.go | 540 ------------------ vice-president/pkg/president/operator_test.go | 145 ----- vice-president/pkg/president/secret.go | 88 --- vice-president/pkg/president/secret_test.go | 62 -- .../pkg/president/symantecMetricCollector.go | 191 ------- vice-president/pkg/president/test_util.go | 239 -------- vice-president/pkg/president/util.go | 232 -------- vice-president/pkg/president/vice.go | 252 -------- 42 files changed, 4361 deletions(-) delete mode 100644 vice-president/.dockerignore delete mode 100644 vice-president/Dockerfile delete mode 100644 vice-president/LICENSE delete mode 100644 vice-president/Makefile delete mode 100644 vice-president/README.md delete mode 100644 vice-president/cmd/main.go delete mode 100644 vice-president/etc/vice-president/vice-president.conf delete mode 100644 vice-president/example/vice-presidential-ingress.yaml delete mode 100644 vice-president/go.mod delete mode 100644 vice-president/go.sum delete mode 100755 vice-president/p12_to_key_and_cert.sh delete mode 100644 vice-president/pkg/config/config.go delete mode 100644 vice-president/pkg/config/options.go delete mode 100644 vice-president/pkg/k8sutils/framework.go delete mode 100644 vice-president/pkg/k8sutils/ingress.go delete mode 100644 vice-president/pkg/k8sutils/ingress_test.go delete mode 100644 vice-president/pkg/k8sutils/secret.go delete mode 100644 vice-president/pkg/k8sutils/util.go delete mode 100644 vice-president/pkg/log/log.go delete mode 100644 vice-president/pkg/president/certificate.go delete mode 100644 vice-president/pkg/president/certificate_test.go delete mode 100644 vice-president/pkg/president/constants.go delete mode 100644 vice-president/pkg/president/fixtures/approveCertificateResponse.xml delete mode 100644 vice-president/pkg/president/fixtures/chain.pem delete mode 100644 vice-president/pkg/president/fixtures/enrollCertificateResponse.xml delete mode 100644 vice-president/pkg/president/fixtures/example.key delete mode 100644 vice-president/pkg/president/fixtures/example.kubeconfig delete mode 100644 vice-president/pkg/president/fixtures/example.pem delete mode 100644 vice-president/pkg/president/fixtures/example.vicepresidentconfig delete mode 100644 vice-president/pkg/president/fixtures/intermediate.pem delete mode 100644 vice-president/pkg/president/fixtures/mySecret.json delete mode 100644 vice-president/pkg/president/fixtures/pickupCertificateResponse.xml delete mode 100644 vice-president/pkg/president/fixtures/renewCertificateResponse.xml delete mode 100644 vice-president/pkg/president/metrics.go delete mode 100644 vice-president/pkg/president/operator.go delete mode 100644 vice-president/pkg/president/operator_test.go delete mode 100644 vice-president/pkg/president/secret.go delete mode 100644 vice-president/pkg/president/secret_test.go delete mode 100644 vice-president/pkg/president/symantecMetricCollector.go delete mode 100644 vice-president/pkg/president/test_util.go delete mode 100644 vice-president/pkg/president/util.go delete mode 100644 vice-president/pkg/president/vice.go diff --git a/vice-president/.dockerignore b/vice-president/.dockerignore deleted file mode 100644 index 1a2653597..000000000 --- a/vice-president/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -bin/ -Dockerfile diff --git a/vice-president/Dockerfile b/vice-president/Dockerfile deleted file mode 100644 index d9381322e..000000000 --- a/vice-president/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -FROM golang:1.12.9-alpine3.10 as builder -WORKDIR /go/src/github.com/sapcc/kubernetes-operators/vice-president -RUN apk add --no-cache make dep git mercurial -COPY . . -ARG VERSION -RUN make all - -FROM alpine:3.10 -LABEL maintainer="Michael Schmidt , Arno Uhlig " -LABEL source_repository="https://github.com/sapcc/kubernetes-operators" - -RUN apk add --no-cache tini ca-certificates wget -RUN tini --version -COPY --from=builder /go/src/github.com/sapcc/kubernetes-operators/vice-president/bin/linux/vice-president /usr/local/bin/ -ENTRYPOINT ["tini", "--"] -CMD ["vice-president"] diff --git a/vice-president/LICENSE b/vice-president/LICENSE deleted file mode 100644 index 8dada3eda..000000000 --- a/vice-president/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vice-president/Makefile b/vice-president/Makefile deleted file mode 100644 index fb46f8de0..000000000 --- a/vice-president/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -DATE = $(shell date +%Y%m%d%H%M) -IMAGE ?= sapcc/vice-president -VERSION = v$(DATE) -GOOS ?= $(shell go env | grep GOOS | cut -d'"' -f2) -BINARY := vice-president - -LDFLAGS := -X github.com/sapcc/kubernetes-operators/vice-president/pkg/president.VERSION=$(VERSION) -GOFLAGS := -ldflags "$(LDFLAGS)" - -SRCDIRS := cmd pkg -PACKAGES := $(shell find $(SRCDIRS) -type d) -GOFILES := $(addsuffix /*.go,$(PACKAGES)) -GOFILES := $(wildcard $(GOFILES)) - -.PHONY: all clean tests static-check - -all: bin/$(GOOS)/$(BINARY) - -bin/%/$(BINARY): $(GOFILES) Makefile - GOOS=$* GOARCH=amd64 go build $(GOFLAGS) -v -i -o bin/$*/$(BINARY) ./cmd - -build: tests bin/linux/$(BINARY) - docker build -t $(IMAGE):$(VERSION) . - -static-check: - @if s="$$(gofmt -s -l *.go pkg 2>/dev/null)" && test -n "$$s"; then printf ' => %s\n%s\n' gofmt "$$s"; false; fi - @if s="$$(golint . && find pkg -type d -exec golint {} \; 2>/dev/null)" && test -n "$$s"; then printf ' => %s\n%s\n' golint "$$s"; false; fi - -tests: all static-check - go test -v github.com/sapcc/kubernetes-operators/vice-president/pkg/... - -push: build - docker push $(IMAGE):$(VERSION) - -clean: - rm -rf bin/* - -generate-dummy-cert: - mkdir tmp - openssl genrsa -out tmp/ca.key 2048 - openssl req -new -x509 -days 365 -key tmp/ca.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=MyRootCA" -out tmp/ca.crt - openssl req -newkey rsa:2048 -nodes -keyout tmp/cert.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=www.example.com" -out tmp/cert.csr - openssl x509 -req -extfile <(printf "subjectAltName=DNS:example.com,DNS:www.example.com,DNS:www.my-example.com") -days 10 -in tmp/cert.csr -CA tmp/ca.crt -CAkey tmp/ca.key -CAcreateserial -out tmp/cert.pem diff --git a/vice-president/README.md b/vice-president/README.md deleted file mode 100644 index 3a4146f4d..000000000 --- a/vice-president/README.md +++ /dev/null @@ -1,99 +0,0 @@ -# Vice President - -[![Docker Repository](https://img.shields.io/docker/pulls/sapcc/vice-president.svg?maxAge=604800)](https://hub.docker.com/r/sapcc/vice-president/) - -This operator automatically checks, requests and renews certificates for Kubernetes Ingresses using the Symantec Vice API. - -An ingress is a resource in Kubernetes that comprises a set of rules, which allow routing inbound traffic to cluster services. -Find more details in the [official documentation](https://kubernetes.io/docs/concepts/services-networking/ingress/#what-is-ingress). - -The vice president discovers the ingresses in a cluster by continuously watching the Kubernetes API. -When it comes to TLS, an ingress can reference a secret in it's spec TLS section. -This secret contains the certificate and the private key, of which the vice president will take care. - -The operator exposes prometheus metrics on successful or failed enrollments, renewals, approvals or pickups, -which can be useful in case of an error. - -## Features - - - Discovers required certificates via Kubernetes API . - - Automatically requests Certificates via Symantec API. - - Periodically verifies Certificates. - - Automatically renews certificates that would expire within a configurable duration. - - Exposes Prometheus metrics. - - Creates Kubernetes events for successful, failed certificate creations. - -## Requirements - - - go1.8.3 - -## Usage - -A [helm chart](https://github.com/sapcc/helm-charts/tree/master/system/kube-system/charts/vice-president/) can be used to bring the vice president to your cluster. -Note that the vice president considers only ingresses that are annotated with -``` -metadata: - annotations: - vice-president: "true" -``` -Other ingresses are ignored. See [example ingress](./example/vice-presidential-ingress.yaml). - -The following configuration and certificates are required. -An example VICE configuration can be found [here](./etc/vice-president/vice-president.conf). - -``` -Usage of vice-president: - --ca-cert string A PEM encoded root CA certificate. (optional. will attempt to download if not found) (default "/etc/vice-president/secrets/ca.cert") - --certificate-recheck-interval duration Interval for checking certificates. (default 5m0s) - --debug Enable debug logging. - --enable-symantec-metrics Export additional symantec metrics. - --enable-validate-remote-cert Enable validation of remote certificate via TLS handshake. - --intermediate-cert string A PEM encoded intermediate certificate. (default "/etc/vice-president/secrets/intermediate.cert") - --kubeconfig string Path to kubeconfig file with authorization and master location information. - --metric-port int Port on which Prometheus metrics are exposed. (default 9091) - --min-cert-validity-days int Renew certificates that expire within n days. (default 30) - --namespace string Limit operator to given namespace. (default "") - --rate-limit int Rate limit of certificate enrollments per host. (unlimited: -1) (default 2) - --resync-interval duration Interval for resyncing informers. (default 2m0s) - --threadiness int Operator threadiness. (default 10) - --vice-cert string A PEM encoded certificate file. (default "/etc/vice-president/secrets/vice.cert") -``` - -Moreover the operator stores the TLS key and certificate in the secret using the following format: -``` -... -data: - tls.crt: < x509.Certificate > - tls.key: < rsa.PrivateKey > -``` -The keys `tls.crt`,`tls.key` can be adjusted by setting an annotation. Example: -``` -metadata: - annotations: - vice-president/tls-cert-secret-key: "ssl.cert" - vice-president/tls-key-secret-key: "ssl.key" -``` - -Setting the annotation `vice-president/replace-cert: "true"` will immediately trigger the replacement of the certificate, -which might be helpful while switching from Symantec to Digicert CA. - -## Development - -The vice president uses [dep](https://github.com/golang/dep) to manage its dependencies. -Run `make vendor` to install them. - -## Debug - -The vice president provides the following set of metrics, which can be useful for alerting or debugging: - `vice_president_successful_enrollments` - `vice_president_failed_enrollments` - `vice_president_successful_renewals` - `vice_president_failed_renewals` - `vice_president_successful_approvals` - `vice_president_failed_approvals` - `vice_president_successful_pickups` - `vice_president_failed_pickups` - -Moreover, CSRs are persistent to the `/tmp` folder. -Details on errors returned by the Symantec VICE API can be found [here](https://support.venafi.com/hc/en-us/articles/215914347-Info-VeriSign-Symantec-MPKI-Error-Codes). - diff --git a/vice-president/cmd/main.go b/vice-president/cmd/main.go deleted file mode 100644 index 99c4e377c..000000000 --- a/vice-president/cmd/main.go +++ /dev/null @@ -1,86 +0,0 @@ -/******************************************************************************* -* -* Copyright 2019 SAP SE -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You should have received a copy of the License along with this -* program. If not, you may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -*******************************************************************************/ - -package main - -import ( - "flag" - "github.com/sapcc/kubernetes-operators/vice-president/pkg/config" - "os" - "os/signal" - "sync" - "syscall" - "time" - - "github.com/sapcc/kubernetes-operators/vice-president/pkg/log" - "github.com/sapcc/kubernetes-operators/vice-president/pkg/president" - "github.com/spf13/pflag" -) - -var options config.Options - -func init() { - pflag.StringVar(&options.KubeConfig, "kubeconfig", "", "Path to kubeconfig file with authorization and master location information.") - pflag.StringVar(&options.VicePresidentConfig, "vice-president-config", "/etc/vice-president/config/vice-president.conf", "Path to VICE President config file with certificate parameters.") - pflag.StringVar(&options.ViceCrtFile, "vice-cert", "/etc/vice-president/secrets/vice.cert", "A PEM encoded certificate file.") - pflag.StringVar(&options.ViceKeyFile, "vice-key", "/etc/vice-president/secrets/vice.key", "A PEM encoded private key file.") - pflag.StringVar(&options.IntermediateCertificate, "intermediate-cert", "/etc/vice-president/secrets/intermediate.cert", "A PEM encoded intermediate certificate.") - pflag.StringVar(&options.RootCACertificate, "ca-cert", "/etc/vice-president/secrets/ca.cert", "A PEM encoded root CA certificate. (optional. will attempt to download if not found)") - pflag.IntVar(&options.MinCertValidityDays, "min-cert-validity-days", 30, "Renew certificates that expire within n days.") - pflag.BoolVar(&options.EnableValidateRemoteCertificate, "enable-validate-remote-cert", false, "Enable validation of remote certificate via TLS handshake.") - pflag.IntVar(&options.MetricPort, "metric-port", 9091, "Port on which Prometheus metrics are exposed.") - pflag.BoolVar(&options.IsEnableAdditionalSymantecMetrics, "enable-symantec-metrics", false, "Export additional symantec metrics.") - pflag.BoolVar(&options.IsDebug, "debug", false, "Enable debug logging.") - pflag.DurationVar(&options.CertificateCheckInterval, "certificate-recheck-interval", 5*time.Minute, "Interval for checking certificates.") - pflag.DurationVar(&options.ResyncInterval, "resync-interval", 2*time.Minute, "Interval for resyncing informers.") - pflag.IntVar(&options.RateLimit, "rate-limit", 2, "Rate limit of certificate enrollments per host. (unlimited: -1)") - pflag.IntVar(&options.Threadiness, "threadiness", 10, "Operator threadiness.") - pflag.StringVar(&options.Namespace, "namespace", "", "Limit operator to given namespace.") - pflag.StringVar(&options.Finalizer, "finalizer", "vicepresident.extensions/v1beta1", "FinalizerVicePresident is the vice presidential finalizer for an ingress") - pflag.StringVar(&options.EventComponent, "event-component", "vice-president", "Component to use for kubernetes events.") - pflag.StringVar(&options.IngressAnnotation, "ingress-annotation", "vice-president", "Handle ingress' with this annotation.") -} - -func main() { - pflag.CommandLine.AddGoFlagSet(flag.CommandLine) - pflag.Parse() - - logger := log.NewLogger(options.IsDebug) - - sigs := make(chan os.Signal, 1) - stop := make(chan struct{}) - signal.Notify(sigs, os.Interrupt, syscall.SIGTERM) // Push signals into channel - - wg := &sync.WaitGroup{} // Goroutines can add themselves to this to be waited on - - vp, err := president.New(options, logger) - if err != nil { - logger.LogFatal("fatal error while starting operator", "err", err) - return - } - - go vp.Run(options.Threadiness, stop, wg) - go president.ExposeMetrics(options, stop, wg, logger) - - <-sigs // Wait for signals (this hangs until a signal arrives) - logger.LogInfo("Shutting down...") - - close(stop) // Tell goroutines to stop themselves - wg.Wait() // Wait for all to be stopped -} diff --git a/vice-president/etc/vice-president/vice-president.conf b/vice-president/etc/vice-president/vice-president.conf deleted file mode 100644 index 08af00d35..000000000 --- a/vice-president/etc/vice-president/vice-president.conf +++ /dev/null @@ -1,11 +0,0 @@ -# symantec VICE API configuration -vice: - first_name: "Max" - last_name: "Muster" - email: "max@muster.com" - country: "DE" - province: "Berlin" - locality: "Berlin" - organization: "My Company" - organizational_unit: "P&I" - default_challenge: "Password1!" diff --git a/vice-president/example/vice-presidential-ingress.yaml b/vice-president/example/vice-presidential-ingress.yaml deleted file mode 100644 index 5ab1ee1ad..000000000 --- a/vice-president/example/vice-presidential-ingress.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - annotations: - vice-president: "true" - name: vice-presidential-ingress - namespace: vice-president -spec: - rules: - - host: vice.president.com - http: - paths: - - backend: - serviceName: vice-president - servicePort: 80 - path: / - tls: - - hosts: - - vice.president.com - secretName: vice-presidential-secret diff --git a/vice-president/go.mod b/vice-president/go.mod deleted file mode 100644 index 727e0e327..000000000 --- a/vice-president/go.mod +++ /dev/null @@ -1,37 +0,0 @@ -module github.com/sapcc/kubernetes-operators/vice-president - -go 1.12 - -require ( - github.com/evanphx/json-patch v4.5.0+incompatible // indirect - github.com/go-kit/kit v0.9.0 - github.com/go-openapi/strfmt v0.19.3 // indirect - github.com/go-openapi/validate v0.19.4 // indirect - github.com/gogo/protobuf v1.3.0 // indirect - github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect - github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect - github.com/google/btree v1.0.0 // indirect - github.com/google/go-querystring v1.0.0 // indirect - github.com/googleapis/gnostic v0.3.1 // indirect - github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect - github.com/hashicorp/golang-lru v0.5.3 // indirect - github.com/imdario/mergo v0.3.7 // indirect - github.com/peterbourgon/diskv v2.0.1+incompatible // indirect - github.com/pkg/errors v0.8.1 - github.com/prometheus/client_golang v1.1.0 - github.com/sapcc/go-vice v0.0.0-20190821082040-e4ca123d1d42 - github.com/sapcc/kubernikus v1.5.0 // indirect - github.com/spf13/pflag v1.0.3 - github.com/stretchr/testify v1.4.0 - golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 - golang.org/x/net v0.0.0-20190912160710-24e19bdeb0f2 // indirect - golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 // indirect - golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect - gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0 - k8s.io/api v0.0.0-20181204000039-89a74a8d264d - k8s.io/apimachinery v0.0.0-20181127025237-2b1284ed4c93 - k8s.io/client-go v10.0.0+incompatible - k8s.io/klog v0.4.0 // indirect - k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf // indirect -) diff --git a/vice-president/go.sum b/vice-president/go.sum deleted file mode 100644 index d6dc52891..000000000 --- a/vice-president/go.sum +++ /dev/null @@ -1,265 +0,0 @@ -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -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/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -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/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -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/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= -github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= -github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= -github.com/go-openapi/analysis v0.19.4/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= -github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= -github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= -github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= -github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= -github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= -github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= -github.com/go-openapi/strfmt v0.19.2/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= -github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= -github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= -github.com/go-openapi/validate v0.19.4 h1:LGjO87VyXY3bIKjlYpXSFuLRG2mTeuYlZyeNwFFWpyM= -github.com/go-openapi/validate v0.19.4/go.mod h1:BkJ0ZmXui7yB0bJXWSXgLPNTmbLVeX/3D1xn/N9mMUM= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.3.0 h1:G8O7TerXerS4F6sx9OV7/nRfJdnXgHZu/S/7F2SN+UE= -github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -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 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= -github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk= -github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= -github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= -github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= -github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= -github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -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/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -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/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -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-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c h1:Hww8mOyEKTeON4bZn7FrlLismspbPc1teNRUVH7wLQ8= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c h1:eSfnfIuwhxZyULg1NNuZycJcYkjYVGYe7FczwQReM6U= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -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.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -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.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/sapcc/go-vice v0.0.0-20190821082040-e4ca123d1d42 h1:YW+fmrltroSu0+pcRKFYulxXa21hi/ABhTkDmulzsl8= -github.com/sapcc/go-vice v0.0.0-20190821082040-e4ca123d1d42/go.mod h1:lMDxaObNEtoIgIZ4rgwOnVKIugNfMe8DCenltRBq1hs= -github.com/sapcc/kubernikus v1.5.0/go.mod h1:myK1pthNZIGRz7BRoztctl69QaZzj69fbGr4WvzEJ2M= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -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.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -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 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -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-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 h1:0hQKqeLdqlt5iIwVOBErRisrHJAN57yOiPRQItI20fU= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/net v0.0.0-20170114055629-f2499483f923/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-20181005035420-146acd28ed58/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-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/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-20190912160710-24e19bdeb0f2 h1:4dVFTC832rPn4pomLSz1vA+are2+dU19w1H8OngV7nc= -golang.org/x/net v0.0.0-20190912160710-24e19bdeb0f2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/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-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/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-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0 h1:POO/ycCATvegFmVuPpQzZFJ+pGZeX22Ufu6fibxDVjU= -gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -k8s.io/api v0.0.0-20181204000039-89a74a8d264d h1:HQoGWsWUe/FmRcX9BU440AAMnzBFEf+DBo4nbkQlNzs= -k8s.io/api v0.0.0-20181204000039-89a74a8d264d/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= -k8s.io/apimachinery v0.0.0-20181127025237-2b1284ed4c93 h1:tT6oQBi0qwLbbZSfDkdIsb23EwaLY85hoAV4SpXfdao= -k8s.io/apimachinery v0.0.0-20181127025237-2b1284ed4c93/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= -k8s.io/client-go v10.0.0+incompatible h1:F1IqCqw7oMBzDkqlcBymRq1450wD0eNqLE9jzUrIi34= -k8s.io/client-go v10.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= -k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v0.4.0 h1:lCJCxf/LIowc2IGS9TPjWDyXY4nOmdGdfcwwDQCOURQ= -k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= -k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf h1:EYm5AW/UUDbnmnI+gK0TJDVK9qPLhM+sRHYanNKw0EQ= -k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= -sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= -sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/vice-president/p12_to_key_and_cert.sh b/vice-president/p12_to_key_and_cert.sh deleted file mode 100755 index cd0996932..000000000 --- a/vice-president/p12_to_key_and_cert.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -if [[ "$#" -ne 1 ]]; then - echo "usage: p12_to_key_and_cert "; - exit 1; -fi -openssl pkcs12 -in $(pwd)/$1 -nocerts -nodes -out $(pwd)/$1.key -openssl pkcs12 -in $(pwd)/$1 -clcerts -nokeys -nodes -out $(pwd)/$1.pem diff --git a/vice-president/pkg/config/config.go b/vice-president/pkg/config/config.go deleted file mode 100644 index ae496e6bc..000000000 --- a/vice-president/pkg/config/config.go +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************* -* -* Copyright 2019 SAP SE -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You should have received a copy of the License along with this -* program. If not, you may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -*******************************************************************************/ - -package config - -import ( - "fmt" - "io/ioutil" - - "gopkg.in/yaml.v1" -) - -// VicePresidentConfig is a wrapper for both parts of the config. -type VicePresidentConfig struct { - ViceConfig `yaml:"vice"` -} - -// ViceConfig to define the parameters when talking to the Symantec VICE API. -type ViceConfig struct { - FirstName string `yaml:"first_name"` - LastName string `yaml:"last_name"` - EMail string `yaml:"email"` - Country string `yaml:"country"` - Province string `yaml:"province"` - Locality string `yaml:"locality"` - Organization string `yaml:"organization"` - OrganizationalUnit string `yaml:"organizational_unit"` - DefaultChallenge string `yaml:"default_challenge"` -} - -// ReadConfig reads a given vice configuration file and returns the ViceConfig object and if applicable an error. -func ReadConfig(filePath string) (cfg VicePresidentConfig, err error) { - cfgBytes, err := ioutil.ReadFile(filePath) - if err != nil { - return cfg, fmt.Errorf("read configuration file: %s", err.Error()) - } - err = yaml.Unmarshal(cfgBytes, &cfg) - if err != nil { - return cfg, fmt.Errorf("parse configuration: %s", err.Error()) - } - return cfg, nil -} diff --git a/vice-president/pkg/config/options.go b/vice-president/pkg/config/options.go deleted file mode 100644 index 8855ee48f..000000000 --- a/vice-president/pkg/config/options.go +++ /dev/null @@ -1,76 +0,0 @@ -/******************************************************************************* -* -* Copyright 2019 SAP SE -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You should have received a copy of the License along with this -* program. If not, you may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -*******************************************************************************/ - -package config - -import ( - "fmt" - "os" - "time" -) - -// Options to configure your vice president. -type Options struct { - ResyncInterval time.Duration - CertificateCheckInterval time.Duration - KubeConfig string - VicePresidentConfig string - ViceKeyFile string - ViceCrtFile string - IntermediateCertificate string - RootCACertificate string - Namespace string - EnableValidateRemoteCertificate bool - IsEnableAdditionalSymantecMetrics bool - IsDebug bool - RateLimit int - Threadiness int - MinCertValidityDays int - MetricPort int - Finalizer string - EventComponent string - IngressAnnotation string -} - -// CheckOptions verifies the Options and sets default values if necessary. -func (o *Options) CheckOptions() error { - if o.ViceCrtFile == "" { - return fmt.Errorf("path to vice certificate not provided. Aborting") - } - if o.ViceKeyFile == "" { - return fmt.Errorf("path to vice key not provided. Aborting") - } - if o.VicePresidentConfig == "" { - return fmt.Errorf("path to vice config not provided. Aborting") - } - if o.MinCertValidityDays <= 0 { - o.MinCertValidityDays = 30 - } - if o.MetricPort == 0 { - o.MetricPort = 9091 - } - if o.RateLimit <= 0 { - // Unlimited power! - o.RateLimit = -1 - } - if o.IsDebug { - os.Setenv("DEBUG", "true") - } - return nil -} diff --git a/vice-president/pkg/k8sutils/framework.go b/vice-president/pkg/k8sutils/framework.go deleted file mode 100644 index 3e81c0c4b..000000000 --- a/vice-president/pkg/k8sutils/framework.go +++ /dev/null @@ -1,398 +0,0 @@ -package k8sutils - -import ( - "context" - "fmt" - "time" - - "github.com/pkg/errors" - "github.com/sapcc/kubernetes-operators/vice-president/pkg/config" - "github.com/sapcc/kubernetes-operators/vice-president/pkg/log" - corev1 "k8s.io/api/core/v1" - extensionsv1beta1 "k8s.io/api/extensions/v1beta1" - apiErrors "k8s.io/apimachinery/pkg/api/errors" - apimetav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - apimachineryWatch "k8s.io/apimachinery/pkg/watch" - corev1Informers "k8s.io/client-go/informers/core/v1" - v1beta1Informers "k8s.io/client-go/informers/extensions/v1beta1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/scheme" - v12 "k8s.io/client-go/kubernetes/typed/core/v1" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/tools/record" - "k8s.io/client-go/tools/watch" -) - -// WaitTimeout is the time we're waiting before considering an operation failed. -const WaitTimeout = 2 * time.Minute - -// K8sFramework ... -type K8sFramework struct { - *kubernetes.Clientset - logger log.Logger - finalizer string - eventRecorder record.EventRecorder - ingressInformer cache.SharedIndexInformer - secretInformer cache.SharedIndexInformer -} - -// NewK8sFramework returns a new k8s framework. -func NewK8sFramework(opts config.Options, logger log.Logger) (*K8sFramework, error) { - logger = log.NewLoggerWith(logger, "component", "K8sFramework") - - rules := clientcmd.NewDefaultClientConfigLoadingRules() - overrides := &clientcmd.ConfigOverrides{} - if opts.KubeConfig != "" { - rules.ExplicitPath = opts.KubeConfig - } - - config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(rules, overrides).ClientConfig() - if err != nil { - return nil, err - } - - clientset, err := kubernetes.NewForConfig(config) - if err != nil { - return nil, err - } - - ingressInformer := v1beta1Informers.NewIngressInformer( - clientset, - opts.Namespace, - opts.ResyncInterval, - cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, - ) - - secretInformer := corev1Informers.NewSecretInformer( - clientset, - opts.Namespace, - opts.ResyncInterval, - cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, - ) - - b := record.NewBroadcaster() - b.StartLogging(logger.LogEvent) - b.StartRecordingToSink(&v12.EventSinkImpl{ - Interface: clientset.CoreV1().Events(apimetav1.NamespaceAll), - }) - eventRecorder := b.NewRecorder(scheme.Scheme, corev1.EventSource{ - Component: opts.EventComponent, - }) - - return &K8sFramework{ - Clientset: clientset, - logger: logger, - finalizer: opts.Finalizer, - eventRecorder: eventRecorder, - ingressInformer: ingressInformer, - secretInformer: secretInformer, - }, nil -} - -// Run starts the informers. -func (k8s *K8sFramework) Run(stopCh <-chan struct{}) { - go k8s.ingressInformer.Run(stopCh) - go k8s.secretInformer.Run(stopCh) -} - -// WaitForCacheSync returns true if all informer caches have been synced. -func (k8s *K8sFramework) WaitForCacheSync(stopCh <-chan struct{}) bool { - return cache.WaitForCacheSync( - stopCh, - k8s.ingressInformer.HasSynced, - k8s.secretInformer.HasSynced, - ) -} - -// AddIngressInformerEventHandler adds event handlers to the ingress informer. -func (k8s *K8sFramework) AddIngressInformerEventHandler(addFunc, deleteFunc func(obj interface{}), updateFunc func(oldObj, newObj interface{})) { - k8s.ingressInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: addFunc, - UpdateFunc: updateFunc, - DeleteFunc: deleteFunc, - }) -} - -// AddSecretInformerEventHandler adds event handlers to the secret informer. -func (k8s *K8sFramework) AddSecretInformerEventHandler(addFunc, deleteFunc func(obj interface{}), updateFunc func(oldObj, newObj interface{})) { - k8s.secretInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: addFunc, - UpdateFunc: updateFunc, - DeleteFunc: deleteFunc, - }) -} - -// Eventf emits an event via the event recorder. -func (k8s *K8sFramework) Eventf(object runtime.Object, eventType, reason, messageFmt string, args ...interface{}) { - k8s.eventRecorder.Eventf(object, eventType, reason, messageFmt, args) -} - -// GetIngressFromIndexerByKey gets an ingress from the ingress informer indexer by key. -func (k8s *K8sFramework) GetIngressFromIndexerByKey(key string) (interface{}, bool, error) { - return k8s.ingressInformer.GetIndexer().GetByKey(key) -} - -// GetIngressInformerStore returns a the store of the ingress informer. -func (k8s *K8sFramework) GetIngressInformerStore() cache.Store { - return k8s.ingressInformer.GetStore() -} - -// GetOrCreateSecret returns an existing Secret or creates it if not found and waits until the operation times out or is completed. -func (k8s *K8sFramework) GetOrCreateSecret(namespace, name string, labels, annotations map[string]string) (*corev1.Secret, error) { - secret, err := k8s.CoreV1().Secrets(namespace).Get(name, metaV1.GetOptions{}) - if err != nil { - // Create secret if not found. - if apiErrors.IsNotFound(err) { - k8s.logger.LogInfo("creating secret", "key", fmt.Sprintf("%s/%s", namespace, name)) - secret = newEmptySecret(namespace, name, labels, annotations) - if err := k8s.CreateSecret(secret); err != nil { - return nil, err - } - return k8s.CoreV1().Secrets(namespace).Get(name, metaV1.GetOptions{}) - } - // Return any other error. - return secret, errors.Wrapf(err, "secret %s/%s exists but failed to get it", namespace, name) - } - return secret, err -} - -// CreateSecret create the given Secret and waits until the operation times out or is completed. -func (k8s *K8sFramework) CreateSecret(secret *corev1.Secret) error { - newSecret, err := k8s.CoreV1().Secrets(secret.GetNamespace()).Create(secret) - if err != nil { - return err - } - - k8s.logger.LogDebug("added upstream secret", "secret", keyFunc(secret)) - - return k8s.waitForUpstreamSecret(newSecret, isSecretAddedOrModified) -} - -// UpdateSecret updates an existing Secret and waits until the operation times out or is completed. -func (k8s *K8sFramework) UpdateSecret(oldSecret, newSecret *corev1.Secret, conditionFuncs ...watch.ConditionFunc) error { - oldSecret, err := k8s.CoreV1().Secrets(oldSecret.GetNamespace()).Get(oldSecret.GetName(), metaV1.GetOptions{}) - if err != nil { - return err - } - - // Nothing to update. - if !isSecretNeedsUpdate(oldSecret, newSecret) { - return nil - } - - updatedSecret, err := k8s.CoreV1().Secrets(oldSecret.GetNamespace()).Update(newSecret) - if err != nil { - return err - } - - k8s.logger.LogDebug("updating secret", "secret", keyFunc(updatedSecret)) - - if conditionFuncs == nil { - conditionFuncs = []watch.ConditionFunc{isSecretAddedOrModified} - } - - return k8s.waitForUpstreamSecret(updatedSecret, conditionFuncs...) -} - -// DeleteSecret deletes an existing secret and waits until the operation times out or is completed. -func (k8s *K8sFramework) DeleteSecret(secret *corev1.Secret) error { - if err := k8s.CoreV1().Secrets(secret.GetNamespace()).Delete(secret.GetName(), &metaV1.DeleteOptions{}); err != nil { - return err - } - - k8s.logger.LogDebug("deleting secret", "secret", keyFunc(secret)) - - return k8s.waitForUpstreamSecret(secret, isSecretDeleted) -} - -// RemoveSecretAnnotation removes an annotation from the given secret. -func (k8s *K8sFramework) RemoveSecretAnnotation(secret *corev1.Secret, annotation string) error { - secret, err := k8s.Clientset.CoreV1().Secrets(secret.GetNamespace()).Get(secret.GetName(), metaV1.GetOptions{}) - if err != nil { - return err - } - - newSecret := secret.DeepCopy() - annotations := newSecret.GetAnnotations() - delete(annotations, annotation) - newSecret.Annotations = annotations - - return k8s.UpdateSecret( - secret, newSecret, - func(event apimachineryWatch.Event) (bool, error) { - switch event.Type { - case apimachineryWatch.Deleted: - return false, apiErrors.NewNotFound(schema.GroupResource{Resource: "secret"}, secret.GetName()) - } - switch secret := event.Object.(type) { - case *corev1.Secret: - return !secretHasAnnotation(secret, annotation), nil - } - return false, nil - }, - ) -} - -// GetIngress returns the ingress or an error. -func (k8s *K8sFramework) GetIngress(namespace, name string) (*extensionsv1beta1.Ingress, error) { - return k8s.ExtensionsV1beta1().Ingresses(namespace).Get(name, metaV1.GetOptions{}) -} - -// UpdateIngress updates an existing Ingress and waits until the operation times out or is completed. -func (k8s *K8sFramework) UpdateIngress(oldIngress, newIngress *extensionsv1beta1.Ingress, conditionFuncs ...watch.ConditionFunc) error { - oldIngress, err := k8s.ExtensionsV1beta1().Ingresses(oldIngress.GetNamespace()).Get(oldIngress.GetName(), metaV1.GetOptions{}) - if err != nil { - return err - } - - // Nothing to update. - if !isIngressNeedsUpdate(oldIngress, newIngress) { - return nil - } - - updatedIngress, err := k8s.ExtensionsV1beta1().Ingresses(oldIngress.GetNamespace()).Update(newIngress) - if err != nil { - return err - } - - k8s.logger.LogDebug("updating ingress", "ingress", keyFunc(updatedIngress)) - - if conditionFuncs == nil { - conditionFuncs = []watch.ConditionFunc{isIngressAddedOrModified} - } - - return k8s.waitForUpstreamIngress(updatedIngress, conditionFuncs...) -} - -// RemoveIngressAnnotation removes the given annotation from an ingress and waits until operation times out or is completed. -func (k8s *K8sFramework) RemoveIngressAnnotation(ingress *extensionsv1beta1.Ingress, annotation string) error { - ingress, err := k8s.GetIngress(ingress.GetNamespace(), ingress.GetName()) - if err != nil { - return err - } - - newIngress := ingress.DeepCopy() - annotations := newIngress.GetAnnotations() - delete(annotations, annotation) - newIngress.Annotations = annotations - - k8s.logger.LogDebug("removing annotation from ingress", "ingress", keyFunc(ingress), "annotation", annotation) - - return k8s.UpdateIngress( - ingress, newIngress, - func(event apimachineryWatch.Event) (bool, error) { - switch event.Type { - case apimachineryWatch.Deleted: - return false, apiErrors.NewNotFound(schema.GroupResource{Resource: "ingress"}, "") - } - switch ing := event.Object.(type) { - case *extensionsv1beta1.Ingress: - return !IngressHasAnnotation(ing, annotation), nil - } - return false, nil - }, - ) -} - -// EnsureVicePresidentFinalizerExists ensures the Finalizer exists on the given Ingress or returns an error. -func (k8s *K8sFramework) EnsureVicePresidentFinalizerExists(ingress *extensionsv1beta1.Ingress) error { - // Add finalizer if not present and ingress was not deleted. - if !IngressHasFinalizer(ingress, k8s.finalizer) && !IngressHasDeletionTimestamp(ingress) { - newIngress := ingress.DeepCopy() - newIngress.Finalizers = append(newIngress.GetFinalizers(), k8s.finalizer) - - k8s.logger.LogDebug("adding finalizer to ingress", "ingress", keyFunc(ingress), "finalizer", k8s.finalizer) - - return k8s.UpdateIngress( - ingress, newIngress, - func(event apimachineryWatch.Event) (bool, error) { - switch event.Type { - case apimachineryWatch.Deleted: - return false, apiErrors.NewNotFound(schema.GroupResource{Resource: "ingress"}, "") - } - switch ing := event.Object.(type) { - case *extensionsv1beta1.Ingress: - return IngressHasFinalizer(ing, k8s.finalizer), nil - } - return false, nil - }, - ) - } - return nil -} - -// EnsureVicePresidentFinalizerRemoved ensures the Finalizer was removed from the given Ingress or returns an error. -func (k8s *K8sFramework) EnsureVicePresidentFinalizerRemoved(ingress *extensionsv1beta1.Ingress) error { - // Do not remove finalizer if DeletionTimestamp is not set. - if IngressHasFinalizer(ingress, k8s.finalizer) && IngressHasDeletionTimestamp(ingress) { - newIngress := ingress.DeepCopy() - for i, fin := range newIngress.GetFinalizers() { - if fin == k8s.finalizer { - // Delete but preserve order. - newIngress.Finalizers = append(newIngress.Finalizers[:i], newIngress.Finalizers[i+1:]...) - - k8s.logger.LogDebug("removing finalizer from ingress", "ingress", keyFunc(ingress), "finalizer", k8s.finalizer) - - return k8s.UpdateIngress( - ingress, newIngress, - func(event apimachineryWatch.Event) (bool, error) { - switch event.Type { - case apimachineryWatch.Deleted: - return false, apiErrors.NewNotFound(schema.GroupResource{Resource: "ingress"}, "") - } - switch ing := event.Object.(type) { - case *extensionsv1beta1.Ingress: - return !IngressHasFinalizer(ing, k8s.finalizer), nil - } - return false, nil - }, - ) - } - } - } - return nil -} - -// waitForUpstreamSecret watches the given secret and wait for max. t minutes until the given condition applies. -func (k8s *K8sFramework) waitForUpstreamSecret(secret *corev1.Secret, conditionFuncs ...watch.ConditionFunc) error { - ctx, _ := context.WithTimeout(context.TODO(), WaitTimeout) - _, err := watch.UntilWithSync( - ctx, - &cache.ListWatch{ - ListFunc: func(options metaV1.ListOptions) (object runtime.Object, e error) { - return k8s.CoreV1().Secrets(secret.GetNamespace()).List(metaV1.SingleObject(metaV1.ObjectMeta{Name: secret.GetName()})) - }, - WatchFunc: func(options metaV1.ListOptions) (i apimachineryWatch.Interface, e error) { - return k8s.CoreV1().Secrets(secret.GetNamespace()).Watch(metaV1.SingleObject(metaV1.ObjectMeta{Name: secret.GetName()})) - }, - }, - secret, - nil, - conditionFuncs..., - ) - return err -} - -// waitForUpstreamIngress watches the given Ingress and wait for max. t minutes until the given condition applies. -func (k8s *K8sFramework) waitForUpstreamIngress(ingress *extensionsv1beta1.Ingress, conditionFuncs ...watch.ConditionFunc) error { - ctx, _ := context.WithTimeout(context.TODO(), WaitTimeout) - _, err := watch.UntilWithSync( - ctx, - &cache.ListWatch{ - ListFunc: func(options metaV1.ListOptions) (object runtime.Object, e error) { - return k8s.ExtensionsV1beta1().Ingresses(ingress.GetNamespace()).List(metaV1.SingleObject(metaV1.ObjectMeta{Name: ingress.GetName()})) - }, - WatchFunc: func(options metaV1.ListOptions) (i apimachineryWatch.Interface, e error) { - return k8s.ExtensionsV1beta1().Ingresses(ingress.GetNamespace()).Watch(metaV1.SingleObject(metaV1.ObjectMeta{Name: ingress.GetName()})) - }, - }, - ingress, - nil, - conditionFuncs..., - ) - return err -} diff --git a/vice-president/pkg/k8sutils/ingress.go b/vice-president/pkg/k8sutils/ingress.go deleted file mode 100644 index 4da0d7cf9..000000000 --- a/vice-president/pkg/k8sutils/ingress.go +++ /dev/null @@ -1,96 +0,0 @@ -/******************************************************************************* -* -* Copyright 2019 SAP SE -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You should have received a copy of the License along with this -* program. If not, you may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -*******************************************************************************/ - -package k8sutils - -import ( - "reflect" - - extensionsv1beta1 "k8s.io/api/extensions/v1beta1" - apiErrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/watch" -) - -// IngressHasAnnotation checks whether the given ingres has the annotation. -func IngressHasAnnotation(ingress *extensionsv1beta1.Ingress, annotation string) bool { - if val, ok := ingress.GetAnnotations()[annotation]; ok { - return val == "true" - } - return false -} - -// IsLastHostInIngressSpec checks if 'hostName' is the last host in ingress.Spec.TLS -func IsLastHostInIngressSpec(ingress *extensionsv1beta1.Ingress, hostName string) bool { - lastHost := ingress.Spec.TLS[len(ingress.Spec.TLS)-1].Hosts - if lastHost != nil && len(lastHost) >= 1 { - // CN is lastHost[0], SANs are lastHost[1:] - return lastHost[0] == hostName - } - return false -} - -// IngressGetSecretKeysFromAnnotation .... -func IngressGetSecretKeysFromAnnotation(ingress *extensionsv1beta1.Ingress, defaultSecretTLSKeyType, defaultSecretTLSCertType string) (tlsKeySecretKey, tlsCertSecretKey string) { - tlsKeySecretKey = defaultSecretTLSKeyType - tlsCertSecretKey = defaultSecretTLSCertType - - if keySecretKey, ok := ingress.GetAnnotations()["vice-president/tls-key-secret-key"]; ok { - tlsKeySecretKey = keySecretKey - } - if certSecretkey, ok := ingress.GetAnnotations()["vice-president/tls-cert-secret-key"]; ok { - tlsCertSecretKey = certSecretkey - } - return tlsKeySecretKey, tlsCertSecretKey -} - -// IngressHasDeletionTimestamp checks whether the ingress has a deletion timestamp set. -func IngressHasDeletionTimestamp(ingress *extensionsv1beta1.Ingress) bool { - return ingress.GetDeletionTimestamp() != nil -} - -// IngressHasFinalizer checks whether the given ingress has a finalizer. -func IngressHasFinalizer(ingress *extensionsv1beta1.Ingress, finalizer string) bool { - for _, fin := range ingress.GetFinalizers() { - if fin == finalizer { - return true - } - } - return false -} - -func isIngressAddedOrModified(event watch.Event) (bool, error) { - switch event.Type { - case watch.Deleted: - return false, apiErrors.NewNotFound(schema.GroupResource{Resource: "ingress"}, "") - case watch.Added, watch.Modified: - return true, nil - default: - return false, nil - } - return false, nil -} - -func isIngressNeedsUpdate(old, new *extensionsv1beta1.Ingress) bool { - // Ingress needs update if spec or annotations changed or deletionTimestamp was added. - if !reflect.DeepEqual(old.Spec, new.Spec) || !reflect.DeepEqual(old.GetAnnotations(), new.GetAnnotations()) || !reflect.DeepEqual(old.GetDeletionTimestamp(), new.GetDeletionTimestamp()) { - return true - } - return false -} diff --git a/vice-president/pkg/k8sutils/ingress_test.go b/vice-president/pkg/k8sutils/ingress_test.go deleted file mode 100644 index 7e0ea64f5..000000000 --- a/vice-president/pkg/k8sutils/ingress_test.go +++ /dev/null @@ -1,69 +0,0 @@ -/******************************************************************************* -* -* Copyright 2019 SAP SE -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You should have received a copy of the License along with this -* program. If not, you may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -*******************************************************************************/ - -package k8sutils - -import ( - "testing" - - "github.com/stretchr/testify/assert" - extensionsv1beta1 "k8s.io/api/extensions/v1beta1" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func createIngress() *extensionsv1beta1.Ingress { - return &extensionsv1beta1.Ingress{ - ObjectMeta: metaV1.ObjectMeta{ - Namespace: "default", - Name: "myIngress", - Annotations: map[string]string{ - "vice-president": "true", - "vice-president/replace-cert": "true", - }, - }, - Spec: extensionsv1beta1.IngressSpec{ - TLS: []extensionsv1beta1.IngressTLS{ - { - Hosts: []string{"a.com"}, - SecretName: "tls-a-com", - }, - { - Hosts: []string{"b.com"}, - SecretName: "tls-b-com", - }, - { - Hosts: []string{"lasthost.com", "foobar.com"}, - SecretName: "tls-lasthost-com", - }, - }, - }, - } -} - -func TestIsLastHostInIngressSpec(t *testing.T) { - lastHost := "lasthost.com" - ingress := createIngress() - - assert.False(t, IsLastHostInIngressSpec(ingress, ""), "is not the last host in the ingress spec") - assert.Truef(t, IsLastHostInIngressSpec(ingress, lastHost), "'%s' is the last host in the ingress.spec.tls", lastHost) -} - -func TestIsCertificateForHostShouldBeReplaced(t *testing.T) { - assert.True(t, IngressHasAnnotation(createIngress(), "vice-president/replace-cert")) -} diff --git a/vice-president/pkg/k8sutils/secret.go b/vice-president/pkg/k8sutils/secret.go deleted file mode 100644 index a43ff48e1..000000000 --- a/vice-president/pkg/k8sutils/secret.go +++ /dev/null @@ -1,82 +0,0 @@ -/******************************************************************************* -* -* Copyright 2019 SAP SE -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You should have received a copy of the License along with this -* program. If not, you may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -*******************************************************************************/ - -package k8sutils - -import ( - coreV1 "k8s.io/api/core/v1" - apiErrors "k8s.io/apimachinery/pkg/api/errors" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/watch" - "reflect" -) - -func newEmptySecret(nameSpace, name string, labels, annotations map[string]string) *coreV1.Secret { - if labels == nil { - labels = map[string]string{} - } - if annotations == nil { - annotations = map[string]string{} - } - return &coreV1.Secret{ - Type: coreV1.SecretTypeOpaque, - ObjectMeta: metaV1.ObjectMeta{ - Name: name, - Namespace: nameSpace, - Labels: labels, - Annotations: annotations, - }, - Data: map[string][]byte{}, - } -} - -func secretHasAnnotation(secret *coreV1.Secret, annotation string) bool { - _, ok := secret.GetAnnotations()[annotation] - return ok -} - -func isSecretAddedOrModified(event watch.Event) (bool, error) { - switch event.Type { - case watch.Deleted: - return false, apiErrors.NewNotFound(schema.GroupResource{Resource: "secret"}, "") - case watch.Added, watch.Modified: - return true, nil - default: - return false, nil - } - return false, nil -} - -func isSecretDeleted(event watch.Event) (bool, error) { - switch event.Type { - case watch.Deleted: - return true, nil - default: - return false, nil - } - return false, nil -} - -func isSecretNeedsUpdate(old, new *coreV1.Secret) bool { - if !reflect.DeepEqual(old.Data, new.Data) || !reflect.DeepEqual(old.Annotations, new.Annotations) { - return true - } - return false -} diff --git a/vice-president/pkg/k8sutils/util.go b/vice-president/pkg/k8sutils/util.go deleted file mode 100644 index 4b3546b1f..000000000 --- a/vice-president/pkg/k8sutils/util.go +++ /dev/null @@ -1,27 +0,0 @@ -/******************************************************************************* -* -* Copyright 2019 SAP SE -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You should have received a copy of the License along with this -* program. If not, you may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -*******************************************************************************/ - -package k8sutils - -import "k8s.io/client-go/tools/cache" - -func keyFunc(obj interface{}) string { - key, _ := cache.MetaNamespaceKeyFunc(obj) - return key -} diff --git a/vice-president/pkg/log/log.go b/vice-president/pkg/log/log.go deleted file mode 100644 index 64b963928..000000000 --- a/vice-president/pkg/log/log.go +++ /dev/null @@ -1,88 +0,0 @@ -/******************************************************************************* -* -* Copyright 2019 SAP SE -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You should have received a copy of the License along with this -* program. If not, you may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -*******************************************************************************/ - -package log - -import ( - "os" - - "github.com/go-kit/kit/log" - "github.com/go-kit/kit/log/level" -) - -// Logger ... -type Logger struct { - logger log.Logger -} - -// NewLogger creates a new Logger. -func NewLogger(isDebug bool) Logger { - logLevel := level.AllowInfo() - if isDebug { - logLevel = level.AllowDebug() - } - var l log.Logger - l = log.NewLogfmtLogger(os.Stdout) - l = level.NewFilter(l, logLevel) - l = log.With(l, "ts", log.DefaultTimestampUTC, "caller", log.Caller(4)) - - return Logger{ - logger: l, - } -} - -// NewLoggerWith return a new Logger with additional keyvals. -func NewLoggerWith(logger Logger, keyvals ...interface{}) Logger { - return Logger{ - logger: log.With(logger.logger, keyvals...), - } -} - -// LogInfo logs info messages. -func (l *Logger) LogInfo(msg string, keyvals ...interface{}) { - level.Info(l.logger).Log(append([]interface{}{"msg", msg}, keyvals...)...) -} - -// LogDebug logs debug messages. -func (l *Logger) LogDebug(msg string, keyvals ...interface{}) { - level.Debug(l.logger).Log(append([]interface{}{"msg", msg}, keyvals...)...) -} - -// LogError logs error messages. -func (l *Logger) LogError(msg string, err error, keyvals ...interface{}) { - // prepend message and append err - keyvals = append([]interface{}{"msg", msg}, keyvals...) - level.Error(l.logger).Log(append(keyvals, []interface{}{"err", err}...)...) -} - -// LogWarn logs warning messages. -func (l *Logger) LogWarn(msg string, keyvals ...interface{}) { - level.Warn(l.logger).Log(append([]interface{}{"msg", msg}, keyvals...)...) -} - -// LogFatal logs fatal messages and exits. -func (l *Logger) LogFatal(msg string, keyvals ...interface{}) { - level.Error(l.logger).Log(append([]interface{}{"msg", msg}, keyvals...)...) - os.Exit(1) -} - -// LogEvent logs events. -func (l *Logger) LogEvent(format string, obj ...interface{}) { - level.Debug(l.logger).Log(append([]interface{}{"event", format}, obj...)...) -} diff --git a/vice-president/pkg/president/certificate.go b/vice-president/pkg/president/certificate.go deleted file mode 100644 index 9bc41f268..000000000 --- a/vice-president/pkg/president/certificate.go +++ /dev/null @@ -1,286 +0,0 @@ -/******************************************************************************* -* -* Copyright 2019 SAP SE -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You should have received a copy of the License along with this -* program. If not, you may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -*******************************************************************************/ - -package president - -import ( - "bytes" - "crypto/rsa" - "crypto/tls" - "crypto/x509" - "encoding/pem" - "fmt" - "io/ioutil" - "net" - "net/http" - "sort" - "strings" - "time" - - "github.com/pkg/errors" - "golang.org/x/crypto/ocsp" - extensionsv1beta1 "k8s.io/api/extensions/v1beta1" -) - -// ViceCertificate contains all properties requires by the Symantec VICE API -type ViceCertificate struct { - roots *x509.CertPool - intermediateCertificate *x509.Certificate - certificate *x509.Certificate - privateKey *rsa.PrivateKey - csr []byte - host string - sans []string - tid string - caCertificate *x509.Certificate - ocspServers []string - ingress *extensionsv1beta1.Ingress - secretName string -} - -// NewViceCertificate returns a new vice certificate. -func NewViceCertificate(ingress *extensionsv1beta1.Ingress, secretName, host string, sans []string, intermediateCertificate *x509.Certificate, rootCertificates *x509.CertPool) *ViceCertificate { - vc := &ViceCertificate{ - ingress: ingress, - secretName: secretName, - intermediateCertificate: intermediateCertificate, - roots: rootCertificates, - host: host, - } - vc.setSANs(sans) - return vc -} - -// DoesCertificateAndHostMatch checks that a given certificate is for the correct host and SANs. -func (vc *ViceCertificate) DoesCertificateAndHostMatch() bool { - if err := vc.certificate.VerifyHostname(vc.host); err != nil { - return false - } - - return isStringSlicesEqual(vc.getSANs(), vc.certificate.DNSNames) -} - -// DoesCertificateExpireSoon checks if a certificate is already expired or will expire within the next n month? -func (vc *ViceCertificate) DoesCertificateExpireSoon(minCertValidityDays int) bool { - certExpiry := vc.certificate.NotAfter.UTC() - shouldBeValidUntil := time.Now().UTC().AddDate(0, 0, minCertValidityDays) - return certExpiry.Before(shouldBeValidUntil) -} - -// DoesKeyAndCertificateTally checks if a given private key is for the correct certificate. -func (vc *ViceCertificate) DoesKeyAndCertificateTally() bool { - certBlock := pem.Block{ - Type: CertificateType, - Bytes: vc.certificate.Raw, - } - - keyBlock := pem.Block{ - Type: PrivateKeyType, - Bytes: x509.MarshalPKCS1PrivateKey(vc.privateKey), - } - - if _, err := tls.X509KeyPair(pem.EncodeToMemory(&certBlock), pem.EncodeToMemory(&keyBlock)); err != nil { - return false - } - return true -} - -// DoesRemoteCertificateMatch connects to the URL, does the TLS handshake and checks if the certificates match -func (vc *ViceCertificate) DoesRemoteCertificateMatch() bool { - conn, err := tls.DialWithDialer( - &net.Dialer{Timeout: 2 * time.Second}, - "tcp", - fmt.Sprintf("%v:%v", vc.host, 443), - &tls.Config{InsecureSkipVerify: true}, - ) - if err != nil { - return true - } - defer conn.Close() - - remoteCert := conn.ConnectionState().PeerCertificates[0] - - // in case we get the kubernetes fake certificate from the ingress controller break here and do nothing but log this - if isIngressFakeCertificate(remoteCert) { - // TODO: surface this properly - return true - } - - return vc.compareRemoteCert(remoteCert) -} - -// getSANs returns the SANs of the certificate. Also checks if the common name is part of the SANs. -func (vc *ViceCertificate) getSANs() []string { - if vc.sans == nil { - vc.sans = []string{} - } - if contains(vc.sans, vc.host) != true { - vc.sans = append(vc.sans, vc.host) - } - return vc.sans -} - -// getSANsString returns the concatenated list of SANs. -func (vc *ViceCertificate) getSANsString() string { - return strings.Join(vc.getSANs(), ",") -} - -// setSANs set the SANs of the certificate. Also checks if the common name is part of the SANs. -func (vc *ViceCertificate) setSANs(sans []string) { - if contains(sans, vc.host) != true { - sans = append(sans, vc.host) - } - if vc.sans == nil { - vc.sans = sans - } else { - vc.sans = append(vc.sans, sans...) - } -} - -// withIntermediateCertificate returns the certificate chain. -func (vc *ViceCertificate) withIntermediateCertificate() []*x509.Certificate { - if vc.intermediateCertificate != nil { - return []*x509.Certificate{ - vc.certificate, - vc.intermediateCertificate, - } - } - return []*x509.Certificate{ - vc.certificate, - } -} - -// IsRevoked checks whether the certificate was revoked using OCSP (Online Certificate Status Protocol) -func (vc *ViceCertificate) IsRevoked() bool { - if err := vc.getRootCA(); err != nil { - return false - } - - if err := vc.getOCSPURIs(); err != nil { - return false - } - - for _, uri := range vc.ocspServers { - ocspResponse, err := vc.issueOCSPRequest(uri) - if err != nil { - return false - } - - if ocspResponse.Status == ocsp.Revoked { - return true - } - } - - return false -} - -func (vc *ViceCertificate) issueOCSPRequest(ocspURI string) (*ocsp.Response, error) { - ocspRequest, err := ocsp.CreateRequest(vc.certificate, vc.caCertificate, &ocsp.RequestOptions{}) - if err != nil { - return nil, errors.Wrap(err, "failed to create OCSP request") - } - - ocspRequestReader := bytes.NewReader(ocspRequest) - httpResponse, err := http.Post(ocspURI, "application/ocsp-request", ocspRequestReader) - if err != nil { - return nil, errors.Wrap(err, "OCSP request failed") - } - defer httpResponse.Body.Close() - - ocspResponseBytes, err := ioutil.ReadAll(httpResponse.Body) - if err != nil { - return nil, errors.Wrap(err, "failed to read request body") - } - return ocsp.ParseResponse(ocspResponseBytes, vc.caCertificate) -} - -func (vc *ViceCertificate) getOCSPURIs() error { - ocspServers := vc.certificate.OCSPServer - if ocspServers != nil { - vc.ocspServers = ocspServers - return nil - } - return fmt.Errorf("failed to get OCSP URIs. certificate OCSP URI is %v", ocspServers) -} - -func (vc *ViceCertificate) getRootCA() error { - caIssuerURIList := vc.certificate.IssuingCertificateURL - if caIssuerURIList == nil || len(caIssuerURIList) == 0 { - return fmt.Errorf("failed to get CA Issuer URI. certificate CA Issuer URI is %v", vc.certificate.IssuingCertificateURL) - } - - caIssuerURI := caIssuerURIList[0] - fileName := getFileNameFromURI(caIssuerURI) - filePath := TmpPath + fileName - - var certByte []byte - certByte, err := ioutil.ReadFile(filePath) - if err != nil { - certByte, err = downloadAndPersistFile(caIssuerURI, filePath) - if err != nil { - return err - } - } - - ca, err := x509.ParseCertificate(certByte) - if err != nil { - return err - } - - vc.caCertificate = ca - return nil -} - -func (vc *ViceCertificate) compareRemoteCert(remoteCert *x509.Certificate) bool { - // FIXME: remoteCert.Equal(vc.certificate) is too error-prone :-/ - - if vc.host != remoteCert.Subject.CommonName { - return false - } - - sort.Strings(vc.getSANs()) - gotSANs := sort.StringSlice(remoteCert.DNSNames) - sort.Strings(gotSANs) - if !isStringSlicesEqual(vc.getSANs(), gotSANs) { - return false - } - - if !vc.certificate.NotBefore.UTC().Equal(remoteCert.NotBefore.UTC()) { - return false - } - - if !vc.certificate.NotAfter.UTC().Equal(remoteCert.NotAfter.UTC()) { - return false - } - - return true -} - -func (vc *ViceCertificate) getIngressKey() string { - return fmt.Sprintf("%s/%s", vc.ingress.GetNamespace(), vc.ingress.GetName()) -} - -func (vc *ViceCertificate) getSecretKey() string { - return fmt.Sprintf("%s/%s", vc.ingress.GetNamespace(), vc.secretName) -} - -// isIngressFakeCertificate determines whether the remote certificate is the fake certificate send by the ingress controller -func isIngressFakeCertificate(certificate *x509.Certificate) bool { - return certificate.Subject.CommonName == IngressFakeCN && contains(certificate.DNSNames, IngressFakeHost) -} diff --git a/vice-president/pkg/president/certificate_test.go b/vice-president/pkg/president/certificate_test.go deleted file mode 100644 index 637ee5043..000000000 --- a/vice-president/pkg/president/certificate_test.go +++ /dev/null @@ -1,135 +0,0 @@ -/******************************************************************************* -* -* Copyright 2019 SAP SE -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You should have received a copy of the License along with this -* program. If not, you may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -*******************************************************************************/ - -package president - -import ( - "crypto/rand" - "crypto/rsa" - "io/ioutil" - "path" - "sort" - "time" -) - -func (s *TestSuite) TestSetSANS() { - - host := "example.com" - - sansWithoutHost := []string{"my-example.com"} - sansWithHost := []string{"example.com", "my-example.com"} - - vc := ViceCertificate{host: host} - vc.setSANs(sansWithHost) - - vc1 := ViceCertificate{host: host} - vc1.setSANs(sansWithoutHost) - - s.Equal(sortedStringSlice(vc.sans), sortedStringSlice(sansWithHost)) - s.Equal(sortedStringSlice(vc1.sans), sortedStringSlice(sansWithHost)) - -} - -func (s *TestSuite) TestGetSANS() { - testViceCertificates := map[*ViceCertificate][]string{ - &ViceCertificate{}: {""}, - &ViceCertificate{host: ""}: {""}, - &ViceCertificate{host: "example.com"}: {"example.com"}, - &ViceCertificate{host: "example.com", sans: []string{"my-example.com"}}: {"example.com", "my-example.com"}, - } - - for viceCert, expectedSANS := range testViceCertificates { - s.Equal( - sortedStringSlice(expectedSANS), - sortedStringSlice(viceCert.getSANs()), - "SANs should be equal", - ) - } -} - -func (s *TestSuite) TestCertificateAndHostMatch() { - s.ViceCert.host = "invalid.com" - s.False(s.ViceCert.DoesCertificateAndHostMatch(), "certificate common name shouldn't match 'invalid.com'") - - s.ViceCert.host = "example.com" - s.ViceCert.sans = []string{"example.com", "www.example.com", "www.my-example.com"} - s.True(s.ViceCert.DoesCertificateAndHostMatch(), "certificate common name and host should match") -} - -func (s *TestSuite) TestDoesKeyAndCertificateTally() { - randomKey, err := rsa.GenerateKey(rand.Reader, 2048) - s.Require().NoError(err, "there should be no error generating a random key") - - s.True(s.ViceCert.DoesKeyAndCertificateTally(), "certificate and private key should tally") - - s.ViceCert.privateKey = randomKey - s.False(s.ViceCert.DoesKeyAndCertificateTally(), "certificate and random private key shouldn't tally") - -} - -func (s *TestSuite) TestDoesCertificateExpireSoon() { - vc := s.ViceCert - minCertValidityDays := s.VP.Options.MinCertValidityDays - - vc.certificate.NotAfter = time.Now().AddDate(0, 0, -1) - s.True( - s.ViceCert.DoesCertificateExpireSoon(minCertValidityDays), - "should be true to indicate the certificate was valid until yesterday and has to be renewed", - ) - - vc.certificate.NotAfter = time.Now().AddDate(0, -1, 0) - s.True( - s.ViceCert.DoesCertificateExpireSoon(minCertValidityDays), - "should be true to indicate the certificate was valid until last month and has to be renewed", - ) - - vc.certificate.NotAfter = time.Now().AddDate(0, 0, 29) - s.True( - s.ViceCert.DoesCertificateExpireSoon(minCertValidityDays), - "should be true to indicate the certificate is valid for 1 month and has to be renewed", - ) - - vc.certificate.NotAfter = time.Now().AddDate(0, 1, 1) - s.False( - s.ViceCert.DoesCertificateExpireSoon(minCertValidityDays), - "should be false to indicate the certificate is valid for more than 1 month. no renewal needed", - ) - - vc.certificate.NotAfter = time.Now().AddDate(0, 6, 0) - s.False( - s.ViceCert.DoesCertificateExpireSoon(minCertValidityDays), - "should be false to indicate the certificate is valid for another 6 month. no renewal needed", - ) -} - -func (s *TestSuite) TestWriteCertificateChain() { - expectedChainPEM, err := ioutil.ReadFile(path.Join(FIXTURES, "chain.pem")) - - chainPEM, err := writeCertificatesToPEM( - s.ViceCert.withIntermediateCertificate(), - ) - s.NoError(err, "there should be no error writing a certificate to PEM format") - - s.Equal(expectedChainPEM, removeSpecialCharactersFromPEM(chainPEM)) -} - -func sortedStringSlice(s []string) []string { - sort.Strings(s) - return s -} diff --git a/vice-president/pkg/president/constants.go b/vice-president/pkg/president/constants.go deleted file mode 100644 index 16a1fdc49..000000000 --- a/vice-president/pkg/president/constants.go +++ /dev/null @@ -1,89 +0,0 @@ -/******************************************************************************* -* -* Copyright 2019 SAP SE -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You should have received a copy of the License along with this -* program. If not, you may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -*******************************************************************************/ - -package president - -import "time" - -const ( - // CertificateType is exactly that. - CertificateType = "CERTIFICATE" - - // PrivateKeyType is exactly that. - PrivateKeyType = "RSA PRIVATE KEY" - - // SecretTLSCertType defines under which key the certificate is stored in the secret. - // The following cert types will also be considered: - // (1) with underscore and dot, e.g.: tls_cert || tls.cert - // (2) *.cert | *.crt - SecretTLSCertType = "tls.crt" - - // SecretTLSKeyType defines under which key the private key is stored in the secret. - // The following key types will be checked: - // (1) with underscore and dot, e.g.: tls_key || tls.key - SecretTLSKeyType = "tls.key" - - // The vice president is tracking the state of the ingresses it handled via the following annotations. - - // IngressStateEnroll means a enrollment request has to be issued. - IngressStateEnroll = "enroll" - - // IngressStateRenew means a renewal request has to be be issued. - IngressStateRenew = "renew" - - // IngressStateApprove means that a certificate has to be approved. - IngressStateApprove = "approve" - - // IngressStateApproved means that a certificate was approved. - IngressStateApproved = "approved" - - // IngressStatePickup means that a certificate has to be picked up. - IngressStatePickup = "pickup" - - // IngressStateReplace means that a certificate has to be replaced. - IngressStateReplace = "replace" - - // BaseDelay defines the delay after which an ingress is added to the workqueue. - BaseDelay = 5 * time.Second - - // TmpPath points to tmp directory. - TmpPath = "/tmp" - - // AnnotationCertificateReplacement triggers one-time replacement of certificates for all hosts defined by the ingress. - AnnotationCertificateReplacement = "vice-president/replace-cert" - - // AnnotationSecretClaimedByIngress is used to indicate that a secret is being used by another ingress. - // This prevents multiple ingress' from using the same secret. - AnnotationSecretClaimedByIngress = "vice-president/claimed-by-ingress" - - // RateLimitPeriod is the period after which all rate limits are reset. - RateLimitPeriod = 1 * time.Hour - - // IngressFakeCN is the CN of the ingress controllers fake certificate. - IngressFakeCN = "Kubernetes Ingress Controller Fake Certificate" - - // IngressFakeHost is the list of hosts used by the ingress controllers fake certificate. - IngressFakeHost = "ingress.local" - - // ReasonSuperseded is the reason for replacing a existing certificate. - ReasonSuperseded = "SUPERSEDED" - - // UpdateEvent is the type of an update event. - UpdateEvent = "UpdateCertificate" -) diff --git a/vice-president/pkg/president/fixtures/approveCertificateResponse.xml b/vice-president/pkg/president/fixtures/approveCertificateResponse.xml deleted file mode 100644 index 053acdcdd..000000000 --- a/vice-president/pkg/president/fixtures/approveCertificateResponse.xml +++ /dev/null @@ -1,28 +0,0 @@ - -0x00 -certificate approved - ------BEGIN CERTIFICATE----- -MIIDZzCCAk+gAwIBAgIJAPhzYwHCM27pMA0GCSqGSIb3DQEBBQUAME8xCzAJBgNV -BAYTAkNOMQswCQYDVQQIEwJHRDELMAkGA1UEBxMCU1oxEzARBgNVBAoTCkFjbWUs -IEluYy4xETAPBgNVBAMTCE15Um9vdENBMB4XDTE3MTAxNzAyNDAyM1oXDTE3MTAy -NzAyNDAyM1owVjELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAkdEMQswCQYDVQQHEwJT -WjETMBEGA1UEChMKQWNtZSwgSW5jLjEYMBYGA1UEAxMPd3d3LmV4YW1wbGUuY29t -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApPPTLgpAIkltqBZogn+y -D48PNKV+70N98yij8z/TztBwtYilNUSxlkFfy3R9wcafpl9kpTxkRMHu/rI6fCnO -xLazkTEVbLRrYqT7abgF3z/wtYExXaxIpJt2hKXzW/9fDT8XlakPqW/YKAHChvSA -M0KTJkZg2JVYPoetAS5Vlmu4Y5vZel0Zzb2+I2WK11ifADBZNX6/9lXMVJ5cwO8X -y4s/Tdcp33aIbhOHb+dr8sMuipG40D0pWCmMbVCdFwFz/J7pg3e1LiXvzR7J/0Cb -aTf5gTiV1Mn1YRlCUCBGZ7g50Lanb6XnaLtcWk5KVk8lnm1Oq7u/QzMSkKzVHzLw -VQIDAQABoz8wPTA7BgNVHREENDAyggtleGFtcGxlLmNvbYIPd3d3LmV4YW1wbGUu -Y29tghJ3d3cubXktZXhhbXBsZS5jb20wDQYJKoZIhvcNAQEFBQADggEBAL/koUjx -SltXBWgtW/beWqqvptknKCiJK3e1ol2r2YSSWpK+gr0YiD9MxSt2ICJvWfat/qKM -ELl3S5DBTfrDPFMvFeD96dsUn++NIzRkzWRwqiIfgUh9jGvxrNBp8w2t4K953RL1 -uPNgJn09GRSX3PEwr6Xj7ZPouN1d/8bKATeT/MYpMNhGJ0k2fizE8Q6zuZyyvBgY -4BWW8u/WrKPEqj3e/lDwqSTakByyvMe/+ZeOtXo27AEwC4JQ+mEaywLuzlH/Jtmt -e7iI7TiuQ5Sd3/Aw9yByp1NWKEf5lIUsQF73b83S7MBDSQKtyAHDJe5XRPDsfbac -rR8bbg2GTfNfH6Q= ------END CERTIFICATE----- - -x509 - \ No newline at end of file diff --git a/vice-president/pkg/president/fixtures/chain.pem b/vice-president/pkg/president/fixtures/chain.pem deleted file mode 100644 index f8ffaf703..000000000 --- a/vice-president/pkg/president/fixtures/chain.pem +++ /dev/null @@ -1,38 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDZzCCAk+gAwIBAgIJAPhzYwHCM27pMA0GCSqGSIb3DQEBBQUAME8xCzAJBgNV -BAYTAkNOMQswCQYDVQQIEwJHRDELMAkGA1UEBxMCU1oxEzARBgNVBAoTCkFjbWUs -IEluYy4xETAPBgNVBAMTCE15Um9vdENBMB4XDTE3MTAxNzAyNDAyM1oXDTE3MTAy -NzAyNDAyM1owVjELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAkdEMQswCQYDVQQHEwJT -WjETMBEGA1UEChMKQWNtZSwgSW5jLjEYMBYGA1UEAxMPd3d3LmV4YW1wbGUuY29t -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApPPTLgpAIkltqBZogn+y -D48PNKV+70N98yij8z/TztBwtYilNUSxlkFfy3R9wcafpl9kpTxkRMHu/rI6fCnO -xLazkTEVbLRrYqT7abgF3z/wtYExXaxIpJt2hKXzW/9fDT8XlakPqW/YKAHChvSA -M0KTJkZg2JVYPoetAS5Vlmu4Y5vZel0Zzb2+I2WK11ifADBZNX6/9lXMVJ5cwO8X -y4s/Tdcp33aIbhOHb+dr8sMuipG40D0pWCmMbVCdFwFz/J7pg3e1LiXvzR7J/0Cb -aTf5gTiV1Mn1YRlCUCBGZ7g50Lanb6XnaLtcWk5KVk8lnm1Oq7u/QzMSkKzVHzLw -VQIDAQABoz8wPTA7BgNVHREENDAyggtleGFtcGxlLmNvbYIPd3d3LmV4YW1wbGUu -Y29tghJ3d3cubXktZXhhbXBsZS5jb20wDQYJKoZIhvcNAQEFBQADggEBAL/koUjx -SltXBWgtW/beWqqvptknKCiJK3e1ol2r2YSSWpK+gr0YiD9MxSt2ICJvWfat/qKM -ELl3S5DBTfrDPFMvFeD96dsUn++NIzRkzWRwqiIfgUh9jGvxrNBp8w2t4K953RL1 -uPNgJn09GRSX3PEwr6Xj7ZPouN1d/8bKATeT/MYpMNhGJ0k2fizE8Q6zuZyyvBgY -4BWW8u/WrKPEqj3e/lDwqSTakByyvMe/+ZeOtXo27AEwC4JQ+mEaywLuzlH/Jtmt -e7iI7TiuQ5Sd3/Aw9yByp1NWKEf5lIUsQF73b83S7MBDSQKtyAHDJe5XRPDsfbac -rR8bbg2GTfNfH6Q= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICsDCCAhmgAwIBAgIJALwzrJEIBOaeMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV -BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQwHhcNMTEwOTMwMTUyNjM2WhcNMjEwOTI3MTUyNjM2WjBF -MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50 -ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB -gQC88Ckwru9VR2p2KJ1WQyqesLzr95taNbhkYfsd0j8Tl0MGY5h+dczCaMQz0YY3 -xHXuU5yAQQTZjiks+D3KA3cx+iKDf2p1q77oXxQcx5CkrXBWTaX2oqVtHm3aX23B -AIORGuPk00b4rT3cld7VhcEFmzRNbyI0EqLMAxIwceUKSQIDAQABo4GnMIGkMB0G -A1UdDgQWBBSGmOdvSXKXclic5UOKPW35JLMEEjB1BgNVHSMEbjBsgBSGmOdvSXKX -clic5UOKPW35JLMEEqFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUt -U3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJALwzrJEI -BOaeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAcPfWn49pgAX54ji5 -SiUPFFNCuQGSSTHh2I+TMrs1G1Mb3a0X1dV5CNLRyXyuVxsqhiM/H2veFnTz2Q4U -wdY/kPxE19Auwcz9AvCkw7ol1LIlLfJvBzjzOjEpZJNtkXTx8ROSooNrDeJl3HyN -cciS5hf80XzIFqwhzaVS9gmiyM8= ------END CERTIFICATE----- diff --git a/vice-president/pkg/president/fixtures/enrollCertificateResponse.xml b/vice-president/pkg/president/fixtures/enrollCertificateResponse.xml deleted file mode 100644 index c408f2ba3..000000000 --- a/vice-president/pkg/president/fixtures/enrollCertificateResponse.xml +++ /dev/null @@ -1,5 +0,0 @@ - -0x00 -success -87d1adc3f1f262409092ec31fb09f4c7 - \ No newline at end of file diff --git a/vice-president/pkg/president/fixtures/example.key b/vice-president/pkg/president/fixtures/example.key deleted file mode 100644 index d51907523..000000000 --- a/vice-president/pkg/president/fixtures/example.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEApPPTLgpAIkltqBZogn+yD48PNKV+70N98yij8z/TztBwtYil -NUSxlkFfy3R9wcafpl9kpTxkRMHu/rI6fCnOxLazkTEVbLRrYqT7abgF3z/wtYEx -XaxIpJt2hKXzW/9fDT8XlakPqW/YKAHChvSAM0KTJkZg2JVYPoetAS5Vlmu4Y5vZ -el0Zzb2+I2WK11ifADBZNX6/9lXMVJ5cwO8Xy4s/Tdcp33aIbhOHb+dr8sMuipG4 -0D0pWCmMbVCdFwFz/J7pg3e1LiXvzR7J/0CbaTf5gTiV1Mn1YRlCUCBGZ7g50Lan -b6XnaLtcWk5KVk8lnm1Oq7u/QzMSkKzVHzLwVQIDAQABAoIBAFjXwmxPtQ9CgT9B -InT3Fg5vPP9cC6wOq+2le/ht7kS725jKykCQiR2mhFPuoDUYJ7DSPEEK7+5gjbq8 -f+eUFPu6c6jFyQZNfnEY7xiXtwHxTFJWDSKowk92NDVptFNejfFyg99luTvHM6MI -n59UXSA+wUaiLVM5zNeQ98tlC1ACQ1W/TaKjieDuYybsg2l5Wt+QeNDL5PRFt9Mz -W7rkNqnVz4g0YEuhx2xHA1OkmMW66EKSC+E36Ub52/oCh2zsnBe0fN6ENq+gaGTh -M1HkCNQHYKh+yjGYIqQxs7UltaUkTmXh50Oblu0LPkBJ9XMxhVpwBFIRdcRlPWXr -Yx2+YYECgYEA0rg/cQAN/m/GTXyeL/LqVEh82qpdNbpvwGm701y/8kcoVqgioe3l -ptVU0JJjxZBTPZSNLEWm2wtEyWoLQbHHA7BuRUurhYDiooeMCNN5snM3qcvAcxHu -QTSwXdgr4NeVLzg3kMpT/bNADysuyLIPt4z2/xCZlOEAEgaEV3lW8LECgYEAyGXn -842FDCmOzMOAToi5ZYfh9z7X/I/OOoBLBkOBMMIaHMdFRgScTbeQqeDk/Y7AqXva -lSI5g2lYJIgEhgAzQFO6ep/oOvaBbUolMkJWSZQpu5zuQpUfEEr0dgGcrccRZF2/ -q9V/55Yp+genO3Ur+YUwLGbM/jPC3WocW2hgQuUCgYAHjX0QoszdmsoDyvysgUtu -2+5FtCeFHnqU7fVCfSeQGobSnFIpdapukWTp2OVklmi1Y1awCIsTfNLpWTiwERwu -AKvjJfQu49C7vJoDUvkgsUqEdn4eYb0lbysHIhFojLGXMK+lK+tYw4z/Pi/+F4vC -q3XK7KBkmq/y9XLW481+8QKBgQCSmwLjCfCA4lJG+OuACMRhQ9SLNd17NpVv9PVu -AqqbyhSiYyIJxEu5C4b40L+Vw/6svyA9JEmhGstSBnQQuErGT7rIBP43TKmJE8o8 -h41qzMrvJzKjYUbVe5B2WF2QQGc33uHfSxUEjrXs5fZxKBpgtBB3G0ny2lk/9GYE -dUqfBQKBgBZCSOGp3ExfRwAGJBbWja6LXwWUm4/20AkVo1hMTsD6jc8Lx96Njrwq -fSSodzMLImYIvHXYeZo2ZwWZlAKO+aI7vi2RUauREbPtCAbX3g3nq6c+nFwAUIa4 -lWo0FqeBXgSK8sckCDUxHl8JxWujQsUct+8WYNQTQ6msXjKFj3Hp ------END RSA PRIVATE KEY----- diff --git a/vice-president/pkg/president/fixtures/example.kubeconfig b/vice-president/pkg/president/fixtures/example.kubeconfig deleted file mode 100644 index 003b33fab..000000000 --- a/vice-president/pkg/president/fixtures/example.kubeconfig +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: v1 -clusters: -- cluster: - certificate-authority: example.pem - server: http://localhost:8001 - name: staging -contexts: -- context: - cluster: staging - namespace: default - user: client@kubernetes.local - name: staging -current-context: staging -kind: Config -preferences: {} -users: -- name: client@kubernetes.local - user: - client-certificate: example.pem - client-key: example.key diff --git a/vice-president/pkg/president/fixtures/example.pem b/vice-president/pkg/president/fixtures/example.pem deleted file mode 100644 index a682c47bf..000000000 --- a/vice-president/pkg/president/fixtures/example.pem +++ /dev/null @@ -1,38 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDZzCCAk+gAwIBAgIJAPhzYwHCM27pMA0GCSqGSIb3DQEBBQUAME8xCzAJBgNV -BAYTAkNOMQswCQYDVQQIEwJHRDELMAkGA1UEBxMCU1oxEzARBgNVBAoTCkFjbWUs -IEluYy4xETAPBgNVBAMTCE15Um9vdENBMB4XDTE3MTAxNzAyNDAyM1oXDTE3MTAy -NzAyNDAyM1owVjELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAkdEMQswCQYDVQQHEwJT -WjETMBEGA1UEChMKQWNtZSwgSW5jLjEYMBYGA1UEAxMPd3d3LmV4YW1wbGUuY29t -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApPPTLgpAIkltqBZogn+y -D48PNKV+70N98yij8z/TztBwtYilNUSxlkFfy3R9wcafpl9kpTxkRMHu/rI6fCnO -xLazkTEVbLRrYqT7abgF3z/wtYExXaxIpJt2hKXzW/9fDT8XlakPqW/YKAHChvSA -M0KTJkZg2JVYPoetAS5Vlmu4Y5vZel0Zzb2+I2WK11ifADBZNX6/9lXMVJ5cwO8X -y4s/Tdcp33aIbhOHb+dr8sMuipG40D0pWCmMbVCdFwFz/J7pg3e1LiXvzR7J/0Cb -aTf5gTiV1Mn1YRlCUCBGZ7g50Lanb6XnaLtcWk5KVk8lnm1Oq7u/QzMSkKzVHzLw -VQIDAQABoz8wPTA7BgNVHREENDAyggtleGFtcGxlLmNvbYIPd3d3LmV4YW1wbGUu -Y29tghJ3d3cubXktZXhhbXBsZS5jb20wDQYJKoZIhvcNAQEFBQADggEBAL/koUjx -SltXBWgtW/beWqqvptknKCiJK3e1ol2r2YSSWpK+gr0YiD9MxSt2ICJvWfat/qKM -ELl3S5DBTfrDPFMvFeD96dsUn++NIzRkzWRwqiIfgUh9jGvxrNBp8w2t4K953RL1 -uPNgJn09GRSX3PEwr6Xj7ZPouN1d/8bKATeT/MYpMNhGJ0k2fizE8Q6zuZyyvBgY -4BWW8u/WrKPEqj3e/lDwqSTakByyvMe/+ZeOtXo27AEwC4JQ+mEaywLuzlH/Jtmt -e7iI7TiuQ5Sd3/Aw9yByp1NWKEf5lIUsQF73b83S7MBDSQKtyAHDJe5XRPDsfbac -rR8bbg2GTfNfH6Q= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICsDCCAhmgAwIBAgIJALwzrJEIBOaeMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV -BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQwHhcNMTEwOTMwMTUyNjM2WhcNMjEwOTI3MTUyNjM2WjBF -MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50 -ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB -gQC88Ckwru9VR2p2KJ1WQyqesLzr95taNbhkYfsd0j8Tl0MGY5h+dczCaMQz0YY3 -xHXuU5yAQQTZjiks+D3KA3cx+iKDf2p1q77oXxQcx5CkrXBWTaX2oqVtHm3aX23B -AIORGuPk00b4rT3cld7VhcEFmzRNbyI0EqLMAxIwceUKSQIDAQABo4GnMIGkMB0G -A1UdDgQWBBSGmOdvSXKXclic5UOKPW35JLMEEjB1BgNVHSMEbjBsgBSGmOdvSXKX -clic5UOKPW35JLMEEqFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUt -U3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJALwzrJEI -BOaeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAcPfWn49pgAX54ji5 -SiUPFFNCuQGSSTHh2I+TMrs1G1Mb3a0X1dV5CNLRyXyuVxsqhiM/H2veFnTz2Q4U -wdY/kPxE19Auwcz9AvCkw7ol1LIlLfJvBzjzOjEpZJNtkXTx8ROSooNrDeJl3HyN -cciS5hf80XzIFqwhzaVS9gmiyM8= ------END CERTIFICATE----- \ No newline at end of file diff --git a/vice-president/pkg/president/fixtures/example.vicepresidentconfig b/vice-president/pkg/president/fixtures/example.vicepresidentconfig deleted file mode 100644 index cc2fb6570..000000000 --- a/vice-president/pkg/president/fixtures/example.vicepresidentconfig +++ /dev/null @@ -1,14 +0,0 @@ -vice: - first_name: "Max" - last_name: "Muster" - email: "max@muster.com" - country: "DE" - province: "Berlin" - locality: "Berlin" - organization: "My Company" - organizational_unit: "P&I" - default_challenge: "Password1!" - -president: - resync_period_minutes: 4 - certificate_check_interval_minutes: 10 \ No newline at end of file diff --git a/vice-president/pkg/president/fixtures/intermediate.pem b/vice-president/pkg/president/fixtures/intermediate.pem deleted file mode 100644 index e95e04aaa..000000000 --- a/vice-president/pkg/president/fixtures/intermediate.pem +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICsDCCAhmgAwIBAgIJALwzrJEIBOaeMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV -BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQwHhcNMTEwOTMwMTUyNjM2WhcNMjEwOTI3MTUyNjM2WjBF -MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50 -ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB -gQC88Ckwru9VR2p2KJ1WQyqesLzr95taNbhkYfsd0j8Tl0MGY5h+dczCaMQz0YY3 -xHXuU5yAQQTZjiks+D3KA3cx+iKDf2p1q77oXxQcx5CkrXBWTaX2oqVtHm3aX23B -AIORGuPk00b4rT3cld7VhcEFmzRNbyI0EqLMAxIwceUKSQIDAQABo4GnMIGkMB0G -A1UdDgQWBBSGmOdvSXKXclic5UOKPW35JLMEEjB1BgNVHSMEbjBsgBSGmOdvSXKX -clic5UOKPW35JLMEEqFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUt -U3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJALwzrJEI -BOaeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAcPfWn49pgAX54ji5 -SiUPFFNCuQGSSTHh2I+TMrs1G1Mb3a0X1dV5CNLRyXyuVxsqhiM/H2veFnTz2Q4U -wdY/kPxE19Auwcz9AvCkw7ol1LIlLfJvBzjzOjEpZJNtkXTx8ROSooNrDeJl3HyN -cciS5hf80XzIFqwhzaVS9gmiyM8= ------END CERTIFICATE----- \ No newline at end of file diff --git a/vice-president/pkg/president/fixtures/mySecret.json b/vice-president/pkg/president/fixtures/mySecret.json deleted file mode 100644 index 99cb3d042..000000000 --- a/vice-president/pkg/president/fixtures/mySecret.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "kind": "Secret", - "apiVersion": "v1", - "metadata": { - "name": "my-secret", - "namespace": "default", - "selfLink": "/api/v1/namespaces/default/secrets/my-secret", - "uid": "2de107fe-c606-11e6-94fc-d61107a5119e", - "resourceVersion": "433504131", - "creationTimestamp": "2017-07-06T11:37:12Z" - }, - "data": { - "tls.crt": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURaekNDQWsrZ0F3SUJBZ0lKQVBoell3SENNMjdwTUEwR0NTcUdTSWIzRFFFQkJRVUFNRTh4Q3pBSkJnTlYKQkFZVEFrTk9NUXN3Q1FZRFZRUUlFd0pIUkRFTE1Ba0dBMVVFQnhNQ1Uxb3hFekFSQmdOVkJBb1RDa0ZqYldVcwpJRWx1WXk0eEVUQVBCZ05WQkFNVENFMTVVbTl2ZEVOQk1CNFhEVEUzTVRBeE56QXlOREF5TTFvWERURTNNVEF5Ck56QXlOREF5TTFvd1ZqRUxNQWtHQTFVRUJoTUNRMDR4Q3pBSkJnTlZCQWdUQWtkRU1Rc3dDUVlEVlFRSEV3SlQKV2pFVE1CRUdBMVVFQ2hNS1FXTnRaU3dnU1c1akxqRVlNQllHQTFVRUF4TVBkM2QzTG1WNFlXMXdiR1V1WTI5dApNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXBQUFRMZ3BBSWtsdHFCWm9nbit5CkQ0OFBOS1YrNzBOOTh5aWo4ei9UenRCd3RZaWxOVVN4bGtGZnkzUjl3Y2FmcGw5a3BUeGtSTUh1L3JJNmZDbk8KeExhemtURVZiTFJyWXFUN2FiZ0Yzei93dFlFeFhheElwSnQyaEtYelcvOWZEVDhYbGFrUHFXL1lLQUhDaHZTQQpNMEtUSmtaZzJKVllQb2V0QVM1VmxtdTRZNXZaZWwwWnpiMitJMldLMTFpZkFEQlpOWDYvOWxYTVZKNWN3TzhYCnk0cy9UZGNwMzNhSWJoT0hiK2RyOHNNdWlwRzQwRDBwV0NtTWJWQ2RGd0Z6L0o3cGczZTFMaVh2elI3Si8wQ2IKYVRmNWdUaVYxTW4xWVJsQ1VDQkdaN2c1MExhbmI2WG5hTHRjV2s1S1ZrOGxubTFPcTd1L1F6TVNrS3pWSHpMdwpWUUlEQVFBQm96OHdQVEE3QmdOVkhSRUVOREF5Z2d0bGVHRnRjR3hsTG1OdmJZSVBkM2QzTG1WNFlXMXdiR1V1ClkyOXRnaEozZDNjdWJYa3RaWGhoYlhCc1pTNWpiMjB3RFFZSktvWklodmNOQVFFRkJRQURnZ0VCQUwva29VangKU2x0WEJXZ3RXL2JlV3FxdnB0a25LQ2lKSzNlMW9sMnIyWVNTV3BLK2dyMFlpRDlNeFN0MklDSnZXZmF0L3FLTQpFTGwzUzVEQlRmckRQRk12RmVEOTZkc1VuKytOSXpSa3pXUndxaUlmZ1VoOWpHdnhyTkJwOHcydDRLOTUzUkwxCnVQTmdKbjA5R1JTWDNQRXdyNlhqN1pQb3VOMWQvOGJLQVRlVC9NWXBNTmhHSjBrMmZpekU4UTZ6dVp5eXZCZ1kKNEJXVzh1L1dyS1BFcWozZS9sRHdxU1Rha0J5eXZNZS8rWmVPdFhvMjdBRXdDNEpRK21FYXl3THV6bEgvSnRtdAplN2lJN1RpdVE1U2QzL0F3OXlCeXAxTldLRWY1bElVc1FGNzNiODNTN01CRFNRS3R5QUhESmU1WFJQRHNmYmFjCnJSOGJiZzJHVGZOZkg2UT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=", - "tls.key": "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBcFBQVExncEFJa2x0cUJab2duK3lENDhQTktWKzcwTjk4eWlqOHovVHp0Qnd0WWlsCk5VU3hsa0ZmeTNSOXdjYWZwbDlrcFR4a1JNSHUvckk2ZkNuT3hMYXprVEVWYkxScllxVDdhYmdGM3ovd3RZRXgKWGF4SXBKdDJoS1h6Vy85ZkRUOFhsYWtQcVcvWUtBSENodlNBTTBLVEprWmcySlZZUG9ldEFTNVZsbXU0WTV2WgplbDBaemIyK0kyV0sxMWlmQURCWk5YNi85bFhNVko1Y3dPOFh5NHMvVGRjcDMzYUliaE9IYitkcjhzTXVpcEc0CjBEMHBXQ21NYlZDZEZ3RnovSjdwZzNlMUxpWHZ6UjdKLzBDYmFUZjVnVGlWMU1uMVlSbENVQ0JHWjdnNTBMYW4KYjZYbmFMdGNXazVLVms4bG5tMU9xN3UvUXpNU2tLelZIekx3VlFJREFRQUJBb0lCQUZqWHdteFB0UTlDZ1Q5QgpJblQzRmc1dlBQOWNDNndPcSsybGUvaHQ3a1M3MjVqS3lrQ1FpUjJtaEZQdW9EVVlKN0RTUEVFSzcrNWdqYnE4CmYrZVVGUHU2YzZqRnlRWk5mbkVZN3hpWHR3SHhURkpXRFNLb3drOTJORFZwdEZOZWpmRnlnOTlsdVR2SE02TUkKbjU5VVhTQSt3VWFpTFZNNXpOZVE5OHRsQzFBQ1ExVy9UYUtqaWVEdVl5YnNnMmw1V3QrUWVOREw1UFJGdDlNegpXN3JrTnFuVno0ZzBZRXVoeDJ4SEExT2ttTVc2NkVLU0MrRTM2VWI1Mi9vQ2gyenNuQmUwZk42RU5xK2dhR1RoCk0xSGtDTlFIWUtoK3lqR1lJcVF4czdVbHRhVWtUbVhoNTBPYmx1MExQa0JKOVhNeGhWcHdCRklSZGNSbFBXWHIKWXgyK1lZRUNnWUVBMHJnL2NRQU4vbS9HVFh5ZUwvTHFWRWg4MnFwZE5icHZ3R203MDF5LzhrY29WcWdpb2UzbApwdFZVMEpKanhaQlRQWlNOTEVXbTJ3dEV5V29MUWJISEE3QnVSVXVyaFlEaW9vZU1DTk41c25NM3FjdkFjeEh1ClFUU3dYZGdyNE5lVkx6ZzNrTXBUL2JOQUR5c3V5TElQdDR6Mi94Q1psT0VBRWdhRVYzbFc4TEVDZ1lFQXlHWG4KODQyRkRDbU96TU9BVG9pNVpZZmg5ejdYL0kvT09vQkxCa09CTU1JYUhNZEZSZ1NjVGJlUXFlRGsvWTdBcVh2YQpsU0k1ZzJsWUpJZ0VoZ0F6UUZPNmVwL29PdmFCYlVvbE1rSldTWlFwdTV6dVFwVWZFRXIwZGdHY3JjY1JaRjIvCnE5Vi81NVlwK2dlbk8zVXIrWVV3TEdiTS9qUEMzV29jVzJoZ1F1VUNnWUFIalgwUW9zemRtc29EeXZ5c2dVdHUKMis1RnRDZUZIbnFVN2ZWQ2ZTZVFHb2JTbkZJcGRhcHVrV1RwMk9Wa2xtaTFZMWF3Q0lzVGZOTHBXVGl3RVJ3dQpBS3ZqSmZRdTQ5Qzd2Sm9EVXZrZ3NVcUVkbjRlWWIwbGJ5c0hJaEZvakxHWE1LK2xLK3RZdzR6L1BpLytGNHZDCnEzWEs3S0JrbXEveTlYTFc0ODErOFFLQmdRQ1Ntd0xqQ2ZDQTRsSkcrT3VBQ01SaFE5U0xOZDE3TnBWdjlQVnUKQXFxYnloU2lZeUlKeEV1NUM0YjQwTCtWdy82c3Z5QTlKRW1oR3N0U0JuUVF1RXJHVDdySUJQNDNUS21KRThvOApoNDFxek1ydkp6S2pZVWJWZTVCMldGMlFRR2MzM3VIZlN4VUVqclhzNWZaeEtCcGd0QkIzRzBueTJsay85R1lFCmRVcWZCUUtCZ0JaQ1NPR3AzRXhmUndBR0pCYldqYTZMWHdXVW00LzIwQWtWbzFoTVRzRDZqYzhMeDk2Tmpyd3EKZlNTb2R6TUxJbVlJdkhYWWVabzJad1dabEFLTythSTd2aTJSVWF1UkViUHRDQWJYM2czbnE2YytuRndBVUlhNApsV28wRnFlQlhnU0s4c2NrQ0RVeEhsOEp4V3VqUXNVY3QrOFdZTlFUUTZtc1hqS0ZqM0hwCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==" - }, - "type": "Opaque" -} diff --git a/vice-president/pkg/president/fixtures/pickupCertificateResponse.xml b/vice-president/pkg/president/fixtures/pickupCertificateResponse.xml deleted file mode 100644 index 20c3873cd..000000000 --- a/vice-president/pkg/president/fixtures/pickupCertificateResponse.xml +++ /dev/null @@ -1,27 +0,0 @@ - -0x00 -success - ------BEGIN CERTIFICATE----- -MIIDZzCCAk+gAwIBAgIJAPhzYwHCM27pMA0GCSqGSIb3DQEBBQUAME8xCzAJBgNV -BAYTAkNOMQswCQYDVQQIEwJHRDELMAkGA1UEBxMCU1oxEzARBgNVBAoTCkFjbWUs -IEluYy4xETAPBgNVBAMTCE15Um9vdENBMB4XDTE3MTAxNzAyNDAyM1oXDTE3MTAy -NzAyNDAyM1owVjELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAkdEMQswCQYDVQQHEwJT -WjETMBEGA1UEChMKQWNtZSwgSW5jLjEYMBYGA1UEAxMPd3d3LmV4YW1wbGUuY29t -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApPPTLgpAIkltqBZogn+y -D48PNKV+70N98yij8z/TztBwtYilNUSxlkFfy3R9wcafpl9kpTxkRMHu/rI6fCnO -xLazkTEVbLRrYqT7abgF3z/wtYExXaxIpJt2hKXzW/9fDT8XlakPqW/YKAHChvSA -M0KTJkZg2JVYPoetAS5Vlmu4Y5vZel0Zzb2+I2WK11ifADBZNX6/9lXMVJ5cwO8X -y4s/Tdcp33aIbhOHb+dr8sMuipG40D0pWCmMbVCdFwFz/J7pg3e1LiXvzR7J/0Cb -aTf5gTiV1Mn1YRlCUCBGZ7g50Lanb6XnaLtcWk5KVk8lnm1Oq7u/QzMSkKzVHzLw -VQIDAQABoz8wPTA7BgNVHREENDAyggtleGFtcGxlLmNvbYIPd3d3LmV4YW1wbGUu -Y29tghJ3d3cubXktZXhhbXBsZS5jb20wDQYJKoZIhvcNAQEFBQADggEBAL/koUjx -SltXBWgtW/beWqqvptknKCiJK3e1ol2r2YSSWpK+gr0YiD9MxSt2ICJvWfat/qKM -ELl3S5DBTfrDPFMvFeD96dsUn++NIzRkzWRwqiIfgUh9jGvxrNBp8w2t4K953RL1 -uPNgJn09GRSX3PEwr6Xj7ZPouN1d/8bKATeT/MYpMNhGJ0k2fizE8Q6zuZyyvBgY -4BWW8u/WrKPEqj3e/lDwqSTakByyvMe/+ZeOtXo27AEwC4JQ+mEaywLuzlH/Jtmt -e7iI7TiuQ5Sd3/Aw9yByp1NWKEf5lIUsQF73b83S7MBDSQKtyAHDJe5XRPDsfbac -rR8bbg2GTfNfH6Q= ------END CERTIFICATE----- - - \ No newline at end of file diff --git a/vice-president/pkg/president/fixtures/renewCertificateResponse.xml b/vice-president/pkg/president/fixtures/renewCertificateResponse.xml deleted file mode 100644 index fa21c41b3..000000000 --- a/vice-president/pkg/president/fixtures/renewCertificateResponse.xml +++ /dev/null @@ -1,5 +0,0 @@ - -0x00 -success -87d1adc3f1f262409092ec31fb09f4c7 - \ No newline at end of file diff --git a/vice-president/pkg/president/metrics.go b/vice-president/pkg/president/metrics.go deleted file mode 100644 index 1c0411c81..000000000 --- a/vice-president/pkg/president/metrics.go +++ /dev/null @@ -1,185 +0,0 @@ -/******************************************************************************* -* -* Copyright 2019 SAP SE -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You should have received a copy of the License along with this -* program. If not, you may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -*******************************************************************************/ - -package president - -import ( - "fmt" - "github.com/sapcc/kubernetes-operators/vice-president/pkg/config" - "net" - "net/http" - "sync" - - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/sapcc/kubernetes-operators/vice-president/pkg/log" -) - -const ( - // MetricNamespace used as prefix for metrics - MetricNamespace = "vice_president" -) - -var ( - labels = []string{"ingress", "host", "sans"} - - enrollSuccessCounter = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Namespace: MetricNamespace, - Name: "successful_enrollments", - Help: "Counter for successful certificate enrollments.", - }, - labels, - ) - - enrollFailedCounter = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Namespace: MetricNamespace, - Name: "failed_enrollments", - Help: "Counter for failed certificate enrollments.", - }, - labels, - ) - - renewSuccessCounter = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Namespace: MetricNamespace, - Name: "successful_renewals", - Help: "Counter for successful certificate renewals.", - }, - labels, - ) - - renewFailedCounter = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Namespace: MetricNamespace, - Name: "failed_renewals", - Help: "Counter for failed certificate renewals.", - }, - labels, - ) - - pickupSuccessCounter = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Namespace: MetricNamespace, - Name: "successful_pickups", - Help: "Counter for successful certificate pickups.", - }, - labels, - ) - - pickupFailedCounter = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Namespace: MetricNamespace, - Name: "failed_pickups", - Help: "Counter for failed certificate pickups.", - }, - labels, - ) - - approveSuccessCounter = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Namespace: MetricNamespace, - Name: "successful_approvals", - Help: "Counter for successful certificate approvals.", - }, - labels, - ) - - approveFailedCounter = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Namespace: MetricNamespace, - Name: "failed_approvals", - Help: "Counter for failed certificate approvals.", - }, - labels, - ) - - replaceSuccessCounter = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Namespace: MetricNamespace, - Name: "successful_replacements", - Help: "Counter for successful certificate replacements.", - }, - labels, - ) - - replaceFailedCounter = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Namespace: MetricNamespace, - Name: "failed_replacements", - Help: "Counter for failed certificate replacements.", - }, - labels, - ) - - apiRateLimitHitGauge = prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Namespace: MetricNamespace, - Name: "rate_limit_reached", - Help: "Maximum number of VICE API requests within 1h reached.", - }, - labels, - ) -) - -func registerCollectors(collector prometheus.Collector) { - if collector != nil { - prometheus.MustRegister(collector) - } - prometheus.MustRegister( - enrollSuccessCounter, - enrollFailedCounter, - renewSuccessCounter, - renewFailedCounter, - pickupSuccessCounter, - pickupFailedCounter, - approveSuccessCounter, - approveFailedCounter, - replaceSuccessCounter, - replaceFailedCounter, - apiRateLimitHitGauge, - ) -} - -// ExposeMetrics exposes the above defined metrics on :/metrics -func ExposeMetrics(options config.Options, stopCh <-chan struct{}, wg *sync.WaitGroup, logger log.Logger) { - wg.Add(1) - defer wg.Done() - - logger = log.NewLoggerWith(logger, "component", "metrics") - - if options.IsEnableAdditionalSymantecMetrics { - registerCollectors(NewSymantecMetricsCollector(options, logger)) - } else { - registerCollectors(nil) - } - - ln, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%v", options.MetricPort)) - if err != nil { - logger.LogError("failed to open listener", err) - return - } - - logger.LogInfo("exposing prometheus metrics", "host", "0.0.0.0", "port", options.MetricPort) - - go http.Serve(ln, promhttp.Handler()) - <- stopCh - ln.Close() -} diff --git a/vice-president/pkg/president/operator.go b/vice-president/pkg/president/operator.go deleted file mode 100644 index 249eb2624..000000000 --- a/vice-president/pkg/president/operator.go +++ /dev/null @@ -1,540 +0,0 @@ -/******************************************************************************* -* -* Copyright 2019 SAP SE -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You should have received a copy of the License along with this -* program. If not, you may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -*******************************************************************************/ - -package president - -import ( - "crypto/tls" - "crypto/x509" - "fmt" - "reflect" - "strings" - "sync" - "time" - - "github.com/pkg/errors" - "github.com/prometheus/client_golang/prometheus" - "github.com/sapcc/kubernetes-operators/vice-president/pkg/config" - "github.com/sapcc/kubernetes-operators/vice-president/pkg/k8sutils" - "github.com/sapcc/kubernetes-operators/vice-president/pkg/log" - corev1 "k8s.io/api/core/v1" - extensionsv1beta1 "k8s.io/api/extensions/v1beta1" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/tools/record" - "k8s.io/client-go/util/workqueue" -) - -var ( - // VERSION of the vice president - VERSION = "0.0.0.dev" -) - -// Operator is the vice-president certificate operator -type Operator struct { - config.Options - - vicePresidentConfig config.VicePresidentConfig - rootCertPool *x509.CertPool - intermediateCertificate *x509.Certificate - k8sFramework *k8sutils.K8sFramework - viceClient *viceClient - queue workqueue.RateLimitingInterface - logger log.Logger - eventRecorder record.EventRecorder - rateLimitMap sync.Map // stores mapping of { host : numAPIRequests } -} - -// New creates a new operator using the given options -func New(options config.Options, logger log.Logger) (*Operator, error) { - viceLogger := log.NewLoggerWith(logger, "component", "viceClient") - operatorLogger := log.NewLoggerWith(logger, "component", "operator") - logger.LogDebug("creating new vice president", "version", VERSION) - - if err := options.CheckOptions(); err != nil { - return nil, errors.Wrap(err, "error in configuration") - } - - vicePresidentConfig, err := config.ReadConfig(options.VicePresidentConfig) - if err != nil { - return nil, errors.Wrap(err, "could get vice configuration") - } - - cert, err := tls.LoadX509KeyPair(options.ViceCrtFile, options.ViceKeyFile) - if err != nil { - return nil, errors.Wrap(err, "couldn't load certificate for vice client") - } - - // create a new vice client or die - viceClient := newViceClient(cert, vicePresidentConfig, viceLogger) - if viceClient == nil { - return nil, errors.Wrap(err, "couldn't create vice client") - } - - intermediateCert, err := readCertFromFile(options.IntermediateCertificate) - if err != nil { - return nil, errors.Wrap(err, "couldn't read intermediate certificate") - } - - caCert, err := readCertFromFile(options.ViceCrtFile) - if err != nil { - return nil, errors.Wrap(err, "couldn't read CA Cert") - } - rootCertPool := x509.NewCertPool() - rootCertPool.AddCert(caCert) - - queue := workqueue.NewRateLimitingQueue( - workqueue.NewItemExponentialFailureRateLimiter(30*time.Second, 600*time.Second), - ) - - k8sFramework, err := k8sutils.NewK8sFramework(options, logger) - if err != nil { - return nil, errors.Wrap(err, "couldn't create kubernetes framework") - } - - vp := &Operator{ - queue: queue, - logger: operatorLogger, - Options: options, - k8sFramework: k8sFramework, - vicePresidentConfig: vicePresidentConfig, - viceClient: viceClient, - rootCertPool: rootCertPool, - intermediateCertificate: intermediateCert, - rateLimitMap: sync.Map{}, - } - - vp.k8sFramework.AddIngressInformerEventHandler( - vp.enqueueItem, - vp.enqueueItem, - func(oldObj, newObj interface{}) { - old := oldObj.(*extensionsv1beta1.Ingress) - new := newObj.(*extensionsv1beta1.Ingress) - // Enqueue if either Spec, annotations changed or DeletionTimestamp was set. - if !reflect.DeepEqual(old.Spec, new.Spec) || !reflect.DeepEqual(old.GetAnnotations(), new.GetAnnotations()) || !reflect.DeepEqual(old.GetDeletionTimestamp(), new.GetDeletionTimestamp()) { - vp.enqueueItem(newObj) - } - }, - ) - - vp.k8sFramework.AddSecretInformerEventHandler( - nil, - func(obj interface{}) { - secret := obj.(*corev1.Secret) - // If secret is deleted but used by an ingress, requeue the ingress. - if ingressKey, ok := secret.GetAnnotations()[AnnotationSecretClaimedByIngress]; ok { - vp.queue.AddAfter(ingressKey, BaseDelay) - } - }, - nil, - ) - - return vp, nil -} - -// Run starts the operator. -func (vp *Operator) Run(threadiness int, stopCh <-chan struct{}, wg *sync.WaitGroup) { - defer utilruntime.HandleCrash() - defer vp.queue.ShutDown() - defer wg.Done() - wg.Add(1) - - vp.logger.LogInfo("Ladies and Gentlemen, the Vice President! Renewing your Digicert certificates.", "version", VERSION) - - go vp.k8sFramework.Run(stopCh) - - vp.logger.LogInfo("waiting for caches to sync...") - if !vp.k8sFramework.WaitForCacheSync(stopCh) { - utilruntime.HandleError(errors.New("timed out while waiting for caches to sync")) - return - } - - for i := 0; i < threadiness; i++ { - go wait.Until(vp.runWorker, time.Second, stopCh) - } - - vp.logger.LogInfo("cache primed. ready for operations.") - - ticker := time.NewTicker(vp.CertificateCheckInterval) - tickerResetRateLimit := time.NewTicker(RateLimitPeriod) - go func() { - for { - select { - case <-ticker.C: - vp.checkCertificates() - vp.logger.LogInfo("next check", "interval", vp.CertificateCheckInterval) - case <-tickerResetRateLimit.C: - vp.resetRateLimits() - vp.logger.LogInfo("resetting all rate limits") - case <-stopCh: - ticker.Stop() - return - } - } - }() - - <-stopCh - vp.logger.LogInfo("vice president is resigning") -} - -func (vp *Operator) runWorker() { - for vp.processNextWorkItem() { - } -} - -func (vp *Operator) processNextWorkItem() bool { - obj, quit := vp.queue.Get() - if quit { - return false - } - - err := func(obj interface{}) error { - defer vp.queue.Done(obj) - - var ( - key string - ok bool - ) - - if key, ok = obj.(string); !ok { - vp.queue.Forget(key) - utilruntime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj)) - return nil - } - - if err := vp.syncHandler(key); err != nil { - vp.queue.AddRateLimited(key) - return fmt.Errorf("error syncing '%s': %s, requeuing", key, err.Error()) - } - - vp.queue.Forget(key) - vp.logger.LogInfo("successfully synced", "key", key) - return nil - }(obj) - - if err != nil { - utilruntime.HandleError(err) - return true - } - - return true -} - -func (vp *Operator) syncHandler(key string) error { - o, exists, err := vp.k8sFramework.GetIngressFromIndexerByKey(key) - if err != nil { - utilruntime.HandleError(fmt.Errorf("%v failed with : %v", key, err)) - return err - } - if !exists { - vp.logger.LogInfo("ingress was deleted", "key", key) - return nil - } - - ingress := o.(*extensionsv1beta1.Ingress) - // return right here if ingress is not annotated with vice-president: true - if !k8sutils.IngressHasAnnotation(ingress, vp.IngressAnnotation) { - vp.logger.LogDebug("ignoring ingress as vice-presidential annotation is not set", "key", key) - return nil - } - // Check each host. - for _, tls := range ingress.Spec.TLS { - if len(tls.Hosts) == 0 { - return fmt.Errorf("no hosts found in ingress.spec.tls. key %s", key) - } - - // Use the given name or the normalized hostname for the secret name. - secretName := tls.SecretName - if secretName == "" { - return fmt.Errorf("no secret name given in ingress %s for host %s", key, tls.Hosts[0]) - } - - vp.logger.LogDebug("checking ingress", "key", key, "hosts", strings.Join(tls.Hosts, ", "), "secret", keyFunc(ingress.GetNamespace(), secretName)) - - // The tls.Hosts[0] will be the CN, tls.Hosts[1:] the SANs of the certificate. - vc := NewViceCertificate(ingress, secretName, tls.Hosts[0], tls.Hosts[1:], vp.intermediateCertificate, vp.rootCertPool) - return vp.checkCertificate(vc) - } - return err -} - -func (vp *Operator) checkCertificate(vc *ViceCertificate) error { - // Labels to be used for prometheus metrics. - labels := prometheus.Labels{ - "ingress": vc.getIngressKey(), - "host": vc.host, - "sans": vc.getSANsString(), - } - - // Check if an alternative key for the TLS certificate and private key is configured via annotation on the ingress. - tlsKeySecretKey, tlsCertSecretKey := k8sutils.IngressGetSecretKeysFromAnnotation(vc.ingress, SecretTLSKeyType, SecretTLSCertType) - - var state, nextState string - - // Find the secret or create it. - // Find the TLS certificate and key or create them (IngressStateEnroll). - // Check the TLS certificate and key or re-create (IngressStateEnroll|IngressStateReplace) or renew (IngressStateRenew) them. - // If a certificate was enrolled, renewed or replaced and approval is required using the the certificates transaction id (TID). - // Add/update the approved certificate to the referenced secret. - for { - state = nextState - switch state { - case IngressStateEnroll: - if err := vp.viceClient.enroll(vc); err != nil { - vp.logger.LogError("couldn't enroll certificate", err, "key", vc.getIngressKey(), "host", vc.host) - enrollFailedCounter.With(labels).Inc() - return err - } - enrollSuccessCounter.With(labels).Inc() - nextState = IngressStateApprove - - case IngressStateRenew: - if err := vp.renewCertificateRateLimited(vc); err != nil { - vp.logger.LogError("couldn't renew certificate", err, "key", vc.getIngressKey(), "host", vc.host, "tid", vc.tid) - renewFailedCounter.With(labels).Inc() - return err - } - renewSuccessCounter.With(labels).Inc() - nextState = IngressStateApprove - - case IngressStatePickup: - if err := vp.pickupCertificateRateLimited(vc); err != nil { - vp.logger.LogError("couldn't pickup certificate", err, "key", vc.getIngressKey(), "host", vc.host, "tid", vc.tid) - pickupFailedCounter.With(labels).Inc() - return err - } - if err := vp.updateCertificateAndKeyInSecret(vc, tlsKeySecretKey, tlsCertSecretKey); err != nil { - return err - } - pickupSuccessCounter.With(labels).Inc() - nextState = IngressStateApprove - - case IngressStateReplace: - if err := vp.replaceCertificateRateLimited(vc); err != nil { - vp.logger.LogError("couldn't replace certificate", err, "key", vc.getIngressKey(), "host", vc.host, "tid", vc.tid) - replaceFailedCounter.With(labels).Inc() - return err - } - // Remove the vice-president/replace-cert annotation from the ingress if all certificates have been replaced. - if k8sutils.IsLastHostInIngressSpec(vc.ingress, vc.host) { - if err := vp.k8sFramework.RemoveIngressAnnotation(vc.ingress, AnnotationCertificateReplacement); err != nil { - return err - } - } - replaceSuccessCounter.With(labels).Inc() - nextState = IngressStateApprove - - case IngressStateApprove: - if vc.certificate != nil && vc.tid == "" { - vp.logger.LogInfo("certificate was automatically approved", "key", vc.getIngressKey(), "host", vc.host) - } else { - if err := vp.viceClient.approve(vc); err != nil { - vp.logger.LogError("couldn't approve certificate", err, "ingress", vc.getIngressKey(), "host", vc.host, "tid", vc.tid) - approveFailedCounter.With(labels).Inc() - return err - } - } - if err := vp.updateCertificateAndKeyInSecret(vc, tlsKeySecretKey, tlsCertSecretKey); err != nil { - approveFailedCounter.With(labels).Inc() - return err - } - approveSuccessCounter.With(labels).Inc() - vp.k8sFramework.Eventf(vc.ingress, corev1.EventTypeNormal, UpdateEvent, fmt.Sprintf("updated certificate for host %s, ingress %s", vc.host, vc.getIngressKey())) - nextState = IngressStateApproved - - case IngressStateApproved: - // Certificate approved. We're done. - return nil - - default: - secret, err := vp.k8sFramework.GetOrCreateSecret(vc.ingress.GetNamespace(), vc.secretName, vc.ingress.GetLabels(), map[string]string{AnnotationSecretClaimedByIngress: vc.getIngressKey()}) - if err != nil { - return errors.Wrapf(err, "couldn't get nor create secret %s", vc.getSecretKey()) - } - - // Check if the given Secret is already being claimed (aka used) by another Ingress. If so, return here and leave it to the ingress that came first. - // Multiple references to a secret can be intentional to re-use the certificate or due to misconfiguration if the host differs. - // In the latter case we prevent the operator from enrolling new certificates over and and over again. - if claimedByIngress, isClaimedByAnotherIngress := isSecretClaimedByAnotherIngress(secret, vc.getIngressKey()); isClaimedByAnotherIngress { - vp.logger.LogInfo("cannot use secret. already in use by another ingress", "secret", vc.getSecretKey(), "claimedByIngress", claimedByIngress, "ingress", vc.getIngressKey()) - return nil - } - - // Get the certificate and private key from the secret. - vc.certificate, vc.privateKey = getCertificateAndKeyFromSecret(secret, tlsKeySecretKey, tlsCertSecretKey) - - // Add finalizer before creating anything. Return error if this fails. - if err := vp.k8sFramework.EnsureVicePresidentFinalizerExists(vc.ingress); err != nil { - return errors.Wrapf(err, "will not create certificate in this cycle. failed to add finalizer %v", vp.Finalizer) - } - - // There was an attempt to delete the ingress. Remove the claim on the secret and the finalizer. - if k8sutils.IngressHasDeletionTimestamp(vc.ingress) { - if err := vp.k8sFramework.RemoveSecretAnnotation(secret, AnnotationSecretClaimedByIngress); err != nil { - return err - } - return vp.k8sFramework.EnsureVicePresidentFinalizerRemoved(vc.ingress) - } - - nextState = vp.getNextState(vc) - } - } - return nil -} - -func (vp *Operator) getNextState(vc *ViceCertificate) string { - if vc.certificate == nil && vc.privateKey == nil { - vp.logger.LogInfo("no certificate and/or key found in secret", "key", vc.getSecretKey(), "host", vc.host) - return IngressStateEnroll - } - - if k8sutils.IngressHasAnnotation(vc.ingress, AnnotationCertificateReplacement) { - vp.logger.LogInfo("annotation found on ingress. replacing certificate", "annotation", AnnotationCertificateReplacement, "ingress", vc.getIngressKey(), "host", vc.host) - return IngressStateReplace - } - - if !vc.DoesKeyAndCertificateTally() { - vp.logger.LogInfo("certificate and key don't match", "host", vc.host) - return IngressStateEnroll - } - - // is the certificate for the correct host? - if !vc.DoesCertificateAndHostMatch() { - vp.logger.LogInfo("certificate and hosts don't match", "host", vc.host, "sans", vc.getSANsString()) - return IngressStateEnroll - } - - if vp.EnableValidateRemoteCertificate { - if !vc.DoesRemoteCertificateMatch() { - vp.logger.LogInfo("mismatching remote certificate", "host", vc.host) - return IngressStateEnroll - } - } - - if vc.DoesCertificateExpireSoon(vp.MinCertValidityDays) { - vp.logger.LogInfo("certificate will expire soon", "host", vc.host, "expiresIn[d]", vp.MinCertValidityDays) - return IngressStateRenew - } - - if vc.IsRevoked() { - vp.logger.LogInfo("certificate is revoked", "host", vc.host) - return IngressStateReplace - } - - vp.logger.LogInfo("certificate ist valid", "host", vc.host, "sans", vc.getSANsString(), "validUntil", vc.certificate.NotAfter.UTC().String()) - return IngressStateApproved -} - -// updateCertificateInSecret adds or updates the certificate in a secret -func (vp *Operator) updateCertificateAndKeyInSecret(vc *ViceCertificate, tlsKeySecretKey, tlsCertSecretKey string) error { - secret, err := vp.k8sFramework.GetOrCreateSecret(vc.ingress.GetNamespace(), vc.secretName, vc.ingress.GetLabels(), map[string]string{AnnotationSecretClaimedByIngress: vc.getIngressKey()}) - if err != nil { - return err - } - - vp.logger.LogInfo("add/update certificate to secret", "key", vc.getSecretKey()) - - updatedSecret, err := addCertificateAndKeyToSecret(vc, secret, tlsKeySecretKey, tlsCertSecretKey) - if err != nil { - vp.logger.LogError("couldn't update secret", err, "key", vc.getSecretKey()) - return err - } - - if updatedSecret.Annotations == nil { - updatedSecret.Annotations = map[string]string{} - } - - // Set a claim on the secret for the ingress to prevent other ingress from using it as well. - updatedSecret.Annotations[AnnotationSecretClaimedByIngress] = vc.getIngressKey() - return vp.k8sFramework.UpdateSecret(secret, updatedSecret) -} - -func (vp *Operator) checkCertificates() { - for _, o := range vp.k8sFramework.GetIngressInformerStore().List() { - vp.enqueueItem(o) - } -} - -func (vp *Operator) resetRateLimits() { - vp.rateLimitMap = sync.Map{} - apiRateLimitHitGauge.Reset() -} - -func (vp *Operator) isRateLimitForHostExceeded(viceCert *ViceCertificate) bool { - if vp.RateLimit == -1 { - return false - } - if n, ok := vp.rateLimitMap.LoadOrStore(viceCert.host, 1); ok { - numRequests := n.(int) - if numRequests >= vp.RateLimit { - vp.logger.LogInfo("rate limit reached", "key", viceCert.getIngressKey(), "host", viceCert.host, "max requests", vp.RateLimit, "period", RateLimitPeriod) - apiRateLimitHitGauge.With(prometheus.Labels{ - "ingress": viceCert.getIngressKey(), - "host": viceCert.host, - "sans": viceCert.getSANsString(), - }).Set(1.0) - return true - } - vp.rateLimitMap.Store(viceCert.host, numRequests+1) - } - return false -} - -// enrollCertificateRateLimited triggers the enrollment of a certificate if rate limit is not exceeded -func (vp *Operator) enrollCertificateRateLimited(vc *ViceCertificate) error { - if vp.isRateLimitForHostExceeded(vc) { - return nil - } - return vp.viceClient.enroll(vc) -} - -// renewCertificateRateLimited triggers the renewal of a certificate if rate limit is not exceeded -func (vp *Operator) renewCertificateRateLimited(vc *ViceCertificate) error { - if vp.isRateLimitForHostExceeded(vc) { - return nil - } - return vp.viceClient.renew(vc) -} - -// pickupCertificateRateLimited picks up a given certificate if rate limit is not exceeded -func (vp *Operator) pickupCertificateRateLimited(vc *ViceCertificate) error { - if vp.isRateLimitForHostExceeded(vc) { - return nil - } - return vp.viceClient.pickup(vc) -} - -// replaceCertificateRateLimited triggers the replacement of the certificate if rate limit is not exceeded -func (vp *Operator) replaceCertificateRateLimited(vc *ViceCertificate) error { - if vp.isRateLimitForHostExceeded(vc) { - return nil - } - return vp.viceClient.replace(vc) -} - -func (vp *Operator) enqueueItem(obj interface{}) { - key, err := cache.MetaNamespaceKeyFunc(obj) - if err != nil { - utilruntime.HandleError(err) - return - } - vp.queue.AddRateLimited(key) - vp.logger.LogDebug("adding item to queue", "key", key) -} diff --git a/vice-president/pkg/president/operator_test.go b/vice-president/pkg/president/operator_test.go deleted file mode 100644 index 557746cc7..000000000 --- a/vice-president/pkg/president/operator_test.go +++ /dev/null @@ -1,145 +0,0 @@ -/******************************************************************************* -* -* Copyright 2019 SAP SE -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You should have received a copy of the License along with this -* program. If not, you may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -*******************************************************************************/ - -package president - -import ( - "crypto/rand" - "crypto/rsa" - "testing" - - "github.com/stretchr/testify/suite" - extensionsv1beta1 "k8s.io/api/extensions/v1beta1" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/cache" -) - -const ( - IngressName = "my-ingress" - SecretName = "my-secret" - Namespace = "default" - HostName = "example.com" -) - -func TestMySuite(t *testing.T) { - suite.Run(t, new(TestSuite)) -} - -func (s *TestSuite) TestEnrollCertificate() { - err := s.VP.enrollCertificateRateLimited(s.ViceCert) - s.NoError(err, "there should be no error enrolling a new certificate") - s.Equal("87d1adc3f1f262409092ec31fb09f4c7", s.ViceCert.tid) -} - -func (s *TestSuite) TestRenewCertificate() { - err := s.VP.renewCertificateRateLimited(s.ViceCert) - s.NoError(err, "there should be no error renewing a certificate") - s.Equal("87d1adc3f1f262409092ec31fb09f4c7", s.ViceCert.tid) -} - -func (s *TestSuite) TestApproveCertificate() { - vc := s.ViceCert - vc.tid = "87d1adc3f1f262409092ec31fb09f4c7" - err := s.VP.viceClient.approve(vc) - s.NoError(err, "there should be no error approving a certificate") -} - -func (s *TestSuite) TestPickupCertificate() { - vc := s.ViceCert - vc.tid = "87d1adc3f1f262409092ec31fb09f4c7" - - err := s.VP.pickupCertificateRateLimited(s.ViceCert) - s.NoError(err, "there should be no error picking up a certificate") -} - -func (s *TestSuite) TestGenerateWriteReadPrivateKey() { - key, err := rsa.GenerateKey(rand.Reader, 2048) - s.Require().NoError(err, "there must be no error generating a private key") - - keyPEM, err := writePrivateKeyToPEM(key) - s.NoError(err, "there should be no error writing a private key to PEM format") - - rKey, err := readPrivateKeyFromPEM(keyPEM) - s.NoError(err, "there should be no error reading a private key from PEM format") - - s.Equal(key, rKey, "the keys should be equal after transformation to and from PEM format") -} - -func (s *TestSuite) TestStateMachine() { - ingress := &extensionsv1beta1.Ingress{ - ObjectMeta: metaV1.ObjectMeta{ - Namespace: Namespace, - Name: IngressName, - Annotations: map[string]string{ - "vice-president": "true", - }, - }, - Spec: extensionsv1beta1.IngressSpec{ - TLS: []extensionsv1beta1.IngressTLS{ - { - Hosts: []string{HostName}, - SecretName: SecretName, - }, - }, - }, - } - - // Go for it: Secret doesn't exist. This should result in below error. - key, err := cache.MetaNamespaceKeyFunc(ingress) - s.Require().NoError(err, "there must be no error creating a key from an ingress") - - // Add the ingress to the queue bypassing the informers. - s.VP.queue.Add(key) - - if err := s.VP.syncHandler(key); err != nil { - // TODO: At least the state machine is triggered once. - s.EqualError(err, "couldn't get nor create secret default/my-secret: the server could not find the requested resource (post secrets)") - } -} - -func (s *TestSuite) TestRateLimitExceeded() { - // Set rate limit of 2, meaning the 3rd attempt for the same host must fail. - s.VP.RateLimit = 2 - hostName := "rateLimitedHost" - vc := &ViceCertificate{ - host: hostName, - ingress: &extensionsv1beta1.Ingress{ - ObjectMeta: metaV1.ObjectMeta{ - Namespace: Namespace, - Name: IngressName, - }, - }, - } - s.Assert().NoError(s.VP.enrollCertificateRateLimited(vc)) - nReq, ok := s.VP.rateLimitMap.Load(hostName) - s.Assert().True(ok) - s.Assert().Equal(1, nReq.(int)) - - s.Assert().NoError(s.VP.enrollCertificateRateLimited(vc)) - nReq, ok = s.VP.rateLimitMap.Load(hostName) - s.Assert().True(ok) - s.Assert().Equal(2, nReq.(int)) - - // 3rd enrollment is expected to be skipped since the limit of 2 requests for the host was reached. - // this is logged and the number of requests is not incremented - s.Assert().NoError(s.VP.enrollCertificateRateLimited(vc)) - nReq, ok = s.VP.rateLimitMap.Load(hostName) - s.Assert().True(ok) - s.Assert().Equal(2, nReq.(int)) -} diff --git a/vice-president/pkg/president/secret.go b/vice-president/pkg/president/secret.go deleted file mode 100644 index 16e4b65ee..000000000 --- a/vice-president/pkg/president/secret.go +++ /dev/null @@ -1,88 +0,0 @@ -/******************************************************************************* -* -* Copyright 2019 SAP SE -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You should have received a copy of the License along with this -* program. If not, you may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -*******************************************************************************/ - -package president - -import ( - "crypto/rsa" - "crypto/x509" - corev1 "k8s.io/api/core/v1" -) - -// getCertificateAndKeyFromSecret extracts the certificate and private key from a given secrets spec -func getCertificateAndKeyFromSecret(secret *corev1.Secret, tlsKeySecretKey, tlsCertSecretKey string) (*x509.Certificate, *rsa.PrivateKey) { - var ( - certificate *x509.Certificate - privateKey *rsa.PrivateKey - ) - - if secret.Data == nil { - return nil, nil - } - - if k, ok := secret.Data[tlsKeySecretKey]; ok && len(k) > 0 { - key, err := readPrivateKeyFromPEM(k) - if err != nil { - return nil, nil - } - privateKey = key - } - - if c, ok := secret.Data[tlsCertSecretKey]; ok && len(c) > 0 { - cert, err := readCertificateFromPEM(c) - if err != nil { - // Private key found in secret. We might be able to pickup the certificate. - return nil, privateKey - } - certificate = cert - } - - return certificate, privateKey -} - -// addCertificateAndKeyToSecret adds the given certificate and private key to the secret. -func addCertificateAndKeyToSecret(viceCert *ViceCertificate, oldSecret *corev1.Secret, tlsKeySecretKey, tlsCertSecretKey string) (*corev1.Secret, error) { - certPEM, err := writeCertificatesToPEM(viceCert.withIntermediateCertificate()) - if err != nil { - return nil, err - } - keyPEM, err := writePrivateKeyToPEM(viceCert.privateKey) - if err != nil { - return nil, err - } - - secret := oldSecret.DeepCopy() - if secret.Data == nil { - secret.Data = map[string][]byte{} - } - - secret.Data[tlsCertSecretKey] = removeSpecialCharactersFromPEM(certPEM) - secret.Data[tlsKeySecretKey] = removeSpecialCharactersFromPEM(keyPEM) - - return secret, nil -} - -// isSecretClaimedByAnotherIngress checks whether the given Secret is already claimed at all and if so by a different ingress than the one given. -func isSecretClaimedByAnotherIngress(secret *corev1.Secret, ingressKey string) (string, bool) { - if key, ok := secret.GetAnnotations()[AnnotationSecretClaimedByIngress]; ok { - return key, key != ingressKey - } - // Not claimed. - return "", false -} diff --git a/vice-president/pkg/president/secret_test.go b/vice-president/pkg/president/secret_test.go deleted file mode 100644 index b33e5488a..000000000 --- a/vice-president/pkg/president/secret_test.go +++ /dev/null @@ -1,62 +0,0 @@ -/******************************************************************************* -* -* Copyright 2019 SAP SE -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You should have received a copy of the License along with this -* program. If not, you may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -*******************************************************************************/ - -package president - -import ( - coreV1 "k8s.io/api/core/v1" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func (s *TestSuite) TestUpdateCertificateInSecret() { - secret := &coreV1.Secret{ - ObjectMeta: metaV1.ObjectMeta{ - Name: SecretName, - Namespace: Namespace, - }, - } - - // add - updatedSecret, err := addCertificateAndKeyToSecret(s.ViceCert, secret, SecretTLSKeyType, SecretTLSCertType) - s.NoError(err, "there should be no error storing the certificate and key in the secret") - - // verify - certificate, privateKey := getCertificateAndKeyFromSecret(updatedSecret, SecretTLSKeyType, SecretTLSCertType) - s.Equal(s.ViceCert.certificate, certificate, "the retrieved certificate should be equal to the one initially stored in the secret") - s.Equal(s.ViceCert.privateKey, privateKey, "the retrieved private key should be equal to the one initially stored in the secret") -} - -func (s *TestSuite) TestGetCertificateFromSecret() { - certificate, privateKey := getCertificateAndKeyFromSecret( - &coreV1.Secret{ - ObjectMeta: metaV1.ObjectMeta{ - Namespace: Namespace, - Name: SecretName, - }, - Data: map[string][]byte{ - SecretTLSCertType: s.CertByte, - SecretTLSKeyType: s.KeyByte, - }, - }, - SecretTLSKeyType, SecretTLSCertType, - ) - - s.Equal(s.Cert, certificate, "should be equal to the certificate from the secret") - s.Equal(s.Key, privateKey, "should be equal to the private key from the secret") -} diff --git a/vice-president/pkg/president/symantecMetricCollector.go b/vice-president/pkg/president/symantecMetricCollector.go deleted file mode 100644 index e070023f3..000000000 --- a/vice-president/pkg/president/symantecMetricCollector.go +++ /dev/null @@ -1,191 +0,0 @@ -/******************************************************************************* -* -* Copyright 2019 SAP SE -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You should have received a copy of the License along with this -* program. If not, you may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -*******************************************************************************/ - -package president - -import ( - "context" - "crypto/tls" - "crypto/x509" - "fmt" - "github.com/sapcc/kubernetes-operators/vice-president/pkg/config" - "time" - - "github.com/prometheus/client_golang/prometheus" - "github.com/sapcc/go-vice" - "github.com/sapcc/kubernetes-operators/vice-president/pkg/log" -) - -const ( - tokenCountOrdered = "token_count_ordered" - tokenCountUsed = "token_count_used" - tokenCountRemaining = "token_count_remaining" - organizationExpires = "organization_expires" - ssoCertExpires = "sso_certificate_expires" -) - -// SymantecMetricsCollector .. -type SymantecMetricsCollector struct { - prometheus.Collector - - viceClient *vice.Client - ssoCertificate *x509.Certificate - metrics map[string]*prometheus.Desc - logger log.Logger -} - -// NewSymantecMetricsCollector returns a new collector for Symantec metrics. -func NewSymantecMetricsCollector(options config.Options, logger log.Logger) *SymantecMetricsCollector { - cert, err := tls.LoadX509KeyPair(options.ViceCrtFile, options.ViceKeyFile) - if err != nil { - logger.LogFatal("couldn't load sso certificate", "cert path", options.ViceCrtFile, "key path", options.ViceKeyFile, "err", err) - } - - certX509, err := readCertFromFile(options.ViceCrtFile) - if err != nil { - logger.LogFatal("couldn't load sso certificate", "cert path", options.ViceCrtFile, "key path", options.ViceKeyFile, "err", err) - } - - return &SymantecMetricsCollector{ - viceClient: vice.New(cert), - logger: logger, - ssoCertificate: certX509, - metrics: map[string]*prometheus.Desc{ - tokenCountOrdered: newTokenCountMetricDesc("ordered", "The number of ordered certificate units.", []string{"type"}), - tokenCountUsed: newTokenCountMetricDesc("used", "The number of used certificate units.", []string{"type"}), - tokenCountRemaining: newTokenCountMetricDesc("remaining", "The number of remaining certificate units.", []string{"type"}), - organizationExpires: newOrgMetricDesc("expires", "The Symantec organization expiration date.", []string{"name", "status", "auth_status"}), - ssoCertExpires: newSSOCertExpiresDesc("expires", "The expiry of the SSO certificate in unix time."), - }, - } -} - -func newTokenCountMetricDesc(metricName, docString string, labels []string) *prometheus.Desc { - return newMetricDesc("token_count", metricName, docString, labels) -} - -func newOrgMetricDesc(metricName, docString string, labels []string) *prometheus.Desc { - return newMetricDesc("organization", metricName, docString, labels) -} - -func newMetricDesc(subSystem, metricName, docString string, labels []string) *prometheus.Desc { - return prometheus.NewDesc( - prometheus.BuildFQName(MetricNamespace, subSystem, metricName), - docString, - labels, - nil, - ) -} - -func newSSOCertExpiresDesc(metricName, docString string) *prometheus.Desc { - return prometheus.NewDesc( - prometheus.BuildFQName(MetricNamespace, "sso_certificate", metricName), - docString, - nil, - nil, - ) -} - -// Describe .. -func (m *SymantecMetricsCollector) Describe(ch chan<- *prometheus.Desc) { - for _, metric := range m.metrics { - ch <- metric - } -} - -// Collect .. -func (m *SymantecMetricsCollector) Collect(ch chan<- prometheus.Metric) { - - // Check the SSO certificate expiry. - ch <- prometheus.MustNewConstMetric( - m.metrics[ssoCertExpires], - prometheus.GaugeValue, - float64(m.ssoCertificate.NotAfter.Unix()), - ) - if !time.Now().UTC().Before(m.ssoCertificate.NotAfter.UTC()) { - m.logger.LogInfo("the sso certificate expired", "notAfter", m.ssoCertificate.NotAfter.UTC().String()) - } - - // Get the number of used and available Symantec tokens. - tokenCount, err := m.viceClient.Certificates.GetTokenCount(context.TODO()) - if err != nil { - m.logger.LogError("unable to fetch token count", err) - return - } - if tokenCount == nil || tokenCount.Tokens == nil { - m.logger.LogInfo("fetched Token count could'nt parse it", "tokens", tokenCount) - return - } - - for _, t := range tokenCount.Tokens { - if t.Ordered == 0 && t.Used == 0 && t.Remaining == 0 { - m.logger.LogDebug("token count is 0", "product type", t.Type) - } else { - m.logger.LogDebug("got token count", "count", t) - - ch <- prometheus.MustNewConstMetric( - m.metrics[tokenCountOrdered], - prometheus.GaugeValue, - float64(t.Ordered), - string(t.Type), - ) - - ch <- prometheus.MustNewConstMetric( - m.metrics[tokenCountUsed], - prometheus.GaugeValue, - float64(t.Used), - string(t.Type), - ) - - ch <- prometheus.MustNewConstMetric( - m.metrics[tokenCountRemaining], - prometheus.GaugeValue, - float64(t.Remaining), - string(t.Type), - ) - } - } - - // Gets the expiry of the Organization in Symantec. - o, err := m.viceClient.Certificates.GetOrganizationInfo(context.TODO()) - if err != nil { - m.logger.LogError("unable to fetch symantec organization information", err) - return - } - if o == nil { - m.logger.LogInfo("unable to parse symantec org info") - return - } - - org := o.Organization - if isAnyStringEmpty(org.Name, org.OrgStatus, org.AuthStatus) { - m.logger.LogInfo("unable to parse some values from symatec org") - return - } - - m.logger.LogDebug("got org info", fmt.Sprintf("%v", org)) - ch <- prometheus.MustNewConstMetric( - m.metrics[organizationExpires], - prometheus.GaugeValue, - float64(org.AuthExpires.Unix()), - org.Name, - org.OrgStatus, - org.AuthStatus, - ) -} diff --git a/vice-president/pkg/president/test_util.go b/vice-president/pkg/president/test_util.go deleted file mode 100644 index 811df430c..000000000 --- a/vice-president/pkg/president/test_util.go +++ /dev/null @@ -1,239 +0,0 @@ -/******************************************************************************* -* -* Copyright 2019 SAP SE -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You should have received a copy of the License along with this -* program. If not, you may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -*******************************************************************************/ - -package president - -import ( - "crypto/rsa" - "crypto/x509" - "encoding/pem" - "fmt" - "github.com/sapcc/kubernetes-operators/vice-president/pkg/config" - "io/ioutil" - "net/http" - "net/url" - "os" - "path" - "strconv" - - "github.com/sapcc/kubernetes-operators/vice-president/pkg/log" - "github.com/stretchr/testify/suite" - coreV1 "k8s.io/api/core/v1" - extensionsv1beta1 "k8s.io/api/extensions/v1beta1" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -const ( - // FIXTURES path to the subfolder containing fixtures. - FIXTURES = "fixtures" - - // TESTPORT the port used by the MockServer. - TESTPORT = 8001 -) - -// TestSuite .. -type TestSuite struct { - suite.Suite - VP *Operator - HTTPMux *http.ServeMux - TestPort int - Cert *x509.Certificate - CertByte []byte - Key *rsa.PrivateKey - KeyByte []byte - Secret *coreV1.Secret - ViceCert *ViceCertificate - IntermediateCertByte []byte -} - -// SetupMockEndpoints defines the endpoints available during mock tests -func (s *TestSuite) SetupMockEndpoints() { - headersEnroll := map[string]string{ - "Content-Type": "application/xml;charset=UTF-8", - "Content-Length": "2042", - } - - s.respondWith("/vswebservices/rest/services/enroll", 200, headersEnroll, "enrollCertificateResponse.xml") - - headersRenew := map[string]string{ - "Content-Type": "text/xml;charset=UTF-8", - "Content-Length": "1761", - } - s.respondWith("/vswebservices/rest/services/renew", 200, headersRenew, "renewCertificateResponse.xml") - - headersApprove := map[string]string{ - "Content-Type": "text/xml;charset=UTF-8", - "Content-Length": "1761", - } - s.respondWith("/vswebservices/rest/services/approve", 200, headersApprove, "approveCertificateResponse.xml") - - headersPickup := map[string]string{ - "Content-Type": "text/xml;charset=UTF-8", - "Content-Length": "1761", - } - s.respondWith("/vswebservices/rest/services/pickup", 200, headersPickup, "pickupCertificateResponse.xml") -} - -// SetupSuite creates a new TestSuite. -func (s *TestSuite) SetupSuite() { - s.T().Logf("Initializing TestSuite") - testPort := strconv.Itoa(TESTPORT) - var err error - - s.CertByte, err = s.readFixture("example.pem") - if err != nil { - s.T().Log("Couldn't read example.pem") - } - certBlock, _ := pem.Decode(s.CertByte) - if certBlock == nil { - s.T().Errorf("failed to decode PEM block containing certificate.") - } - s.Cert, err = x509.ParseCertificate(certBlock.Bytes) - if err != nil { - s.T().Errorf("failed to parse certificate: %s", err.Error()) - } - - s.IntermediateCertByte, err = s.readFixture("intermediate.pem") - if err != nil { - s.T().Log("Coulnd't load intermediate.pem") - } - - s.KeyByte, err = s.readFixture("example.key") - if err != nil { - s.T().Log("Couldn't read example.key") - } - keyBlock, _ := pem.Decode(s.KeyByte) - if keyBlock == nil { - s.T().Errorf("Failed to decode PEM block containing the public key") - } - s.Key, err = x509.ParsePKCS1PrivateKey(keyBlock.Bytes) - if err != nil { - s.T().Errorf("Could not parse private key: %s", err.Error()) - } - - intermediateCert, err := readCertFromFile(path.Join(FIXTURES, "intermediate.pem")) - if err != nil { - s.T().Error(err) - } - - s.ViceCert = NewViceCertificate( - &extensionsv1beta1.Ingress{ - ObjectMeta: metaV1.ObjectMeta{ - Namespace: "default", - Name: "my-ingress", - }, - }, - "my-secret", - "www.example.com", - []string{"www.example.com"}, - intermediateCert, - &x509.CertPool{}, - ) - s.ViceCert.privateKey = s.Key - s.ViceCert.certificate = s.Cert - - s.Secret = &coreV1.Secret{ - Type: coreV1.SecretTypeOpaque, - Data: map[string][]byte{ - SecretTLSCertType: append(s.IntermediateCertByte, s.CertByte...), - SecretTLSKeyType: s.KeyByte, - }, - ObjectMeta: metaV1.ObjectMeta{ - Namespace: "default", - Name: "my-secret", - }, - } - - opts := config.Options{ - ViceCrtFile: "fixtures/example.pem", - ViceKeyFile: "fixtures/example.key", - VicePresidentConfig: "fixtures/example.vicepresidentconfig", - KubeConfig: "fixtures/example.kubeconfig", - IntermediateCertificate: "fixtures/intermediate.pem", - } - - // Create vice president. - s.VP, err = New(opts, log.NewLogger(true)) - if err != nil { - s.Fail("failed to create new vice president") - } - - s.VP.viceClient.BaseURL, _ = url.Parse(fmt.Sprintf("http://localhost:%s", testPort)) - - go s.setupMockServer(testPort) -} - -// TearDownSuite tears down the testsuite -func (s *TestSuite) TearDownSuite() { - s.T().Logf("Shutting down TestSuite.") -} - -func (s *TestSuite) setupMockServer(port string) { - s.T().Logf("Starting local mockserver on port %s.", port) - s.HTTPMux = http.NewServeMux() - - s.SetupMockEndpoints() - - if err := http.ListenAndServe(fmt.Sprintf(":%s", port), s.HTTPMux); err != nil { - s.T().Errorf(err.Error()) - } -} - -func (s *TestSuite) readFixture(fileName string) (file []byte, err error) { - pwd, err := os.Getwd() - if err != nil { - s.T().Errorf("Couldn't get current path. %s", err) - return nil, err - } - fullPath := path.Join(pwd, FIXTURES, fileName) - file, err = ioutil.ReadFile(fullPath) - if err != nil { - s.T().Errorf("Couldn't load file %s. %s", fullPath, err) - return nil, err - } - return file, nil -} - -func (s *TestSuite) respondWith(endpoint string, responseCode int, headers map[string]string, filePath string) { - - var _headers map[string]string - - fileContent, err := s.readFixture(filePath) - if err != nil { - s.T().Errorf("Couldn't find fixture %s: %s.", filePath, err) - return - } - - if headers == nil { - _headers = map[string]string{ - "Content-Type": "application/json, */*", - "Encoding": "gzip", - } - } else { - _headers = headers - } - - s.HTTPMux.HandleFunc(endpoint, func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(responseCode) - for k, v := range _headers { - w.Header().Set(k, v) - } - w.Write(fileContent) - }) -} diff --git a/vice-president/pkg/president/util.go b/vice-president/pkg/president/util.go deleted file mode 100644 index b1dc73f40..000000000 --- a/vice-president/pkg/president/util.go +++ /dev/null @@ -1,232 +0,0 @@ -/******************************************************************************* -* -* Copyright 2019 SAP SE -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You should have received a copy of the License along with this -* program. If not, you may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -*******************************************************************************/ - -package president - -import ( - "bytes" - "crypto/rsa" - "crypto/x509" - "encoding/pem" - "fmt" - "io/ioutil" - "net/http" - "os" - "sort" - "strings" - - "github.com/pkg/errors" - "golang.org/x/crypto/ocsp" -) - -func isAnyStringEmpty(s ...string) bool { - if s != nil { - for _, str := range s { - if str == "" { - return true - } - } - } - return false -} - -func readPrivateKeyFromPEM(keyPEM []byte) (*rsa.PrivateKey, error) { - block, _ := pem.Decode(keyPEM) - if block == nil { - return nil, errors.New("failed to decode public key from PEM block") - } - key, err := x509.ParsePKCS1PrivateKey(block.Bytes) - if err != nil { - return nil, errors.Wrap(err, "couldn't parse private key") - } - return key, nil -} - -func writePrivateKeyToPEM(key *rsa.PrivateKey) ([]byte, error) { - keyPEM := pem.EncodeToMemory( - &pem.Block{ - Type: PrivateKeyType, - Headers: nil, - Bytes: x509.MarshalPKCS1PrivateKey(key), - }, - ) - if keyPEM == nil { - return nil, errors.New("couldn't encode private key to PEM block") - } - return keyPEM, nil -} - -func readCertificateFromPEM(certPEM []byte) (*x509.Certificate, error) { - block, _ := pem.Decode(certPEM) - if block == nil { - return nil, errors.New("couldn't decode certificate from PEM block") - } - if block.Type != CertificateType { - return nil, errors.New("certificate contains invalid date") - } - cert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse certificate") - } - return cert, nil -} - -func writeCertificatesToPEM(certs []*x509.Certificate) ([]byte, error) { - certPEMs := []byte{} - - for _, c := range certs { - certByte := removeSpecialCharactersFromPEM( - pem.EncodeToMemory( - &pem.Block{ - Type: CertificateType, - Bytes: c.Raw, - }, - ), - ) - for _, b := range certByte { - certPEMs = append(certPEMs, b) - } - } - - if certPEMs == nil { - return nil, errors.New("couldn't encode certificates") - } - return certPEMs, nil -} - -func readCertFromFile(filePath string) (*x509.Certificate, error) { - certPEM, err := ioutil.ReadFile(filePath) - if err != nil { - return nil, err - } - return readCertificateFromPEM(certPEM) -} - -func removeSpecialCharactersFromPEM(pem []byte) []byte { - specialChars := []string{"\"", "^@", "\x00", "0"} - var result []byte - for _, c := range specialChars { - result = bytes.TrimLeft(pem, fmt.Sprintf("%q\n", c)) - } - return result -} - -func contains(stringSlice []string, searchString string) bool { - for _, value := range stringSlice { - if value == searchString { - return true - } - } - return false -} - -func ocspRevokationReasonToString(status int) string { - switch status { - case ocsp.Unspecified: - return "unspecified" - case ocsp.KeyCompromise: - return "key compromise" - case ocsp.CACompromise: - return "ca compromise" - case ocsp.AffiliationChanged: - return "affiliation changed" - case ocsp.Superseded: - return "superseded" - case ocsp.CessationOfOperation: - return "cessation of operation" - case ocsp.CertificateHold: - return "certificate hold" - } - return "unknown" -} - -func ocspStatusToString(status int) string { - switch status { - case ocsp.Good: - return "good" - case ocsp.Revoked: - return "revoked" - case ocsp.Unknown: - return "unknown" - case ocsp.ServerFailed: - return "server failed" - } - return "unknown" -} - -func downloadAndPersistFile(CAURI string, filePath string) ([]byte, error) { - resp, err := http.Get(CAURI) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - bytes, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - return saveToFile(bytes, filePath) -} - -func getFileNameFromURI(URI string) string { - f := strings.Split(URI, "/") - return f[len(f)-1] -} - -func isStringSlicesEqual(a, b []string) bool { - if len(a) != len(b) { - return false - } - - aCopy := make([]string, len(a)) - copy(aCopy, a) - - bCopy := make([]string, len(b)) - copy(bCopy, b) - - sort.Strings(aCopy) - sort.Strings(bCopy) - - for idx, val := range aCopy { - if bCopy[idx] != val { - return false - } - } - return true -} - -func saveToFile(contentBytes []byte, filePath string) ([]byte, error) { - out, err := os.Create(filePath) - if err != nil { - return nil, err - } - defer out.Close() - - _, err = out.Write(contentBytes) - if err != nil { - return nil, err - } - - return contentBytes, nil -} - -func keyFunc(namespace, name string) string { - return fmt.Sprintf("%s/%s", namespace, name) -} diff --git a/vice-president/pkg/president/vice.go b/vice-president/pkg/president/vice.go deleted file mode 100644 index c198d9631..000000000 --- a/vice-president/pkg/president/vice.go +++ /dev/null @@ -1,252 +0,0 @@ -/******************************************************************************* -* -* Copyright 2019 SAP SE -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You should have received a copy of the License along with this -* program. If not, you may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -*******************************************************************************/ - -package president - -import ( - "bytes" - "context" - "crypto/rand" - "crypto/rsa" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "fmt" - - "github.com/pkg/errors" - "github.com/sapcc/go-vice" - "github.com/sapcc/kubernetes-operators/vice-president/pkg/config" - "github.com/sapcc/kubernetes-operators/vice-president/pkg/log" -) - -type viceClient struct { - *vice.Client - cfg config.VicePresidentConfig - logger log.Logger -} - -func newViceClient(ssoCert tls.Certificate, cfg config.VicePresidentConfig, logger log.Logger) *viceClient { - return &viceClient{ - Client: vice.New(ssoCert), - cfg: cfg, - logger: logger, - } -} - -func (v *viceClient) createCSR(cert *ViceCertificate) error { - key, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - return errors.Wrapf(err, "couldn't generate private key") - } - - csr, err := vice.CreateCSR( - pkix.Name{ - CommonName: cert.host, - Country: []string{v.cfg.Country}, - Province: []string{v.cfg.Province}, - Locality: []string{v.cfg.Locality}, - Organization: []string{v.cfg.Organization}, - OrganizationalUnit: []string{v.cfg.OrganizationalUnit}, - }, - v.cfg.EMail, - cert.getSANs(), - key, - ) - if err != nil { - return errors.Wrapf(err, "couldn't create CSR") - } - cert.csr = csr - cert.privateKey = key - - csrFilePath := fmt.Sprintf("%s/%s.csr", TmpPath, cert.host) - keyFilePath := fmt.Sprintf("%s/%s.key", TmpPath, cert.host) - v.logger.LogDebug("temporarily persisting CSR and key", "csrPath", csrFilePath, "keyPath", keyFilePath) - - _, err = saveToFile(cert.csr, csrFilePath) - if err != nil { - return err - } - - keyByte, err := writePrivateKeyToPEM(cert.privateKey) - if err != nil { - return err - } - _, err = saveToFile(keyByte, keyFilePath) - return err -} - -func (v *viceClient) enroll(cert *ViceCertificate) error { - if err := v.createCSR(cert); err != nil { - return err - } - - enrollment, err := v.Certificates.Enroll( - context.TODO(), - &vice.EnrollRequest{ - FirstName: v.cfg.FirstName, - LastName: v.cfg.LastName, - Email: v.cfg.EMail, - CSR: string(cert.csr), - Challenge: v.cfg.DefaultChallenge, - CertProductType: vice.CertProductType.Server, - ServerType: vice.ServerType.OpenSSL, - ValidityPeriod: vice.ValidityPeriod.OneYear, - SubjectAltNames: cert.getSANs(), - SignatureAlgorithm: vice.SignatureAlgorithm.SHA256WithRSAEncryption, - }, - ) - if err != nil { - return errors.Wrapf(err, "couldn't enroll new certificate for host %v", cert.host) - } - // enrollment will only contain a cert if automatic approval is enabled - if enrollment.Certificate != "" { - if cert.certificate, err = readCertificateFromPEM([]byte(enrollment.Certificate)); err != nil { - return errors.Wrapf(err, "failed to read certificate for host %v", cert.host) - } - } - cert.tid = enrollment.TransactionID - return nil -} - -func (v *viceClient) renew(cert *ViceCertificate) error { - if err := v.createCSR(cert); err != nil { - return err - } - - originalCertificate, err := writeCertificatesToPEM([]*x509.Certificate{cert.certificate}) - if err != nil { - return errors.Wrapf(err, "failed to create certificate for host %s", cert.host) - } - originalCertificate = bytes.TrimSpace(originalCertificate) - - renewal, err := v.Certificates.Renew( - context.TODO(), - &vice.RenewRequest{ - FirstName: v.cfg.FirstName, - LastName: v.cfg.LastName, - Email: v.cfg.EMail, - CSR: string(cert.csr), - SubjectAltNames: cert.certificate.DNSNames, - OriginalChallenge: v.cfg.DefaultChallenge, - Challenge: v.cfg.DefaultChallenge, - OriginalCertificate: string(originalCertificate), - CertProductType: vice.CertProductType.Server, - ServerType: vice.ServerType.OpenSSL, - ValidityPeriod: vice.ValidityPeriod.OneYear, - SignatureAlgorithm: vice.SignatureAlgorithm.SHA256WithRSAEncryption, - }, - ) - if err != nil { - return errors.Wrapf(err, "couldn't renew certificate for host %v", cert.host) - } - // renewal will only contain a cert if automatic approval is enabled - if renewal.Certificate != "" { - if cert.certificate, err = readCertificateFromPEM([]byte(renewal.Certificate)); err != nil { - return errors.Wrapf(err, "failed to read certificate for host %v", cert.host) - } - } - cert.tid = renewal.TransactionID - return nil -} - -func (v *viceClient) pickup(cert *ViceCertificate) error { - if cert.tid == "" { - return fmt.Errorf("cannot pick up a certificate for host %v without its transaction ID", cert.host) - } - - pickup, err := v.Certificates.Pickup( - context.TODO(), - &vice.PickupRequest{ - TransactionID: cert.tid, - }, - ) - if err != nil { - return errors.Wrapf(err, "couldn't pickup certificate for host %v with TID %s", cert.host, cert.tid) - } - - pickedUpCert, err := readCertificateFromPEM([]byte(pickup.Certificate)) - if err != nil { - return errors.Wrapf(err, "failed to read certificate for host %v", cert.host) - } - cert.certificate = pickedUpCert - return nil -} - -func (v *viceClient) approve(cert *ViceCertificate) error { - if cert.tid == "" { - return fmt.Errorf("cannot approve a certificate for host %s without its Transaction ID", cert.host) - } - approval, err := v.Certificates.Approve( - context.TODO(), - &vice.ApprovalRequest{ - TransactionID: cert.tid, - }, - ) - if err != nil { - return errors.Wrapf(err, "couldn't approve certificate for host %s using TID %s", cert.host, cert.tid) - } - if approval.Certificate == "" { - return fmt.Errorf("approval didn't contain a certificate for host %s using TID %s", cert.host, cert.tid) - } - approvedCert, err := readCertificateFromPEM([]byte(approval.Certificate)) - if err != nil { - return errors.Wrapf(err, "failed to read certificate for host %s", cert.host) - } - cert.certificate = approvedCert - return nil -} - -func (v *viceClient) replace(cert *ViceCertificate) error { - if cert.csr == nil { - if err := v.createCSR(cert); err != nil { - return err - } - } - - originalCertificate, err := writeCertificatesToPEM([]*x509.Certificate{cert.certificate}) - if err != nil { - return errors.Wrapf(err, "failed to create certificate for host %s", cert.host) - } - originalCertificate = bytes.TrimSpace(originalCertificate) - - replacement, err := v.Certificates.Replace( - context.TODO(), - &vice.ReplaceRequest{ - OriginalCertificate: string(originalCertificate), - OriginalChallenge: v.cfg.DefaultChallenge, - Challenge: v.cfg.DefaultChallenge, - Reason: ReasonSuperseded, - CSR: string(cert.csr), - FirstName: v.cfg.FirstName, - LastName: v.cfg.LastName, - Email: v.cfg.EMail, - ServerType: vice.ServerType.OpenSSL, - SignatureAlgorithm: vice.SignatureAlgorithm.SHA256WithRSAEncryption, - }, - ) - - // replacement will only contain a cert if automatic approval is enabled - if replacement.Certificate != "" { - if cert.certificate, err = readCertificateFromPEM([]byte(replacement.Certificate)); err != nil { - return errors.Wrapf(err, "failed to read certificate for host %v", cert.host) - } - } - cert.tid = replacement.TransactionID - return nil -}