mirror of
https://github.com/wahyd4/cert-manager.git
synced 2026-08-09 05:06:38 +10:00
Use standard golang http server for webhook
Signed-off-by: James Munnelly <james@munnelly.eu>
This commit is contained in:
@@ -9526,215 +9526,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/openshift/generic-admission-server licensed under: =
|
||||
|
||||
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.
|
||||
|
||||
= vendor/github.com/openshift/generic-admission-server/LICENSE e3fc50a88d0a364313df4b21ef20c29e
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/pborman/uuid licensed under: =
|
||||
|
||||
|
||||
@@ -18,9 +18,10 @@ go_library(
|
||||
"//pkg/logs:go_default_library",
|
||||
"//pkg/webhook:go_default_library",
|
||||
"//pkg/webhook/handlers:go_default_library",
|
||||
"@com_github_openshift_generic_admission_server//pkg/cmd:go_default_library",
|
||||
"//pkg/webhook/server:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/runtime/schema:go_default_library",
|
||||
"@io_k8s_klog//:go_default_library",
|
||||
"@io_k8s_klog//klogr:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
+67
-42
@@ -18,23 +18,38 @@ package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/openshift/generic-admission-server/pkg/cmd"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/klog"
|
||||
"k8s.io/klog/klogr"
|
||||
|
||||
"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
|
||||
"github.com/jetstack/cert-manager/pkg/logs"
|
||||
"github.com/jetstack/cert-manager/pkg/webhook"
|
||||
"github.com/jetstack/cert-manager/pkg/webhook/handlers"
|
||||
"github.com/jetstack/cert-manager/pkg/webhook/server"
|
||||
)
|
||||
|
||||
var (
|
||||
GroupName = "webhook." + v1alpha2.SchemeGroupVersion.Group
|
||||
|
||||
securePort int
|
||||
healthzPort int
|
||||
tlsCertFile string
|
||||
tlsKeyFile string
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.IntVar(&healthzPort, "healthz-port", 6080, "port number to listen on for insecure healthz connections")
|
||||
flag.IntVar(&securePort, "secure-port", 6443, "port number to listen on for secure TLS connections")
|
||||
flag.StringVar(&tlsCertFile, "tls-cert-file", "", "path to the file containing the TLS certificate to serve with")
|
||||
flag.StringVar(&tlsKeyFile, "tls-private-key-file", "", "path to the file containing the TLS private key to serve with")
|
||||
}
|
||||
|
||||
var (
|
||||
validationFuncs = map[schema.GroupVersionKind]handlers.ValidationFunc{
|
||||
v1alpha2.SchemeGroupVersion.WithKind(v1alpha2.CertificateKind): webhook.ValidateCertificate,
|
||||
@@ -44,50 +59,60 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
var validationHook cmd.ValidatingAdmissionHook = handlers.NewFuncBackedValidator(logs.Log, GroupName, webhook.Scheme, validationFuncs)
|
||||
var mutationHook cmd.MutatingAdmissionHook = handlers.NewSchemeBackedDefaulter(logs.Log, GroupName, webhook.Scheme)
|
||||
var validationHook handlers.ValidatingAdmissionHook = handlers.NewFuncBackedValidator(logs.Log, GroupName, webhook.Scheme, validationFuncs)
|
||||
var mutationHook handlers.MutatingAdmissionHook = handlers.NewSchemeBackedDefaulter(logs.Log, GroupName, webhook.Scheme)
|
||||
|
||||
func main() {
|
||||
// Avoid "logging before flag.Parse" errors from glog
|
||||
flag.CommandLine.Parse([]string{})
|
||||
klog.InitFlags(flag.CommandLine)
|
||||
flag.Parse()
|
||||
|
||||
// parse the command line flags to pull out the tls-cert-file
|
||||
// argument. This flag will be parsed by code inside cmd.RunAdmissionServer
|
||||
// so no need to pass it through the call stack or have nice errors
|
||||
tlsflagSet := flag.NewFlagSet("tls", flag.ContinueOnError)
|
||||
tlsflagVal := tlsflagSet.String("tls-cert-file", "", "")
|
||||
tlsflagSet.Parse(os.Args[1:])
|
||||
if *tlsflagVal != "" {
|
||||
runfilewatch(*tlsflagVal)
|
||||
}
|
||||
log := klogr.New()
|
||||
stopCh := setupSignalHandler()
|
||||
|
||||
cmd.RunAdmissionServer(
|
||||
validationHook,
|
||||
mutationHook,
|
||||
)
|
||||
}
|
||||
|
||||
func runfilewatch(filename string) {
|
||||
info, err := os.Stat(filename)
|
||||
if err != nil {
|
||||
// missing TLS cert file will get turned into a proper error later
|
||||
return
|
||||
}
|
||||
modtime := info.ModTime()
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(1 * time.Minute)
|
||||
info, err := os.Stat(filename)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if info.ModTime().After(modtime) {
|
||||
// let the k8s scheduler restart us
|
||||
// TODO(dmo): figure out if there's a way to do this with clean
|
||||
// shutdown
|
||||
klog.Infof("Detected change in TLS certificate %s. Restarting to pick up new certificate", filename)
|
||||
os.Exit(0)
|
||||
}
|
||||
var source server.CertificateSource
|
||||
if tlsCertFile == "" || tlsKeyFile == "" {
|
||||
log.Info("warning: serving insecurely as tls certificate data not provided")
|
||||
} else {
|
||||
log.Info("enabling TLS as certificate file flags specified")
|
||||
source = &server.FileCertificateSource{
|
||||
CertPath: tlsCertFile,
|
||||
KeyPath: tlsKeyFile,
|
||||
Log: log,
|
||||
}
|
||||
}
|
||||
|
||||
srv := server.Server{
|
||||
ListenAddr: fmt.Sprintf(":%d", securePort),
|
||||
HealthzAddr: fmt.Sprintf(":%d", healthzPort),
|
||||
CertificateSource: source,
|
||||
ValidationWebhook: validationHook,
|
||||
MutationWebhook: mutationHook,
|
||||
Log: log,
|
||||
}
|
||||
if err := srv.Run(stopCh); err != nil {
|
||||
log.Error(err, "error running server")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
var shutdownSignals = []os.Signal{os.Interrupt, syscall.SIGTERM}
|
||||
var onlyOneSignalHandler = make(chan struct{})
|
||||
|
||||
// setupSignalHandler registered for SIGTERM and SIGINT. A stop channel is returned
|
||||
// which is closed on one of these signals. If a second signal is caught, the program
|
||||
// is terminated with exit code 1.
|
||||
func setupSignalHandler() (stopCh <-chan struct{}) {
|
||||
close(onlyOneSignalHandler) // panics when called twice
|
||||
|
||||
stop := make(chan struct{})
|
||||
c := make(chan os.Signal, 2)
|
||||
signal.Notify(c, shutdownSignals...)
|
||||
go func() {
|
||||
<-c
|
||||
close(stop)
|
||||
<-c
|
||||
os.Exit(1) // second signal. Exit directly.
|
||||
}()
|
||||
|
||||
return stop
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
{{- if .Values.webhook.enabled -}}
|
||||
apiVersion: apiregistration.k8s.io/v1beta1
|
||||
kind: APIService
|
||||
metadata:
|
||||
name: v1beta1.webhook.cert-manager.io
|
||||
labels:
|
||||
app: {{ include "webhook.name" . }}
|
||||
app.kubernetes.io/name: {{ include "webhook.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
helm.sh/chart: {{ include "webhook.chart" . }}
|
||||
annotations:
|
||||
cert-manager.io/inject-ca-from-secret: "{{ .Release.Namespace }}/{{ include "webhook.servingCertificate" . }}"
|
||||
spec:
|
||||
group: webhook.cert-manager.io
|
||||
groupPriorityMinimum: 1000
|
||||
versionPriority: 15
|
||||
service:
|
||||
name: {{ include "webhook.fullname" . }}
|
||||
namespace: "{{ .Release.Namespace }}"
|
||||
version: v1beta1
|
||||
{{- end -}}
|
||||
@@ -53,6 +53,16 @@ spec:
|
||||
{{- if .Values.webhook.extraArgs }}
|
||||
{{ toYaml .Values.webhook.extraArgs | indent 10 }}
|
||||
{{- end }}
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /livez
|
||||
port: 6080
|
||||
scheme: HTTP
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 6080
|
||||
scheme: HTTP
|
||||
env:
|
||||
- name: POD_NAMESPACE
|
||||
valueFrom:
|
||||
|
||||
@@ -10,9 +10,7 @@ metadata:
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
helm.sh/chart: {{ include "webhook.chart" . }}
|
||||
annotations:
|
||||
{{- if .Values.webhook.injectAPIServerCA }}
|
||||
cert-manager.io/inject-apiserver-ca: "true"
|
||||
{{- end }}
|
||||
cert-manager.io/inject-ca-from-secret: "{{ .Release.Namespace }}/{{ include "webhook.servingCertificate" . }}"
|
||||
webhooks:
|
||||
- name: webhook.cert-manager.io
|
||||
rules:
|
||||
@@ -34,7 +32,7 @@ webhooks:
|
||||
sideEffects: None
|
||||
clientConfig:
|
||||
service:
|
||||
name: kubernetes
|
||||
namespace: default
|
||||
path: /apis/webhook.cert-manager.io/v1beta1/mutations
|
||||
name: {{ include "webhook.fullname" . }}
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
path: /mutate
|
||||
{{- end -}}
|
||||
|
||||
@@ -10,9 +10,7 @@ metadata:
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
helm.sh/chart: {{ include "webhook.chart" . }}
|
||||
annotations:
|
||||
{{- if .Values.webhook.injectAPIServerCA }}
|
||||
cert-manager.io/inject-apiserver-ca: "true"
|
||||
{{- end }}
|
||||
cert-manager.io/inject-ca-from-secret: "{{ .Release.Namespace }}/{{ include "webhook.servingCertificate" . }}"
|
||||
webhooks:
|
||||
- name: webhook.cert-manager.io
|
||||
namespaceSelector:
|
||||
@@ -42,7 +40,7 @@ webhooks:
|
||||
sideEffects: None
|
||||
clientConfig:
|
||||
service:
|
||||
name: kubernetes
|
||||
namespace: default
|
||||
path: /apis/webhook.cert-manager.io/v1beta1/validations
|
||||
name: {{ include "webhook.fullname" . }}
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
path: /mutate
|
||||
{{- end -}}
|
||||
|
||||
@@ -77,7 +77,6 @@ require (
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.1 // indirect
|
||||
github.com/opencontainers/runc v0.1.1 // indirect
|
||||
github.com/openshift/generic-admission-server v1.14.0
|
||||
github.com/ory/dockertest v3.3.4+incompatible // indirect
|
||||
github.com/pascaldekloe/goe v0.1.0 // indirect
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||
@@ -114,8 +113,6 @@ require (
|
||||
sigs.k8s.io/testing_frameworks v0.1.1
|
||||
)
|
||||
|
||||
replace github.com/openshift/generic-admission-server => github.com/openshift/generic-admission-server v1.14.0
|
||||
|
||||
replace sigs.k8s.io/controller-runtime => github.com/munnerz/controller-runtime v0.1.8-0.20190907105316-d02b94982e57
|
||||
|
||||
replace github.com/evanphx/json-patch => github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550
|
||||
|
||||
@@ -400,8 +400,6 @@ github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVo
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
|
||||
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/openshift/generic-admission-server v1.14.0 h1:GAQy5JNVcbmUuIpPvLd39+2rPecxEm7WQ2sP7ACrse4=
|
||||
github.com/openshift/generic-admission-server v1.14.0/go.mod h1:GD9KN/W4KxqRQGVMbqQHpHzb2XcQVvLCaBaSciqXvfM=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/ory/dockertest v3.3.4+incompatible h1:VrpM6Gqg7CrPm3bL4Wm1skO+zFWLbh7/Xb5kGEbJRh8=
|
||||
github.com/ory/dockertest v3.3.4+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs=
|
||||
|
||||
@@ -1331,15 +1331,6 @@ def go_repositories():
|
||||
sum = "h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=",
|
||||
version = "v0.1.1",
|
||||
)
|
||||
go_repository(
|
||||
name = "com_github_openshift_generic_admission_server",
|
||||
build_file_generation = "on",
|
||||
build_file_proto_mode = "disable",
|
||||
importpath = "github.com/openshift/generic-admission-server",
|
||||
replace = "github.com/openshift/generic-admission-server",
|
||||
sum = "h1:GAQy5JNVcbmUuIpPvLd39+2rPecxEm7WQ2sP7ACrse4=",
|
||||
version = "v1.14.0",
|
||||
)
|
||||
go_repository(
|
||||
name = "com_github_ory_dockertest",
|
||||
build_file_generation = "on",
|
||||
|
||||
@@ -29,6 +29,7 @@ filegroup(
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/webhook/handlers:all-srcs",
|
||||
"//pkg/webhook/server:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
|
||||
@@ -3,6 +3,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"interfaces.go",
|
||||
"mutation.go",
|
||||
"validation.go",
|
||||
],
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
Copyright 2019 The Jetstack cert-manager contributors.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package handlers
|
||||
|
||||
import (
|
||||
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
type AdmissionHook interface {
|
||||
// Initialize is called as a post-start hook
|
||||
Initialize(kubeClientConfig *restclient.Config, stopCh <-chan struct{}) error
|
||||
}
|
||||
|
||||
type ValidatingAdmissionHook interface {
|
||||
AdmissionHook
|
||||
|
||||
// ValidatingResource is the resource to use for hosting your admission webhook. If the hook implements
|
||||
// MutatingAdmissionHook as well, the two resources for validating and mutating admission must be different.
|
||||
// Note: this is (usually) not the same as the payload resource!
|
||||
ValidatingResource() (plural schema.GroupVersionResource, singular string)
|
||||
|
||||
// Validate is called to decide whether to accept the admission request. The returned AdmissionResponse
|
||||
// must not use the Patch field.
|
||||
Validate(admissionSpec *admissionv1beta1.AdmissionRequest) *admissionv1beta1.AdmissionResponse
|
||||
}
|
||||
|
||||
type MutatingAdmissionHook interface {
|
||||
AdmissionHook
|
||||
|
||||
// MutatingResource is the resource to use for hosting your admission webhook. If the hook implements
|
||||
// ValidatingAdmissionHook as well, the two resources for validating and mutating admission must be different.
|
||||
// Note: this is (usually) not the same as the payload resource!
|
||||
MutatingResource() (plural schema.GroupVersionResource, singular string)
|
||||
|
||||
// Admit is called to decide whether to accept the admission request. The returned AdmissionResponse may
|
||||
// use the Patch field to mutate the object from the passed AdmissionRequest.
|
||||
Admit(admissionSpec *admissionv1beta1.AdmissionRequest) *admissionv1beta1.AdmissionResponse
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"server.go",
|
||||
"source.go",
|
||||
"tls_file_source.go",
|
||||
],
|
||||
importpath = "github.com/jetstack/cert-manager/pkg/webhook/server",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/logs:go_default_library",
|
||||
"//pkg/webhook/handlers:go_default_library",
|
||||
"@com_github_go_logr_logr//:go_default_library",
|
||||
"@io_k8s_api//admission/v1beta1:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/runtime:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/runtime/schema:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/runtime/serializer/json:go_default_library",
|
||||
"@io_k8s_sigs_controller_runtime//pkg/log:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
Copyright 2019 The Jetstack cert-manager contributors.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer/json"
|
||||
crlog "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
||||
"github.com/jetstack/cert-manager/pkg/webhook/handlers"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultScheme = runtime.NewScheme()
|
||||
)
|
||||
|
||||
func init() {
|
||||
admissionv1beta1.AddToScheme(defaultScheme)
|
||||
|
||||
// we need to add the options to empty v1
|
||||
// TODO fix the server code to avoid this
|
||||
metav1.AddToGroupVersion(defaultScheme, schema.GroupVersion{Version: "v1"})
|
||||
|
||||
// TODO: keep the generic API server from wanting this
|
||||
unversioned := schema.GroupVersion{Group: "", Version: "v1"}
|
||||
defaultScheme.AddUnversionedTypes(unversioned,
|
||||
&metav1.Status{},
|
||||
&metav1.APIVersions{},
|
||||
&metav1.APIGroupList{},
|
||||
&metav1.APIGroup{},
|
||||
&metav1.APIResourceList{},
|
||||
)
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
// ListenAddr is the address the HTTP server should listen on
|
||||
// This must be specified.
|
||||
ListenAddr string
|
||||
|
||||
// HealthzAddr is the address the healthz HTTP server should listen on
|
||||
// If not specified, the healthz endpoint will not be exposed.
|
||||
HealthzAddr string
|
||||
|
||||
// Scheme is used to decode/encode request/response payloads.
|
||||
// If not specified, a default scheme that registers the AdmissionReview
|
||||
// resource type will be used.
|
||||
Scheme *runtime.Scheme
|
||||
|
||||
// If specified, the server will listen with TLS using certificates
|
||||
// provided by this CertificateSource.
|
||||
CertificateSource CertificateSource
|
||||
|
||||
ValidationWebhook handlers.ValidatingAdmissionHook
|
||||
MutationWebhook handlers.MutatingAdmissionHook
|
||||
|
||||
// Log is an optional logger to write informational and error messages to.
|
||||
// If not specified, no messages will be logged.
|
||||
Log logr.Logger
|
||||
}
|
||||
|
||||
func (s *Server) Run(stopCh <-chan struct{}) error {
|
||||
if s.Log == nil {
|
||||
s.Log = crlog.NullLogger{}
|
||||
}
|
||||
|
||||
internalStopCh := make(chan struct{})
|
||||
// only close the internalStopCh if it hasn't already been closed
|
||||
shutdown := false
|
||||
defer func() {
|
||||
if !shutdown {
|
||||
close(internalStopCh)
|
||||
}
|
||||
}()
|
||||
|
||||
var healthzChan <-chan error
|
||||
var certSourceChan <-chan error
|
||||
|
||||
// if a HealthzAddr is provided, start the healthz listener
|
||||
if s.HealthzAddr != "" {
|
||||
l, err := net.Listen("tcp", s.HealthzAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/healthz", s.handleHealthz)
|
||||
mux.HandleFunc("/livez", s.handleLivez)
|
||||
s.Log.Info("listening for insecure healthz connections", "address", s.HealthzAddr)
|
||||
healthzChan = s.startServer(l, internalStopCh, mux)
|
||||
}
|
||||
|
||||
// create a listener for actual webhook requests
|
||||
l, err := net.Listen("tcp", s.ListenAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// wrap the listener with TLS if a CertificateSource is provided
|
||||
if s.CertificateSource != nil {
|
||||
s.Log.Info("listening for secure connections", "address", s.ListenAddr)
|
||||
certSourceChan = s.startCertificateSource(internalStopCh)
|
||||
l = tls.NewListener(l, &tls.Config{
|
||||
GetCertificate: s.CertificateSource.GetCertificate,
|
||||
MinVersion: tls.VersionTLS12,
|
||||
PreferServerCipherSuites: true,
|
||||
})
|
||||
} else {
|
||||
s.Log.Info("listening for insecure connections", "address", s.ListenAddr)
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/validate", s.handle(s.validate))
|
||||
mux.HandleFunc("/mutate", s.handle(s.mutate))
|
||||
listenerChan := s.startServer(l, internalStopCh, mux)
|
||||
|
||||
if certSourceChan == nil {
|
||||
certSourceChan = blockingChan(internalStopCh)
|
||||
}
|
||||
if healthzChan == nil {
|
||||
healthzChan = blockingChan(internalStopCh)
|
||||
}
|
||||
|
||||
select {
|
||||
case err = <-healthzChan:
|
||||
case err = <-certSourceChan:
|
||||
case err = <-listenerChan:
|
||||
case <-stopCh:
|
||||
}
|
||||
|
||||
close(internalStopCh)
|
||||
shutdown = true
|
||||
|
||||
s.Log.Info("waiting for server to shutdown")
|
||||
waitForAll(healthzChan, certSourceChan, listenerChan)
|
||||
s.Log.Info("server shutdown successfully")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Server) startServer(l net.Listener, stopCh <-chan struct{}, handle http.Handler) <-chan error {
|
||||
ch := make(chan error)
|
||||
go func() {
|
||||
defer close(ch)
|
||||
|
||||
srv := &http.Server{
|
||||
Handler: handle,
|
||||
}
|
||||
select {
|
||||
case err := <-channelWrapper(func() error { return srv.Serve(l) }):
|
||||
ch <- err
|
||||
case <-stopCh:
|
||||
// allow a fixed 5s for graceful shutdown
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
defer cancel()
|
||||
if err := srv.Shutdown(ctx); err != nil {
|
||||
s.Log.Error(err, "failed to gracefully shutdown http server")
|
||||
ch <- err
|
||||
}
|
||||
s.Log.Info("shutdown HTTP server gracefully")
|
||||
}
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
func (s *Server) startCertificateSource(stopCh <-chan struct{}) <-chan error {
|
||||
fn := func() error {
|
||||
return s.CertificateSource.Run(stopCh)
|
||||
}
|
||||
return channelWrapper(fn)
|
||||
}
|
||||
|
||||
func waitForAll(chs ...<-chan error) error {
|
||||
for _, ch := range chs {
|
||||
if err := <-ch; err != nil {
|
||||
return fmt.Errorf("error waiting for goroutine to exit: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func channelWrapper(fn func() error) <-chan error {
|
||||
ch := make(chan error)
|
||||
go func() {
|
||||
defer close(ch)
|
||||
ch <- fn()
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
// blockingChan returns a 'no-op' error channel.
|
||||
// When stopCh is closed, the error channel will also be closed.
|
||||
func blockingChan(stopCh <-chan struct{}) <-chan error {
|
||||
ch := make(chan error)
|
||||
go func() {
|
||||
defer close(ch)
|
||||
<-stopCh
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
func (s *Server) scheme() *runtime.Scheme {
|
||||
if s.Scheme == nil {
|
||||
return defaultScheme
|
||||
}
|
||||
return s.Scheme
|
||||
}
|
||||
|
||||
func (s *Server) validate(obj runtime.Object) runtime.Object {
|
||||
review := obj.(*admissionv1beta1.AdmissionReview)
|
||||
resp := s.ValidationWebhook.Validate(review.Request)
|
||||
review.Response = resp
|
||||
return review
|
||||
}
|
||||
|
||||
func (s *Server) mutate(obj runtime.Object) runtime.Object {
|
||||
review := obj.(*admissionv1beta1.AdmissionReview)
|
||||
resp := s.MutationWebhook.Admit(review.Request)
|
||||
review.Response = resp
|
||||
return review
|
||||
}
|
||||
|
||||
func (s *Server) handle(inner func(runtime.Object) runtime.Object) func(w http.ResponseWriter, req *http.Request) {
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
defer req.Body.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
s.Log.Error(err, "failed to read request body")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
codec := json.NewSerializerWithOptions(json.DefaultMetaFactory, s.scheme(), s.scheme(), json.SerializerOptions{
|
||||
Pretty: true,
|
||||
})
|
||||
codec.Decode(data, nil, nil)
|
||||
obj, _, err := codec.Decode(data, nil, nil)
|
||||
if err != nil {
|
||||
s.Log.Error(err, "failed to decode request body")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
result := inner(obj)
|
||||
if err := codec.Encode(result, w); err != nil {
|
||||
s.Log.Error(err, "failed to encode response body")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) handleHealthz(w http.ResponseWriter, req *http.Request) {
|
||||
defer req.Body.Close()
|
||||
|
||||
if s.CertificateSource != nil && !s.CertificateSource.Healthy() {
|
||||
s.Log.Info("Health check failed as CertificateSource is unhealthy")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) handleLivez(w http.ResponseWriter, req *http.Request) {
|
||||
defer req.Body.Close()
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
Copyright 2019 The Jetstack cert-manager contributors.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package server
|
||||
|
||||
import "crypto/tls"
|
||||
|
||||
type CertificateSource interface {
|
||||
// GetCertificate returns a Certificate based on the given
|
||||
// ClientHelloInfo. It will only be called if the client supplies SNI
|
||||
// information or if Certificates is empty.
|
||||
//
|
||||
// If GetCertificate is nil or returns nil, then the certificate is
|
||||
// retrieved from NameToCertificate. If NameToCertificate is nil, the
|
||||
// first element of Certificates will be used.
|
||||
GetCertificate(*tls.ClientHelloInfo) (*tls.Certificate, error)
|
||||
|
||||
// Run will start the certificate source.
|
||||
// This may include setting up watches on certificate stores, or any other
|
||||
// kind of background operation.
|
||||
// The Run function should return when stopCh is closed, and may return an
|
||||
// error if an irrecoverable error occurs whilst running.
|
||||
Run(stopCh <-chan struct{}) error
|
||||
|
||||
// Healthy can be used to check the status of the CertificateSource.
|
||||
// It will return true if the source has a certificate available.
|
||||
Healthy() bool
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
Copyright 2019 The Jetstack cert-manager contributors.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
crlog "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
||||
logf "github.com/jetstack/cert-manager/pkg/logs"
|
||||
)
|
||||
|
||||
// FileCertificateSource provides certificate data for a golang HTTP server by
|
||||
// reloading data on disk periodically.
|
||||
type FileCertificateSource struct {
|
||||
// CertPath is the path to the TLS certificate.
|
||||
// This file will be read periodically and will be used as the private key
|
||||
// for TLS connections.
|
||||
CertPath string
|
||||
|
||||
// KeyPath is the path to the private key.
|
||||
// This file will be read periodically and will be used as the private key
|
||||
// for TLS connections.
|
||||
KeyPath string
|
||||
|
||||
// UpdateInterval is how often the CertPath and KeyPath will be checked for
|
||||
// changes.
|
||||
// If not specified, a default of 10s will be used.
|
||||
UpdateInterval time.Duration
|
||||
|
||||
// MaxFailures is the maximum number of times a failure to read data from
|
||||
// disk should be allowed before treating it as fatal.
|
||||
// If not specified, a default of 12 will be used.
|
||||
MaxFailures int
|
||||
|
||||
// Log is an optional logger to write informational and error messages to.
|
||||
// If not specified, no messages will be logged.
|
||||
Log logr.Logger
|
||||
|
||||
cachedCertificate *tls.Certificate
|
||||
cachedCertBytes []byte
|
||||
cachedKeyBytes []byte
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
const defaultUpdateInterval = time.Second * 10
|
||||
const defaultMaxFailures = 12
|
||||
|
||||
var _ CertificateSource = &FileCertificateSource{}
|
||||
|
||||
func (f *FileCertificateSource) Run(stopCh <-chan struct{}) error {
|
||||
if f.Log == nil {
|
||||
f.Log = crlog.NullLogger{}
|
||||
}
|
||||
|
||||
updateInterval := f.UpdateInterval
|
||||
if updateInterval == 0 {
|
||||
updateInterval = defaultUpdateInterval
|
||||
}
|
||||
maxFailures := f.MaxFailures
|
||||
if maxFailures == 0 {
|
||||
maxFailures = defaultMaxFailures
|
||||
}
|
||||
|
||||
// read the certificate data for the first time immediately, but allow
|
||||
// retrying if the first attempt fails
|
||||
if err := f.updateCertificateFromDisk(); err != nil {
|
||||
f.Log.Error(err, "failed to read certificate from disk")
|
||||
}
|
||||
|
||||
failures := 0
|
||||
for {
|
||||
select {
|
||||
case <-stopCh:
|
||||
return nil
|
||||
case <-time.Tick(updateInterval):
|
||||
if err := f.updateCertificateFromDisk(); err != nil {
|
||||
failures++
|
||||
f.Log.Error(err, "failed to update certificate from disk", "failures", failures)
|
||||
if failures >= maxFailures {
|
||||
return fmt.Errorf("failed to update certificate from disk %d times: %v", failures, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
f.Log.V(logf.DebugLevel).Info("refreshed certificate from data on disk")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FileCertificateSource) GetCertificate(*tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
f.lock.Lock()
|
||||
defer f.lock.Unlock()
|
||||
if f.cachedCertificate == nil {
|
||||
return nil, fmt.Errorf("no tls.Certificate available")
|
||||
}
|
||||
return f.cachedCertificate, nil
|
||||
}
|
||||
|
||||
func (f *FileCertificateSource) Healthy() bool {
|
||||
return f.cachedCertificate != nil
|
||||
}
|
||||
|
||||
// updateCertificateFromDisk will read private key and certificate data from
|
||||
// disk and update the cached tls.Certificate if the data on disk has changed.
|
||||
func (f *FileCertificateSource) updateCertificateFromDisk() error {
|
||||
keyData, err := ioutil.ReadFile(f.KeyPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read keyPath: %w", err)
|
||||
}
|
||||
|
||||
certData, err := ioutil.ReadFile(f.CertPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read certPath: %w", err)
|
||||
}
|
||||
|
||||
f.lock.Lock()
|
||||
defer f.lock.Unlock()
|
||||
if bytes.Compare(keyData, f.cachedKeyBytes) == 0 && bytes.Compare(certData, f.cachedCertBytes) == 0 {
|
||||
f.Log.V(logf.DebugLevel).Info("key and certificate on disk have not changed")
|
||||
return nil
|
||||
}
|
||||
f.Log.Info("detected private key or certificate data on disk has changed. reloading certificate")
|
||||
|
||||
cert, err := tls.X509KeyPair(certData, keyData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f.cachedCertBytes = certData
|
||||
f.cachedKeyBytes = keyData
|
||||
f.cachedCertificate = &cert
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user