Merge pull request #4546 from munnerz/webhook-config-api

Support loading webhook config from versioned file
This commit is contained in:
jetstack-bot
2021-12-14 10:09:02 +00:00
committed by GitHub
48 changed files with 2102 additions and 121 deletions
+12 -1
View File
@@ -1,4 +1,4 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
@@ -8,17 +8,21 @@ go_library(
deps = [
"//cmd/util:go_default_library",
"//cmd/webhook/app/options:go_default_library",
"//internal/apis/config/webhook:go_default_library",
"//pkg/logs:go_default_library",
"//pkg/util:go_default_library",
"//pkg/webhook:go_default_library",
"//pkg/webhook/authority:go_default_library",
"//pkg/webhook/configfile:go_default_library",
"//pkg/webhook/handlers:go_default_library",
"//pkg/webhook/server:go_default_library",
"//pkg/webhook/server/tls:go_default_library",
"@com_github_go_logr_logr//:go_default_library",
"@com_github_spf13_cobra//:go_default_library",
"@com_github_spf13_pflag//:go_default_library",
"@io_k8s_client_go//kubernetes:go_default_library",
"@io_k8s_client_go//tools/clientcmd:go_default_library",
"@io_k8s_component_base//cli/flag:go_default_library",
],
)
@@ -39,3 +43,10 @@ filegroup(
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = ["webhook_test.go"],
embed = [":go_default_library"],
deps = ["//cmd/webhook/app/options:go_default_library"],
)
+8 -2
View File
@@ -2,11 +2,17 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["options.go"],
srcs = [
"globalflags.go",
"options.go",
],
importpath = "github.com/jetstack/cert-manager/cmd/webhook/app/options",
visibility = ["//visibility:public"],
deps = [
"//cmd/util:go_default_library",
"//internal/apis/config/webhook:go_default_library",
"//internal/apis/config/webhook/scheme:go_default_library",
"//pkg/apis/config/webhook/v1alpha1:go_default_library",
"//pkg/logs:go_default_library",
"@com_github_spf13_pflag//:go_default_library",
"@io_k8s_component_base//cli/flag:go_default_library",
],
+36
View File
@@ -0,0 +1,36 @@
/*
Copyright 2021 The cert-manager Authors.
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 options
import (
"flag"
"os"
"github.com/spf13/pflag"
"github.com/jetstack/cert-manager/pkg/logs"
)
func AddGlobalFlags(fs *pflag.FlagSet) {
addKlogFlags(fs)
}
func addKlogFlags(fs *pflag.FlagSet) {
local := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
logs.InitLogs(local)
fs.AddGoFlagSet(local)
}
+51 -77
View File
@@ -22,97 +22,71 @@ import (
"github.com/spf13/pflag"
cliflag "k8s.io/component-base/cli/flag"
cmdutil "github.com/jetstack/cert-manager/cmd/util"
config "github.com/jetstack/cert-manager/internal/apis/config/webhook"
configscheme "github.com/jetstack/cert-manager/internal/apis/config/webhook/scheme"
configv1alpha1 "github.com/jetstack/cert-manager/pkg/apis/config/webhook/v1alpha1"
)
const (
// Default port on which /validate, /mutate, /convert endpoints will be served
defaultListeningPort = 6443
// Default health check port
defaultHealthPort = 6080
)
type WebhookOptions struct {
ListenPort int
HealthzPort int
// Path to TLS certificate and private key on disk.
// Both must be specified if either is.
// May not be specified if DynamicServingCASecretNamespace and
// DynamicServingCASecretName are set.
TLSCertFile string
TLSKeyFile string
// Namespace and name of the Secret resource containing the TLS certificate
// used as a CA to sign dynamic serving certificates.
// Both must be specified if either is.
// May not be specified if TLSCertFile and TLSKeyFile are set.
DynamicServingCASecretNamespace string
DynamicServingCASecretName string
// List of DNSNames that must be present on serving certificates.
DynamicServingDNSNames []string
// Optional path to the kubeconfig used to connect to the apiserver when
// using the 'dynamic serving' certificate sources.
// If not specified, in cluster config will be used.
Kubeconfig string
APIServerHost string
// TLSCipherSuites is the list of allowed cipher suites for the server.
// Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).
TLSCipherSuites []string
// MinTLSVersion is the minimum TLS version supported.
// Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).
MinTLSVersion string
// EnablePprof determines whether pprof is enabled.
EnablePprof bool
// Address on which /debug/pprof endpoint will be served if enabled. Default is
// localhost:6060.
PprofAddress string
// WebhookFlags defines options that can only be configured via flags.
type WebhookFlags struct {
// Path to a file containing a WebhookConfiguration resource
Config string
}
func (o *WebhookOptions) AddFlags(fs *pflag.FlagSet) {
// TODO: rename secure-port to listen-port
fs.IntVar(&o.ListenPort, "secure-port", defaultListeningPort, "port number to listen on for secure TLS connections")
fs.IntVar(&o.HealthzPort, "healthz-port", defaultHealthPort, "port number to listen on for insecure healthz connections")
fs.StringVar(&o.TLSCertFile, "tls-cert-file", "", "path to the file containing the TLS certificate to serve with")
fs.StringVar(&o.TLSKeyFile, "tls-private-key-file", "", "path to the file containing the TLS private key to serve with")
fs.StringVar(&o.DynamicServingCASecretNamespace, "dynamic-serving-ca-secret-namespace", "", "namespace of the secret used to store the CA that signs serving certificates")
fs.StringVar(&o.DynamicServingCASecretName, "dynamic-serving-ca-secret-name", "", "name of the secret used to store the CA that signs serving certificates certificates")
fs.StringSliceVar(&o.DynamicServingDNSNames, "dynamic-serving-dns-names", []string{""}, "DNS names that should be present on certificates generated by the dynamic serving CA")
fs.StringVar(&o.Kubeconfig, "kubeconfig", "", "optional path to the kubeconfig used to connect to the apiserver. If not specified, in-cluster-config will be used")
fs.StringVar(&o.APIServerHost, "api-server-host", "", ""+
func NewWebhookFlags() *WebhookFlags {
return &WebhookFlags{}
}
func (f *WebhookFlags) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&f.Config, "config", "", "Path to a file containing a WebhookConfiguration object used to configure the webhook")
}
func ValidateWebhookFlags(f *WebhookFlags) error {
// No validation needed today
return nil
}
func NewWebhookConfiguration() (*config.WebhookConfiguration, error) {
scheme, _, err := configscheme.NewSchemeAndCodecs()
if err != nil {
return nil, err
}
versioned := &configv1alpha1.WebhookConfiguration{}
scheme.Default(versioned)
config := &config.WebhookConfiguration{}
if err := scheme.Convert(versioned, config, nil); err != nil {
return nil, err
}
return config, nil
}
func AddConfigFlags(fs *pflag.FlagSet, c *config.WebhookConfiguration) {
fs.IntVar(c.SecurePort, "secure-port", *c.SecurePort, "port number to listen on for secure TLS connections")
fs.IntVar(c.HealthzPort, "healthz-port", *c.HealthzPort, "port number to listen on for insecure healthz connections")
fs.StringVar(&c.TLSConfig.Filesystem.CertFile, "tls-cert-file", c.TLSConfig.Filesystem.CertFile, "path to the file containing the TLS certificate to serve with")
fs.StringVar(&c.TLSConfig.Filesystem.KeyFile, "tls-private-key-file", c.TLSConfig.Filesystem.KeyFile, "path to the file containing the TLS private key to serve with")
fs.StringVar(&c.TLSConfig.Dynamic.SecretNamespace, "dynamic-serving-ca-secret-namespace", c.TLSConfig.Dynamic.SecretNamespace, "namespace of the secret used to store the CA that signs serving certificates")
fs.StringVar(&c.TLSConfig.Dynamic.SecretName, "dynamic-serving-ca-secret-name", c.TLSConfig.Dynamic.SecretName, "name of the secret used to store the CA that signs serving certificates certificates")
fs.StringSliceVar(&c.TLSConfig.Dynamic.DNSNames, "dynamic-serving-dns-names", c.TLSConfig.Dynamic.DNSNames, "DNS names that should be present on certificates generated by the dynamic serving CA")
fs.StringVar(&c.KubeConfig, "kubeconfig", c.KubeConfig, "optional path to the kubeconfig used to connect to the apiserver. If not specified, in-cluster-config will be used")
fs.StringVar(&c.APIServerHost, "api-server-host", c.APIServerHost, ""+
"Optional apiserver host address to connect to. If not specified, autoconfiguration "+
"will be attempted.")
fs.BoolVar(&o.EnablePprof, "enable-profiling", cmdutil.DefaultEnableProfiling, ""+
fs.BoolVar(&c.EnablePprof, "enable-profiling", c.EnablePprof, ""+
"Enable profiling for controller.")
fs.StringVar(&o.PprofAddress, "profiler-address", cmdutil.DefaultProfilerAddr,
fs.StringVar(&c.PprofAddress, "profiler-address", c.PprofAddress,
"Address of the Go profiler (pprof). This should never be exposed on a public interface. If this flag is not set, the profiler is not run.")
tlsCipherPossibleValues := cliflag.TLSCipherPossibleValues()
fs.StringSliceVar(&o.TLSCipherSuites, "tls-cipher-suites", o.TLSCipherSuites,
fs.StringSliceVar(&c.TLSConfig.CipherSuites, "tls-cipher-suites", c.TLSConfig.CipherSuites,
"Comma-separated list of cipher suites for the server. "+
"If omitted, the default Go cipher suites will be use. "+
"Possible values: "+strings.Join(tlsCipherPossibleValues, ","))
tlsPossibleVersions := cliflag.TLSPossibleVersions()
fs.StringVar(&o.MinTLSVersion, "tls-min-version", o.MinTLSVersion,
fs.StringVar(&c.TLSConfig.MinTLSVersion, "tls-min-version", c.TLSConfig.MinTLSVersion,
"Minimum TLS version supported. "+
"Possible values: "+strings.Join(tlsPossibleVersions, ", "))
}
func FileTLSSourceEnabled(o WebhookOptions) bool {
if o.TLSCertFile != "" || o.TLSKeyFile != "" {
return true
}
return false
}
func DynamicTLSSourceEnabled(o WebhookOptions) bool {
if o.DynamicServingCASecretNamespace != "" || o.DynamicServingCASecretName != "" {
return true
}
return false
}
+1
View File
@@ -13,6 +13,7 @@ go_library(
"//pkg/webhook/server:go_default_library",
"@com_github_spf13_pflag//:go_default_library",
"@io_k8s_apimachinery//pkg/util/wait:go_default_library",
"@io_k8s_utils//pointer:go_default_library",
],
)
+14 -9
View File
@@ -33,6 +33,7 @@ import (
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/utils/pointer"
"github.com/jetstack/cert-manager/cmd/webhook/app"
"github.com/jetstack/cert-manager/cmd/webhook/app/options"
@@ -58,10 +59,14 @@ type ServerOptions struct {
}
func StartWebhookServer(t *testing.T, ctx context.Context, args []string) (ServerOptions, StopFunc) {
// Allow user to override options using flags
var opts options.WebhookOptions
fs := pflag.NewFlagSet("testset", pflag.ExitOnError)
opts.AddFlags(fs)
webhookFlags := options.NewWebhookFlags()
webhookConfig, err := options.NewWebhookConfiguration()
if err != nil {
t.Fatalf("Failed building test webhook config: %v", err)
}
webhookFlags.AddFlags(fs)
options.AddConfigFlags(fs, webhookConfig)
// Parse the arguments passed in into the WebhookOptions struct
fs.Parse(args)
@@ -70,7 +75,7 @@ func StartWebhookServer(t *testing.T, ctx context.Context, args []string) (Serve
if err != nil {
t.Fatal(err)
}
if !options.FileTLSSourceEnabled(opts) && !options.DynamicTLSSourceEnabled(opts) {
if !webhookConfig.TLSConfig.FilesystemConfigProvided() && !webhookConfig.TLSConfig.DynamicConfigProvided() {
// Generate a CA and serving certificate
ca, certificatePEM, privateKeyPEM, err := generateTLSAssets()
if err != nil {
@@ -85,17 +90,17 @@ func StartWebhookServer(t *testing.T, ctx context.Context, args []string) (Serve
t.Fatal(err)
}
opts.TLSKeyFile = filepath.Join(tempDir, "tls.key")
opts.TLSCertFile = filepath.Join(tempDir, "tls.crt")
webhookConfig.TLSConfig.Filesystem.KeyFile = filepath.Join(tempDir, "tls.key")
webhookConfig.TLSConfig.Filesystem.CertFile = filepath.Join(tempDir, "tls.crt")
}
// Listen on a random port number
opts.ListenPort = 0
opts.HealthzPort = 0
webhookConfig.SecurePort = pointer.Int(0)
webhookConfig.HealthzPort = pointer.Int(0)
stopCh := make(chan struct{})
errCh := make(chan error)
srv, err := app.NewServerWithOptions(log, opts)
srv, err := app.NewServerWithOptions(log, *webhookFlags, *webhookConfig)
if err != nil {
t.Fatal(err)
}
+172 -30
View File
@@ -19,18 +19,24 @@ package app
import (
"context"
"fmt"
"os"
"path/filepath"
"github.com/go-logr/logr"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
cliflag "k8s.io/component-base/cli/flag"
cmdutil "github.com/jetstack/cert-manager/cmd/util"
"github.com/jetstack/cert-manager/cmd/webhook/app/options"
config "github.com/jetstack/cert-manager/internal/apis/config/webhook"
logf "github.com/jetstack/cert-manager/pkg/logs"
"github.com/jetstack/cert-manager/pkg/util"
"github.com/jetstack/cert-manager/pkg/webhook"
"github.com/jetstack/cert-manager/pkg/webhook/authority"
"github.com/jetstack/cert-manager/pkg/webhook/configfile"
"github.com/jetstack/cert-manager/pkg/webhook/handlers"
"github.com/jetstack/cert-manager/pkg/webhook/server"
"github.com/jetstack/cert-manager/pkg/webhook/server/tls"
@@ -40,8 +46,8 @@ var validationHook handlers.ValidatingAdmissionHook = handlers.NewRegistryBacked
var mutationHook handlers.MutatingAdmissionHook = handlers.NewRegistryBackedMutator(logf.Log, webhook.Scheme, webhook.MutationRegistry)
var conversionHook handlers.ConversionHook = handlers.NewSchemeBackedConverter(logf.Log, webhook.Scheme)
func NewServerWithOptions(log logr.Logger, opts options.WebhookOptions) (*server.Server, error) {
restcfg, err := clientcmd.BuildConfigFromFlags(opts.APIServerHost, opts.Kubeconfig)
func NewServerWithOptions(log logr.Logger, _ options.WebhookFlags, opts config.WebhookConfiguration) (*server.Server, error) {
restcfg, err := clientcmd.BuildConfigFromFlags(opts.APIServerHost, opts.KubeConfig)
if err != nil {
return nil, err
}
@@ -54,25 +60,25 @@ func NewServerWithOptions(log logr.Logger, opts options.WebhookOptions) (*server
var source tls.CertificateSource
switch {
case options.FileTLSSourceEnabled(opts):
log.V(logf.InfoLevel).Info("using TLS certificate from local filesystem", "private_key_path", opts.TLSKeyFile, "certificate", opts.TLSCertFile)
case opts.TLSConfig.FilesystemConfigProvided():
log.V(logf.InfoLevel).Info("using TLS certificate from local filesystem", "private_key_path", opts.TLSConfig.Filesystem.KeyFile, "certificate", opts.TLSConfig.Filesystem.CertFile)
source = &tls.FileCertificateSource{
CertPath: opts.TLSCertFile,
KeyPath: opts.TLSKeyFile,
CertPath: opts.TLSConfig.Filesystem.CertFile,
KeyPath: opts.TLSConfig.Filesystem.KeyFile,
Log: log,
}
case options.DynamicTLSSourceEnabled(opts):
restcfg, err := clientcmd.BuildConfigFromFlags("", opts.Kubeconfig)
case opts.TLSConfig.DynamicConfigProvided():
restcfg, err := clientcmd.BuildConfigFromFlags("", opts.KubeConfig)
if err != nil {
return nil, err
}
log.V(logf.InfoLevel).Info("using dynamic certificate generating using CA stored in Secret resource", "secret_namespace", opts.DynamicServingCASecretNamespace, "secret_name", opts.DynamicServingCASecretName)
log.V(logf.InfoLevel).Info("using dynamic certificate generating using CA stored in Secret resource", "secret_namespace", opts.TLSConfig.Dynamic.SecretNamespace, "secret_name", opts.TLSConfig.Dynamic.SecretName)
source = &tls.DynamicSource{
DNSNames: opts.DynamicServingDNSNames,
DNSNames: opts.TLSConfig.Dynamic.DNSNames,
Authority: &authority.DynamicAuthority{
SecretNamespace: opts.DynamicServingCASecretNamespace,
SecretName: opts.DynamicServingCASecretName,
SecretNamespace: opts.TLSConfig.Dynamic.SecretNamespace,
SecretName: opts.TLSConfig.Dynamic.SecretName,
RESTConfig: restcfg,
Log: log,
},
@@ -83,13 +89,13 @@ func NewServerWithOptions(log logr.Logger, opts options.WebhookOptions) (*server
}
return &server.Server{
ListenAddr: fmt.Sprintf(":%d", opts.ListenPort),
HealthzAddr: fmt.Sprintf(":%d", opts.HealthzPort),
PprofAddr: opts.PprofAddress,
ListenAddr: fmt.Sprintf(":%d", *opts.SecurePort),
HealthzAddr: fmt.Sprintf(":%d", *opts.HealthzPort),
EnablePprof: opts.EnablePprof,
PprofAddr: opts.PprofAddress,
CertificateSource: source,
CipherSuites: opts.TLSCipherSuites,
MinTLSVersion: opts.MinTLSVersion,
CipherSuites: opts.TLSConfig.CipherSuites,
MinTLSVersion: opts.TLSConfig.MinTLSVersion,
ValidationWebhook: validationHook,
MutationWebhook: mutationHook,
ConversionWebhook: conversionHook,
@@ -97,27 +103,163 @@ func NewServerWithOptions(log logr.Logger, opts options.WebhookOptions) (*server
}, nil
}
const componentWebhook = "webhook"
func NewServerCommand(stopCh <-chan struct{}) *cobra.Command {
var opts options.WebhookOptions
ctx := cmdutil.ContextWithStopCh(context.Background(), stopCh)
ctx = logf.NewContext(ctx, nil, "webhook")
log := logf.FromContext(ctx)
cleanFlagSet := pflag.NewFlagSet(componentWebhook, pflag.ContinueOnError)
// Replaces all instances of `_` in flag names with `-`
cleanFlagSet.SetNormalizeFunc(cliflag.WordSepNormalizeFunc)
webhookFlags := options.NewWebhookFlags()
webhookConfig, err := options.NewWebhookConfiguration()
if err != nil {
log.Error(err, "Failed to create new webhook configuration")
os.Exit(1)
}
cmd := &cobra.Command{
Use: "webhook",
Short: fmt.Sprintf("Webhook component providing API validation, mutation and conversion functionality for cert-manager (%s) (%s)", util.AppVersion, util.AppGitCommit),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmdutil.ContextWithStopCh(context.Background(), stopCh)
ctx = logf.NewContext(ctx, nil, "webhook")
log := logf.FromContext(ctx)
srv, err := NewServerWithOptions(log, opts)
if err != nil {
return err
Use: componentWebhook,
Long: fmt.Sprintf("Webhook component providing API validation, mutation and conversion functionality for cert-manager (%s) (%s)", util.AppVersion, util.AppGitCommit),
// The webhook has special flag parsing requirements to handle precedence of providing
// configuration via versioned configuration files and flag values.
// Setting DisableFlagParsing=true prevents Cobra from interfering with flag parsing
// at all, and instead we handle it all in the RunE below.
DisableFlagParsing: true,
Run: func(cmd *cobra.Command, args []string) {
// initial flag parse, since we disable cobra's flag parsing
if err := cleanFlagSet.Parse(args); err != nil {
log.Error(err, "Failed to parse webhook flag")
cmd.Usage()
os.Exit(1)
}
return srv.Run(stopCh)
// check if there are non-flag arguments in the command line
cmds := cleanFlagSet.Args()
if len(cmds) > 0 {
log.Error(nil, "Unknown command", "command", cmds[0])
cmd.Usage()
os.Exit(1)
}
// short-circuit on help
help, err := cleanFlagSet.GetBool("help")
if err != nil {
log.Info(`"help" flag is non-bool, programmer error, please correct`)
os.Exit(1)
}
if help {
cmd.Help()
return
}
if err := options.ValidateWebhookFlags(webhookFlags); err != nil {
log.Error(err, "Failed to validate webhook flags")
os.Exit(1)
}
if configFile := webhookFlags.Config; len(configFile) > 0 {
webhookConfig, err = loadConfigFile(configFile)
if err != nil {
log.Error(err, "Failed to load webhook config file", "path", configFile)
os.Exit(1)
}
if err := webhookConfigFlagPrecedence(webhookConfig, args); err != nil {
log.Error(err, "Failed to merge flags with config file values")
os.Exit(1)
}
}
srv, err := NewServerWithOptions(log, *webhookFlags, *webhookConfig)
if err != nil {
log.Error(err, "Failed initialising server")
os.Exit(1)
}
if err := srv.Run(stopCh); err != nil {
log.Error(err, "Failed running server")
os.Exit(1)
}
},
}
opts.AddFlags(cmd.Flags())
webhookFlags.AddFlags(cleanFlagSet)
options.AddConfigFlags(cleanFlagSet, webhookConfig)
options.AddGlobalFlags(cleanFlagSet)
cleanFlagSet.BoolP("help", "h", false, fmt.Sprintf("help for %s", cmd.Name()))
// ugly, but necessary, because Cobra's default UsageFunc and HelpFunc pollute the flagset with global flags
const usageFmt = "Usage:\n %s\n\nFlags:\n%s"
cmd.SetUsageFunc(func(cmd *cobra.Command) error {
fmt.Fprintf(cmd.OutOrStderr(), usageFmt, cmd.UseLine(), cleanFlagSet.FlagUsagesWrapped(2))
return nil
})
cmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
fmt.Fprintf(cmd.OutOrStdout(), "%s\n\n"+usageFmt, cmd.Long, cmd.UseLine(), cleanFlagSet.FlagUsagesWrapped(2))
})
return cmd
}
// newFlagSetWithGlobals constructs a new pflag.FlagSet with global flags registered
// on it.
func newFlagSetWithGlobals() *pflag.FlagSet {
fs := pflag.NewFlagSet("", pflag.ExitOnError)
// set the normalize func, similar to k8s.io/component-base/cli//flags.go:InitFlags
fs.SetNormalizeFunc(cliflag.WordSepNormalizeFunc)
// explicitly add flags from libs that register global flags
options.AddGlobalFlags(fs)
return fs
}
// newFakeFlagSet constructs a pflag.FlagSet with the same flags as fs, but where
// all values have noop Set implementations
func newFakeFlagSet(fs *pflag.FlagSet) *pflag.FlagSet {
ret := pflag.NewFlagSet("", pflag.ExitOnError)
ret.SetNormalizeFunc(fs.GetNormalizeFunc())
fs.VisitAll(func(f *pflag.Flag) {
ret.VarP(cliflag.NoOp{}, f.Name, f.Shorthand, f.Usage)
})
return ret
}
// webhookConfigFlagPrecedence re-parses flags over the WebhookConfiguration object.
// We must enforce flag precedence by re-parsing the command line into the new object.
// This is necessary to preserve backwards-compatibility across binary upgrades.
// See issue #56171 for more details.
func webhookConfigFlagPrecedence(cfg *config.WebhookConfiguration, args []string) error {
// We use a throwaway webhookFlags and a fake global flagset to avoid double-parses,
// as some Set implementations accumulate values from multiple flag invocations.
fs := newFakeFlagSet(newFlagSetWithGlobals())
// register throwaway KubeletFlags
options.NewWebhookFlags().AddFlags(fs)
// register new WebhookConfiguration
options.AddConfigFlags(fs, cfg)
// re-parse flags
if err := fs.Parse(args); err != nil {
return err
}
return nil
}
func loadConfigFile(name string) (*config.WebhookConfiguration, error) {
const errFmt = "failed to load webhook config file %s, error %v"
// compute absolute path based on current working dir
webhookConfigFile, err := filepath.Abs(name)
if err != nil {
return nil, fmt.Errorf(errFmt, name, err)
}
loader, err := configfile.NewFSLoader(configfile.NewRealFS(), webhookConfigFile)
if err != nil {
return nil, fmt.Errorf(errFmt, name, err)
}
cfg, err := loader.Load()
if err != nil {
return nil, fmt.Errorf(errFmt, name, err)
}
return cfg, nil
}
+57
View File
@@ -0,0 +1,57 @@
/*
Copyright 2021 The cert-manager Authors.
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 app
import (
"testing"
"github.com/jetstack/cert-manager/cmd/webhook/app/options"
)
// Test to ensure flags take precedence over config options.
func TestWebhookConfigFlagPrecedence_FlagsTakePrecedence(t *testing.T) {
cfg, err := options.NewWebhookConfiguration()
if err != nil {
t.Fatal(err)
}
cfg.KubeConfig = "<invalid>"
if err := webhookConfigFlagPrecedence(cfg, []string{"--kubeconfig=valid"}); err != nil {
t.Fatal(err)
}
if cfg.KubeConfig != "valid" {
t.Errorf("unexpected field value %q, expected %q", cfg.KubeConfig, "valid")
}
}
// Test to ensure that when flags are not provided, config provided values are preserved.
func TestWebhookConfigFlagPrecedence_ConfigPersistsWithoutFlags(t *testing.T) {
cfg, err := options.NewWebhookConfiguration()
if err != nil {
t.Fatal(err)
}
cfg.KubeConfig = "valid"
if err := webhookConfigFlagPrecedence(cfg, []string{}); err != nil {
t.Fatal(err)
}
if cfg.KubeConfig != "valid" {
t.Errorf("unexpected field value %q, expected %q", cfg.KubeConfig, "valid")
}
}
@@ -137,6 +137,7 @@ The following table lists the configurable parameters of the cert-manager chart
| `webhook.mutatingWebhookConfigurationAnnotations` | Annotations to add to the mutating webhook configuration | `{}` |
| `webhook.validatingWebhookConfigurationAnnotations` | Annotations to add to the validating webhook configuration | `{}` |
| `webhook.serviceAnnotations` | Annotations to add to the webhook service | `{}` |
| `webhook.config` | WebhookConfiguration YAML used to configure flags for the webhook. Generates a ConfigMap containing contents of the field. See `values.yaml` for example. | `{}` |
| `webhook.extraArgs` | Optional flags for cert-manager webhook component | `[]` |
| `webhook.serviceAccount.create` | If `true`, create a new service account for the webhook component | `true` |
| `webhook.serviceAccount.name` | Service account for the webhook component to be used. If not set and `webhook.serviceAccount.create` is `true`, a name is generated using the fullname template | |
@@ -0,0 +1,24 @@
{{- if .Values.webhook.config -}}
{{- if not .Values.webhook.config.apiVersion -}}
{{- fail "webhook.config.apiVersion must be set" -}}
{{- end -}}
{{- if not .Values.webhook.config.kind -}}
{{- fail "webhook.config.kind must be set" -}}
{{- end -}}
{{- end -}}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "webhook.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
app: {{ include "webhook.name" . }}
app.kubernetes.io/name: {{ include "webhook.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/component: "webhook"
data:
{{- if .Values.webhook.config }}
config.yaml: |
{{ .Values.webhook.config | toYaml | nindent 4 }}
{{- end }}
@@ -61,21 +61,40 @@ spec:
{{- if .Values.global.logLevel }}
- --v={{ .Values.global.logLevel }}
{{- end }}
{{- if .Values.webhook.config }}
- --config=/var/cert-manager/config/config.yaml
{{- end }}
{{- $config := default .Values.webhook.config "" }}
{{ if not $config.securePort -}}
- --secure-port={{ .Values.webhook.securePort }}
{{- end }}
{{- $tlsConfig := default $config.tlsConfig "" }}
{{ if or (not $config.tlsConfig) (and (not $tlsConfig.dynamic) (not $tlsConfig.filesystem) ) -}}
- --dynamic-serving-ca-secret-namespace=$(POD_NAMESPACE)
- --dynamic-serving-ca-secret-name={{ template "webhook.fullname" . }}-ca
- --dynamic-serving-dns-names={{ template "webhook.fullname" . }},{{ template "webhook.fullname" . }}.{{ .Release.Namespace }},{{ template "webhook.fullname" . }}.{{ .Release.Namespace }}.svc{{ if .Values.webhook.url.host }},{{ .Values.webhook.url.host }}{{ end }}
{{- end }}
{{- with .Values.webhook.extraArgs }}
{{- toYaml . | nindent 10 }}
{{- end }}
ports:
- name: https
protocol: TCP
{{- if $config.securePort }}
containerPort: {{ $config.securePort }}
{{- else if .Values.webhook.securePort }}
containerPort: {{ .Values.webhook.securePort }}
{{- else }}
containerPort: 6443
{{- end }}
livenessProbe:
httpGet:
path: /livez
{{- if $config.healthzPort }}
port: {{ $config.healthzPort }}
{{- else }}
port: 6080
{{- end }}
scheme: HTTP
initialDelaySeconds: {{ .Values.webhook.livenessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.webhook.livenessProbe.periodSeconds }}
@@ -85,7 +104,11 @@ spec:
readinessProbe:
httpGet:
path: /healthz
{{- if $config.healthzPort }}
port: {{ $config.healthzPort }}
{{- else }}
port: 6080
{{- end }}
scheme: HTTP
initialDelaySeconds: {{ .Values.webhook.readinessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.webhook.readinessProbe.periodSeconds }}
@@ -105,6 +128,11 @@ spec:
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if .Values.webhook.config }}
volumeMounts:
- name: config
mountPath: /var/cert-manager/config
{{- end }}
{{- with .Values.webhook.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
@@ -117,4 +145,9 @@ spec:
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if .Values.webhook.config }}
volumes:
- name: config
configMap:
name: {{ include "webhook.fullname" . }}
{{- end }}
@@ -25,7 +25,7 @@ spec:
- name: https
port: 443
protocol: TCP
targetPort: {{ .Values.webhook.securePort }}
targetPort: "https"
selector:
app.kubernetes.io/name: {{ include "webhook.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
+17
View File
@@ -207,6 +207,23 @@ webhook:
replicaCount: 1
timeoutSeconds: 10
# Used to configure options for the webhook pod.
# This allows setting options that'd usually be provided via flags.
# An APIVersion and Kind must be specified in your values.yaml file.
# Flags will override options that are set here.
config:
# apiVersion: webhook.config.cert-manager.io/v1alpha1
# kind: WebhookConfiguration
# The port that the webhook should listen on for requests.
# In GKE private clusters, by default kubernetes apiservers are allowed to
# talk to the cluster nodes only on 443 and 10250. so configuring
# securePort: 10250, will work out of the box without needing to add firewall
# rules or requiring NET_BIND_SERVICE capabilities to bind port numbers <1000.
# This should be uncommented and set as a default by the chart once we graduate
# the apiVersion of WebhookConfiguration past v1alpha1.
# securePort: 10250
strategy: {}
# type: RollingUpdate
# rollingUpdate:
+4
View File
@@ -44,6 +44,8 @@ deepcopy_inputs=(
pkg/apis/acme/v1beta1 \
pkg/apis/acme/v1 \
internal/apis/acme \
pkg/apis/config/webhook/v1alpha1 \
internal/apis/config/webhook \
pkg/apis/meta/v1 \
internal/apis/meta \
pkg/webhook/handlers/testdata/apis/testgroup/v2 \
@@ -76,6 +78,7 @@ defaulter_inputs=(
internal/apis/acme/v1alpha3 \
internal/apis/acme/v1beta1 \
internal/apis/acme/v1 \
internal/apis/config/webhook/v1alpha1 \
internal/apis/meta/v1 \
pkg/webhook/handlers/testdata/apis/testgroup/v2 \
pkg/webhook/handlers/testdata/apis/testgroup/v1 \
@@ -91,6 +94,7 @@ conversion_inputs=(
internal/apis/acme/v1alpha3 \
internal/apis/acme/v1beta1 \
internal/apis/acme/v1 \
internal/apis/config/webhook/v1alpha1 \
internal/apis/meta/v1 \
pkg/webhook/handlers/testdata/apis/testgroup/v2 \
pkg/webhook/handlers/testdata/apis/testgroup/v1 \
+1
View File
@@ -13,6 +13,7 @@ filegroup(
"//internal/api/validation:all-srcs",
"//internal/apis/acme:all-srcs",
"//internal/apis/certmanager:all-srcs",
"//internal/apis/config/webhook:all-srcs",
"//internal/apis/meta:all-srcs",
"//internal/ingress:all-srcs",
"//internal/vault:all-srcs",
+40
View File
@@ -0,0 +1,40 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"register.go",
"types.go",
"zz_generated.deepcopy.go",
],
importpath = "github.com/jetstack/cert-manager/internal/apis/config/webhook",
visibility = ["//:__subpackages__"],
deps = [
"//pkg/apis/config/webhook: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",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//internal/apis/config/webhook/fuzzer:all-srcs",
"//internal/apis/config/webhook/install:all-srcs",
"//internal/apis/config/webhook/scheme:all-srcs",
"//internal/apis/config/webhook/v1alpha1:all-srcs",
"//internal/apis/config/webhook/validation:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
+21
View File
@@ -0,0 +1,21 @@
/*
Copyright 2021 The cert-manager Authors.
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.
*/
// +k8s:deepcopy-gen=package,register
// Package webhook is the internal version of the webhook config API.
// +groupName=webhook.config.cert-manager.io
package webhook
@@ -0,0 +1,28 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["fuzzer.go"],
importpath = "github.com/jetstack/cert-manager/internal/apis/config/webhook/fuzzer",
visibility = ["//:__subpackages__"],
deps = [
"//internal/apis/config/webhook:go_default_library",
"@com_github_google_gofuzz//:go_default_library",
"@io_k8s_apimachinery//pkg/runtime/serializer:go_default_library",
"@io_k8s_utils//pointer: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,41 @@
/*
Copyright 2021 The cert-manager Authors.
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 fuzzer
import (
fuzz "github.com/google/gofuzz"
runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/utils/pointer"
"github.com/jetstack/cert-manager/internal/apis/config/webhook"
)
// Funcs returns the fuzzer functions for the webhook config api group.
var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
return []interface{}{
func(s *webhook.WebhookConfiguration, c fuzz.Continue) {
c.FuzzNoCustom(s) // fuzz self without calling this function again
if s.HealthzPort == nil {
s.HealthzPort = pointer.Int(12)
}
if s.SecurePort == nil {
s.SecurePort = pointer.Int(123)
}
},
}
}
@@ -0,0 +1,41 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["install.go"],
importpath = "github.com/jetstack/cert-manager/internal/apis/config/webhook/install",
visibility = ["//:__subpackages__"],
deps = [
"//internal/apis/config/webhook:go_default_library",
"//internal/apis/config/webhook/v1alpha1:go_default_library",
"@io_k8s_apimachinery//pkg/runtime:go_default_library",
"@io_k8s_apimachinery//pkg/util/runtime: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"],
)
go_test(
name = "go_default_test",
srcs = ["roundtrip_test.go"],
data = [
"//deploy/crds:templated_files",
],
embed = [":go_default_library"],
deps = [
"//internal/apis/config/webhook/fuzzer:go_default_library",
"@io_k8s_apimachinery//pkg/api/apitesting/roundtrip:go_default_library",
],
)
@@ -0,0 +1,33 @@
/*
Copyright 2021 The cert-manager Authors.
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 install installs the API group, making it available as an option to
// all of the API encoding/decoding machinery.
package install
import (
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"github.com/jetstack/cert-manager/internal/apis/config/webhook"
"github.com/jetstack/cert-manager/internal/apis/config/webhook/v1alpha1"
)
// Install registers the API group and adds types to a scheme
func Install(scheme *runtime.Scheme) {
utilruntime.Must(webhook.AddToScheme(scheme))
utilruntime.Must(v1alpha1.AddToScheme(scheme))
}
@@ -0,0 +1,29 @@
/*
Copyright 2021 The cert-manager Authors.
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 install
import (
"testing"
"k8s.io/apimachinery/pkg/api/apitesting/roundtrip"
configfuzzer "github.com/jetstack/cert-manager/internal/apis/config/webhook/fuzzer"
)
func TestRoundTripTypes(t *testing.T) {
roundtrip.RoundTripTestForAPIGroup(t, Install, configfuzzer.Funcs)
}
+46
View File
@@ -0,0 +1,46 @@
/*
Copyright 2021 The cert-manager Authors.
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 webhook
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/jetstack/cert-manager/pkg/apis/config/webhook"
)
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: webhook.GroupName, Version: runtime.APIVersionInternal}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
// Adds the list of known types to api.Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&WebhookConfiguration{},
// Add new kinds to be registered here
)
return nil
}
@@ -0,0 +1,28 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["scheme.go"],
importpath = "github.com/jetstack/cert-manager/internal/apis/config/webhook/scheme",
visibility = ["//:__subpackages__"],
deps = [
"//internal/apis/config/webhook:go_default_library",
"//internal/apis/config/webhook/v1alpha1:go_default_library",
"@io_k8s_apimachinery//pkg/runtime:go_default_library",
"@io_k8s_apimachinery//pkg/runtime/serializer: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,40 @@
/*
Copyright 2021 The cert-manager Authors.
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 scheme
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
config "github.com/jetstack/cert-manager/internal/apis/config/webhook"
configv1alpha1 "github.com/jetstack/cert-manager/internal/apis/config/webhook/v1alpha1"
)
// NewSchemeAndCodecs is a utility function that returns a Scheme and CodecFactory
// that understand the types in the config.cert-manager.io API group. Passing mutators allows
// for adjusting the behavior of the CodecFactory, for example enable strict decoding.
func NewSchemeAndCodecs(mutators ...serializer.CodecFactoryOptionsMutator) (*runtime.Scheme, *serializer.CodecFactory, error) {
scheme := runtime.NewScheme()
if err := config.AddToScheme(scheme); err != nil {
return nil, nil, err
}
if err := configv1alpha1.AddToScheme(scheme); err != nil {
return nil, nil, err
}
codecs := serializer.NewCodecFactory(scheme, mutators...)
return scheme, &codecs, nil
}
+116
View File
@@ -0,0 +1,116 @@
/*
Copyright 2021 The cert-manager Authors.
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 webhook
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type WebhookConfiguration struct {
metav1.TypeMeta
// securePort is the port number to listen on for secure TLS connections from the kube-apiserver.
// Defaults to 6443.
SecurePort *int
// healthzPort is the port number to listen on (using plaintext HTTP) for healthz connections.
// Defaults to 6080.
HealthzPort *int
// tlsConfig is used to configure the secure listener's TLS settings.
TLSConfig TLSConfig
// kubeConfig is the kubeconfig file used to connect to the Kubernetes apiserver.
// If not specified, the webhook will attempt to load the in-cluster-config.
KubeConfig string
// apiServerHost is used to override the API server connection address.
// Deprecated: use `kubeConfig` instead.
APIServerHost string
// enablePprof configures whether pprof is enabled.
EnablePprof bool
// pprofAddress configures the address on which /debug/pprof endpoint will be served if enabled.
// Defaults to 'localhost:6060'.
PprofAddress string
}
// TLSConfig configures how TLS certificates are sourced for serving.
// Only one of 'filesystem' or 'dynamic' may be specified.
type TLSConfig struct {
// cipherSuites is the list of allowed cipher suites for the server.
// Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).
// If not specified, the default for the Go version will be used and may change over time.
CipherSuites []string
// minTLSVersion is the minimum TLS version supported.
// Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).
// If not specified, the default for the Go version will be used and may change over time.
MinTLSVersion string
// Filesystem enables using a certificate and private key found on the local filesystem.
// These files will be periodically polled in case they have changed, and dynamically reloaded.
Filesystem FilesystemServingConfig
// When Dynamic serving is enabled, the webhook will generate a CA used to sign webhook
// certificates and persist it into a Kubernetes Secret resource (for other replicas of the
// webhook to consume).
// It will then generate a certificate in-memory for itself using this CA to serve with.
// The CAs certificate can then be copied into the appropriate Validating, Mutating and Conversion
// webhook configuration objects (typically by cainjector).
Dynamic DynamicServingConfig
}
func (c *TLSConfig) FilesystemConfigProvided() bool {
if c.Filesystem.KeyFile != "" || c.Filesystem.CertFile != "" {
return true
}
return false
}
func (c *TLSConfig) DynamicConfigProvided() bool {
if c.Dynamic.SecretNamespace != "" || c.Dynamic.SecretName != "" || len(c.Dynamic.DNSNames) > 0 {
return true
}
return false
}
// DynamicServingConfig makes the webhook generate a CA and persist it into Secret resources.
// This CA will be used by all instances of the webhook for signing serving certificates.
type DynamicServingConfig struct {
// Namespace of the Kubernetes Secret resource containing the TLS certificate
// used as a CA to sign dynamic serving certificates.
SecretNamespace string
// Namespace of the Kubernetes Secret resource containing the TLS certificate
// used as a CA to sign dynamic serving certificates.
SecretName string
// DNSNames that must be present on serving certificates signed by the CA.
DNSNames []string
}
// FilesystemServingConfig enables using a certificate and private key found on the local filesystem.
// These files will be periodically polled in case they have changed, and dynamically reloaded.
type FilesystemServingConfig struct {
// Path to a file containing TLS certificate & chain to serve with
CertFile string
// Path to a file containing a TLS private key to server with
KeyFile string
}
@@ -0,0 +1,38 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"conversion.go",
"defaults.go",
"doc.go",
"register.go",
"zz_generated.conversion.go",
"zz_generated.defaults.go",
],
importpath = "github.com/jetstack/cert-manager/internal/apis/config/webhook/v1alpha1",
visibility = ["//:__subpackages__"],
deps = [
"//internal/apis/config/webhook:go_default_library",
"//pkg/apis/config/webhook:go_default_library",
"//pkg/apis/config/webhook/v1alpha1:go_default_library",
"@io_k8s_apimachinery//pkg/conversion:go_default_library",
"@io_k8s_apimachinery//pkg/runtime:go_default_library",
"@io_k8s_apimachinery//pkg/runtime/schema:go_default_library",
"@io_k8s_utils//pointer: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,17 @@
/*
Copyright 2021 The cert-manager Authors.
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 v1alpha1
@@ -0,0 +1,40 @@
/*
Copyright 2021 The cert-manager Authors.
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 v1alpha1
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/utils/pointer"
"github.com/jetstack/cert-manager/pkg/apis/config/webhook/v1alpha1"
)
func addDefaultingFuncs(scheme *runtime.Scheme) error {
return RegisterDefaults(scheme)
}
func SetDefaults_WebhookConfiguration(obj *v1alpha1.WebhookConfiguration) {
if obj.SecurePort == nil {
obj.SecurePort = pointer.Int(6443)
}
if obj.HealthzPort == nil {
obj.HealthzPort = pointer.Int(6080)
}
if obj.PprofAddress == "" {
obj.PprofAddress = "localhost:6060"
}
}
@@ -0,0 +1,23 @@
/*
Copyright 2021 The cert-manager Authors.
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.
*/
// +k8s:conversion-gen=github.com/jetstack/cert-manager/internal/apis/config/webhook
// +k8s:conversion-gen-external-types=github.com/jetstack/cert-manager/pkg/apis/config/webhook/v1alpha1
// +k8s:defaulter-gen=TypeMeta
// +k8s:defaulter-gen-input=../../../../../pkg/apis/config/webhook/v1alpha1
// +groupName=webhook.config.cert-manager.io
package v1alpha1
@@ -0,0 +1,44 @@
/*
Copyright 2021 The cert-manager Authors.
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 v1alpha1
import (
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/jetstack/cert-manager/pkg/apis/config/webhook"
"github.com/jetstack/cert-manager/pkg/apis/config/webhook/v1alpha1"
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: webhook.GroupName, Version: "v1alpha1"}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
localSchemeBuilder = &v1alpha1.SchemeBuilder
AddToScheme = localSchemeBuilder.AddToScheme
)
func init() {
// We only register manually written functions here. The registration of the
// generated functions takes place in the generated files. The separation
// makes the code compile even when the generated files are missing.
localSchemeBuilder.Register(addDefaultingFuncs)
}
@@ -0,0 +1,197 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
Copyright The cert-manager Authors.
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.
*/
// Code generated by conversion-gen. DO NOT EDIT.
package v1alpha1
import (
unsafe "unsafe"
webhook "github.com/jetstack/cert-manager/internal/apis/config/webhook"
v1alpha1 "github.com/jetstack/cert-manager/pkg/apis/config/webhook/v1alpha1"
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime"
)
func init() {
localSchemeBuilder.Register(RegisterConversions)
}
// RegisterConversions adds conversion functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterConversions(s *runtime.Scheme) error {
if err := s.AddGeneratedConversionFunc((*v1alpha1.DynamicServingConfig)(nil), (*webhook.DynamicServingConfig)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_DynamicServingConfig_To_webhook_DynamicServingConfig(a.(*v1alpha1.DynamicServingConfig), b.(*webhook.DynamicServingConfig), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*webhook.DynamicServingConfig)(nil), (*v1alpha1.DynamicServingConfig)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_webhook_DynamicServingConfig_To_v1alpha1_DynamicServingConfig(a.(*webhook.DynamicServingConfig), b.(*v1alpha1.DynamicServingConfig), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha1.FilesystemServingConfig)(nil), (*webhook.FilesystemServingConfig)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_FilesystemServingConfig_To_webhook_FilesystemServingConfig(a.(*v1alpha1.FilesystemServingConfig), b.(*webhook.FilesystemServingConfig), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*webhook.FilesystemServingConfig)(nil), (*v1alpha1.FilesystemServingConfig)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_webhook_FilesystemServingConfig_To_v1alpha1_FilesystemServingConfig(a.(*webhook.FilesystemServingConfig), b.(*v1alpha1.FilesystemServingConfig), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha1.TLSConfig)(nil), (*webhook.TLSConfig)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_TLSConfig_To_webhook_TLSConfig(a.(*v1alpha1.TLSConfig), b.(*webhook.TLSConfig), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*webhook.TLSConfig)(nil), (*v1alpha1.TLSConfig)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_webhook_TLSConfig_To_v1alpha1_TLSConfig(a.(*webhook.TLSConfig), b.(*v1alpha1.TLSConfig), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha1.WebhookConfiguration)(nil), (*webhook.WebhookConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_WebhookConfiguration_To_webhook_WebhookConfiguration(a.(*v1alpha1.WebhookConfiguration), b.(*webhook.WebhookConfiguration), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*webhook.WebhookConfiguration)(nil), (*v1alpha1.WebhookConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_webhook_WebhookConfiguration_To_v1alpha1_WebhookConfiguration(a.(*webhook.WebhookConfiguration), b.(*v1alpha1.WebhookConfiguration), scope)
}); err != nil {
return err
}
return nil
}
func autoConvert_v1alpha1_DynamicServingConfig_To_webhook_DynamicServingConfig(in *v1alpha1.DynamicServingConfig, out *webhook.DynamicServingConfig, s conversion.Scope) error {
out.SecretNamespace = in.SecretNamespace
out.SecretName = in.SecretName
out.DNSNames = *(*[]string)(unsafe.Pointer(&in.DNSNames))
return nil
}
// Convert_v1alpha1_DynamicServingConfig_To_webhook_DynamicServingConfig is an autogenerated conversion function.
func Convert_v1alpha1_DynamicServingConfig_To_webhook_DynamicServingConfig(in *v1alpha1.DynamicServingConfig, out *webhook.DynamicServingConfig, s conversion.Scope) error {
return autoConvert_v1alpha1_DynamicServingConfig_To_webhook_DynamicServingConfig(in, out, s)
}
func autoConvert_webhook_DynamicServingConfig_To_v1alpha1_DynamicServingConfig(in *webhook.DynamicServingConfig, out *v1alpha1.DynamicServingConfig, s conversion.Scope) error {
out.SecretNamespace = in.SecretNamespace
out.SecretName = in.SecretName
out.DNSNames = *(*[]string)(unsafe.Pointer(&in.DNSNames))
return nil
}
// Convert_webhook_DynamicServingConfig_To_v1alpha1_DynamicServingConfig is an autogenerated conversion function.
func Convert_webhook_DynamicServingConfig_To_v1alpha1_DynamicServingConfig(in *webhook.DynamicServingConfig, out *v1alpha1.DynamicServingConfig, s conversion.Scope) error {
return autoConvert_webhook_DynamicServingConfig_To_v1alpha1_DynamicServingConfig(in, out, s)
}
func autoConvert_v1alpha1_FilesystemServingConfig_To_webhook_FilesystemServingConfig(in *v1alpha1.FilesystemServingConfig, out *webhook.FilesystemServingConfig, s conversion.Scope) error {
out.CertFile = in.CertFile
out.KeyFile = in.KeyFile
return nil
}
// Convert_v1alpha1_FilesystemServingConfig_To_webhook_FilesystemServingConfig is an autogenerated conversion function.
func Convert_v1alpha1_FilesystemServingConfig_To_webhook_FilesystemServingConfig(in *v1alpha1.FilesystemServingConfig, out *webhook.FilesystemServingConfig, s conversion.Scope) error {
return autoConvert_v1alpha1_FilesystemServingConfig_To_webhook_FilesystemServingConfig(in, out, s)
}
func autoConvert_webhook_FilesystemServingConfig_To_v1alpha1_FilesystemServingConfig(in *webhook.FilesystemServingConfig, out *v1alpha1.FilesystemServingConfig, s conversion.Scope) error {
out.CertFile = in.CertFile
out.KeyFile = in.KeyFile
return nil
}
// Convert_webhook_FilesystemServingConfig_To_v1alpha1_FilesystemServingConfig is an autogenerated conversion function.
func Convert_webhook_FilesystemServingConfig_To_v1alpha1_FilesystemServingConfig(in *webhook.FilesystemServingConfig, out *v1alpha1.FilesystemServingConfig, s conversion.Scope) error {
return autoConvert_webhook_FilesystemServingConfig_To_v1alpha1_FilesystemServingConfig(in, out, s)
}
func autoConvert_v1alpha1_TLSConfig_To_webhook_TLSConfig(in *v1alpha1.TLSConfig, out *webhook.TLSConfig, s conversion.Scope) error {
out.CipherSuites = *(*[]string)(unsafe.Pointer(&in.CipherSuites))
out.MinTLSVersion = in.MinTLSVersion
if err := Convert_v1alpha1_FilesystemServingConfig_To_webhook_FilesystemServingConfig(&in.Filesystem, &out.Filesystem, s); err != nil {
return err
}
if err := Convert_v1alpha1_DynamicServingConfig_To_webhook_DynamicServingConfig(&in.Dynamic, &out.Dynamic, s); err != nil {
return err
}
return nil
}
// Convert_v1alpha1_TLSConfig_To_webhook_TLSConfig is an autogenerated conversion function.
func Convert_v1alpha1_TLSConfig_To_webhook_TLSConfig(in *v1alpha1.TLSConfig, out *webhook.TLSConfig, s conversion.Scope) error {
return autoConvert_v1alpha1_TLSConfig_To_webhook_TLSConfig(in, out, s)
}
func autoConvert_webhook_TLSConfig_To_v1alpha1_TLSConfig(in *webhook.TLSConfig, out *v1alpha1.TLSConfig, s conversion.Scope) error {
out.CipherSuites = *(*[]string)(unsafe.Pointer(&in.CipherSuites))
out.MinTLSVersion = in.MinTLSVersion
if err := Convert_webhook_FilesystemServingConfig_To_v1alpha1_FilesystemServingConfig(&in.Filesystem, &out.Filesystem, s); err != nil {
return err
}
if err := Convert_webhook_DynamicServingConfig_To_v1alpha1_DynamicServingConfig(&in.Dynamic, &out.Dynamic, s); err != nil {
return err
}
return nil
}
// Convert_webhook_TLSConfig_To_v1alpha1_TLSConfig is an autogenerated conversion function.
func Convert_webhook_TLSConfig_To_v1alpha1_TLSConfig(in *webhook.TLSConfig, out *v1alpha1.TLSConfig, s conversion.Scope) error {
return autoConvert_webhook_TLSConfig_To_v1alpha1_TLSConfig(in, out, s)
}
func autoConvert_v1alpha1_WebhookConfiguration_To_webhook_WebhookConfiguration(in *v1alpha1.WebhookConfiguration, out *webhook.WebhookConfiguration, s conversion.Scope) error {
out.SecurePort = (*int)(unsafe.Pointer(in.SecurePort))
out.HealthzPort = (*int)(unsafe.Pointer(in.HealthzPort))
if err := Convert_v1alpha1_TLSConfig_To_webhook_TLSConfig(&in.TLSConfig, &out.TLSConfig, s); err != nil {
return err
}
out.KubeConfig = in.KubeConfig
out.APIServerHost = in.APIServerHost
out.EnablePprof = in.EnablePprof
out.PprofAddress = in.PprofAddress
return nil
}
// Convert_v1alpha1_WebhookConfiguration_To_webhook_WebhookConfiguration is an autogenerated conversion function.
func Convert_v1alpha1_WebhookConfiguration_To_webhook_WebhookConfiguration(in *v1alpha1.WebhookConfiguration, out *webhook.WebhookConfiguration, s conversion.Scope) error {
return autoConvert_v1alpha1_WebhookConfiguration_To_webhook_WebhookConfiguration(in, out, s)
}
func autoConvert_webhook_WebhookConfiguration_To_v1alpha1_WebhookConfiguration(in *webhook.WebhookConfiguration, out *v1alpha1.WebhookConfiguration, s conversion.Scope) error {
out.SecurePort = (*int)(unsafe.Pointer(in.SecurePort))
out.HealthzPort = (*int)(unsafe.Pointer(in.HealthzPort))
if err := Convert_webhook_TLSConfig_To_v1alpha1_TLSConfig(&in.TLSConfig, &out.TLSConfig, s); err != nil {
return err
}
out.KubeConfig = in.KubeConfig
out.APIServerHost = in.APIServerHost
out.EnablePprof = in.EnablePprof
out.PprofAddress = in.PprofAddress
return nil
}
// Convert_webhook_WebhookConfiguration_To_v1alpha1_WebhookConfiguration is an autogenerated conversion function.
func Convert_webhook_WebhookConfiguration_To_v1alpha1_WebhookConfiguration(in *webhook.WebhookConfiguration, out *v1alpha1.WebhookConfiguration, s conversion.Scope) error {
return autoConvert_webhook_WebhookConfiguration_To_v1alpha1_WebhookConfiguration(in, out, s)
}
@@ -0,0 +1,39 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
Copyright The cert-manager Authors.
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.
*/
// Code generated by defaulter-gen. DO NOT EDIT.
package v1alpha1
import (
v1alpha1 "github.com/jetstack/cert-manager/pkg/apis/config/webhook/v1alpha1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
// RegisterDefaults adds defaulters functions to the given scheme.
// Public to allow building arbitrary schemes.
// All generated defaulters are covering - they call all nested defaulters.
func RegisterDefaults(scheme *runtime.Scheme) error {
scheme.AddTypeDefaultingFunc(&v1alpha1.WebhookConfiguration{}, func(obj interface{}) { SetObjectDefaults_WebhookConfiguration(obj.(*v1alpha1.WebhookConfiguration)) })
return nil
}
func SetObjectDefaults_WebhookConfiguration(in *v1alpha1.WebhookConfiguration) {
SetDefaults_WebhookConfiguration(in)
}
@@ -0,0 +1,26 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["validation.go"],
importpath = "github.com/jetstack/cert-manager/internal/apis/config/webhook/validation",
visibility = ["//:__subpackages__"],
deps = [
"//internal/apis/config/webhook:go_default_library",
"@io_k8s_apimachinery//pkg/util/errors: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,58 @@
/*
Copyright 2021 The cert-manager Authors.
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 validation
import (
"fmt"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
config "github.com/jetstack/cert-manager/internal/apis/config/webhook"
)
func ValidateWebhookConfiguration(cfg *config.WebhookConfiguration) error {
var allErrors []error
if cfg.TLSConfig.FilesystemConfigProvided() && cfg.TLSConfig.DynamicConfigProvided() {
allErrors = append(allErrors, fmt.Errorf("invalid configuration: cannot specify both filesystem based and dynamic TLS configuration"))
} else {
if cfg.TLSConfig.FilesystemConfigProvided() {
if cfg.TLSConfig.Filesystem.KeyFile == "" {
allErrors = append(allErrors, fmt.Errorf("invalid configuration: tlsConfig.filesystem.keyFile (--tls-private-key-file) must be specified when using filesystem based TLS config"))
}
if cfg.TLSConfig.Filesystem.CertFile == "" {
allErrors = append(allErrors, fmt.Errorf("invalid configuration: tlsConfig.filesystem.certFile (--tls-cert-file) must be specified when using filesystem based TLS config"))
}
} else if cfg.TLSConfig.DynamicConfigProvided() {
if cfg.TLSConfig.Dynamic.SecretNamespace == "" {
allErrors = append(allErrors, fmt.Errorf("invalid configuration: tlsConfig.dynamic.secretNamespace (--dynamic-serving-ca-secret-namespace) must be specified when using dynamic TLS config"))
}
if cfg.TLSConfig.Dynamic.SecretName == "" {
allErrors = append(allErrors, fmt.Errorf("invalid configuration: tlsConfig.dynamic.secretName (--dynamic-serving-ca-secret-name) must be specified when using dynamic TLS config"))
}
if len(cfg.TLSConfig.Dynamic.DNSNames) == 0 {
allErrors = append(allErrors, fmt.Errorf("invalid configuration: tlsConfig.dynamic.dnsNames (--dynamic-serving-dns-names) must be specified when using dynamic TLS config"))
}
}
}
if cfg.HealthzPort == nil {
allErrors = append(allErrors, fmt.Errorf("invalid configuration: healthzPort must be specified"))
}
if cfg.SecurePort == nil {
allErrors = append(allErrors, fmt.Errorf("invalid configuration: securePort must be specified"))
}
return utilerrors.NewAggregate(allErrors)
}
@@ -0,0 +1,122 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
Copyright The cert-manager Authors.
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.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
package webhook
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DynamicServingConfig) DeepCopyInto(out *DynamicServingConfig) {
*out = *in
if in.DNSNames != nil {
in, out := &in.DNSNames, &out.DNSNames
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DynamicServingConfig.
func (in *DynamicServingConfig) DeepCopy() *DynamicServingConfig {
if in == nil {
return nil
}
out := new(DynamicServingConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FilesystemServingConfig) DeepCopyInto(out *FilesystemServingConfig) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FilesystemServingConfig.
func (in *FilesystemServingConfig) DeepCopy() *FilesystemServingConfig {
if in == nil {
return nil
}
out := new(FilesystemServingConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TLSConfig) DeepCopyInto(out *TLSConfig) {
*out = *in
if in.CipherSuites != nil {
in, out := &in.CipherSuites, &out.CipherSuites
*out = make([]string, len(*in))
copy(*out, *in)
}
out.Filesystem = in.Filesystem
in.Dynamic.DeepCopyInto(&out.Dynamic)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSConfig.
func (in *TLSConfig) DeepCopy() *TLSConfig {
if in == nil {
return nil
}
out := new(TLSConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WebhookConfiguration) DeepCopyInto(out *WebhookConfiguration) {
*out = *in
out.TypeMeta = in.TypeMeta
if in.SecurePort != nil {
in, out := &in.SecurePort, &out.SecurePort
*out = new(int)
**out = **in
}
if in.HealthzPort != nil {
in, out := &in.HealthzPort, &out.HealthzPort
*out = new(int)
**out = **in
}
in.TLSConfig.DeepCopyInto(&out.TLSConfig)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookConfiguration.
func (in *WebhookConfiguration) DeepCopy() *WebhookConfiguration {
if in == nil {
return nil
}
out := new(WebhookConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *WebhookConfiguration) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
+1
View File
@@ -20,6 +20,7 @@ filegroup(
":package-srcs",
"//pkg/apis/acme:all-srcs",
"//pkg/apis/certmanager:all-srcs",
"//pkg/apis/config/webhook:all-srcs",
"//pkg/apis/experimental:all-srcs",
"//pkg/apis/meta:all-srcs",
],
+25
View File
@@ -0,0 +1,25 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["doc.go"],
importpath = "github.com/jetstack/cert-manager/pkg/apis/config/webhook",
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/apis/config/webhook/v1alpha1:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
+22
View File
@@ -0,0 +1,22 @@
/*
Copyright 2021 The cert-manager Authors.
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.
*/
// +groupName=webhook.config.cert-manager.io
// Package webhook contains types used to configure the webhook
package webhook
const GroupName = "webhook.config.cert-manager.io"
@@ -0,0 +1,33 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"register.go",
"types.go",
"zz_generated.deepcopy.go",
],
importpath = "github.com/jetstack/cert-manager/pkg/apis/config/webhook/v1alpha1",
visibility = ["//visibility:public"],
deps = [
"//pkg/apis/config/webhook: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",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
+20
View File
@@ -0,0 +1,20 @@
/*
Copyright 2021 The cert-manager Authors.
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 v1alpha1 is the v1alpha1 version of the webhook config API.
// +k8s:deepcopy-gen=package,register
// +groupName=webhook.config.cert-manager.io
package v1alpha1
@@ -0,0 +1,56 @@
/*
Copyright 2021 The cert-manager Authors.
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 v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/jetstack/cert-manager/pkg/apis/config/webhook"
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: webhook.GroupName, Version: "v1alpha1"}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
SchemeBuilder runtime.SchemeBuilder
localSchemeBuilder = &SchemeBuilder
AddToScheme = localSchemeBuilder.AddToScheme
)
func init() {
// We only register manually written functions here. The registration of the
// generated functions takes place in the generated files. The separation
// makes the code compile even when the generated files are missing.
localSchemeBuilder.Register(addKnownTypes)
}
// Adds the list of known types to api.Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&WebhookConfiguration{},
// Add new kinds to be registered here
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}
+102
View File
@@ -0,0 +1,102 @@
/*
Copyright 2021 The cert-manager Authors.
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 v1alpha1
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type WebhookConfiguration struct {
metav1.TypeMeta `json:",inline"`
// securePort is the port number to listen on for secure TLS connections from the kube-apiserver.
// Defaults to 6443.
SecurePort *int `json:"securePort,omitempty"`
// healthzPort is the port number to listen on (using plaintext HTTP) for healthz connections.
// Defaults to 6080.
HealthzPort *int `json:"healthzPort,omitempty"`
// tlsConfig is used to configure the secure listener's TLS settings.
TLSConfig TLSConfig `json:"tlsConfig"`
// kubeConfig is the kubeconfig file used to connect to the Kubernetes apiserver.
// If not specified, the webhook will attempt to load the in-cluster-config.
KubeConfig string `json:"kubeConfig,omitempty"`
// apiServerHost is used to override the API server connection address.
// Deprecated: use `kubeConfig` instead.
APIServerHost string `json:"apiServerHost,omitempty"`
// enablePprof configures whether pprof is enabled.
EnablePprof bool `json:"enablePprof"`
// pprofAddress configures the address on which /debug/pprof endpoint will be served if enabled.
// Defaults to 'localhost:6060'.
PprofAddress string `json:"pprofAddress,omitempty"`
}
// TLSConfig configures how TLS certificates are sourced for serving.
// Only one of 'filesystem' or 'dynamic' may be specified.
type TLSConfig struct {
// cipherSuites is the list of allowed cipher suites for the server.
// Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).
// If not specified, the default for the Go version will be used and may change over time.
CipherSuites []string `json:"cipherSuites,omitempty"`
// minTLSVersion is the minimum TLS version supported.
// Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).
// If not specified, the default for the Go version will be used and may change over time.
MinTLSVersion string `json:"minTLSVersion,omitempty"`
// Filesystem enables using a certificate and private key found on the local filesystem.
// These files will be periodically polled in case they have changed, and dynamically reloaded.
Filesystem FilesystemServingConfig `json:"filesystem"`
// When Dynamic serving is enabled, the webhook will generate a CA used to sign webhook
// certificates and persist it into a Kubernetes Secret resource (for other replicas of the
// webhook to consume).
// It will then generate a certificate in-memory for itself using this CA to serve with.
// The CAs certificate can then be copied into the appropriate Validating, Mutating and Conversion
// webhook configuration objects (typically by cainjector).
Dynamic DynamicServingConfig `json:"dynamic"`
}
// DynamicServingConfig makes the webhook generate a CA and persist it into Secret resources.
// This CA will be used by all instances of the webhook for signing serving certificates.
type DynamicServingConfig struct {
// Namespace of the Kubernetes Secret resource containing the TLS certificate
// used as a CA to sign dynamic serving certificates.
SecretNamespace string `json:"secretNamespace,omitempty"`
// Namespace of the Kubernetes Secret resource containing the TLS certificate
// used as a CA to sign dynamic serving certificates.
SecretName string `json:"secretName,omitempty"`
// DNSNames that must be present on serving certificates signed by the CA.
DNSNames []string `json:"dnsNames,omitempty"`
}
// FilesystemServingConfig enables using a certificate and private key found on the local filesystem.
// These files will be periodically polled in case they have changed, and dynamically reloaded.
type FilesystemServingConfig struct {
// Path to a file containing TLS certificate & chain to serve with
CertFile string `json:"certFile,omitempty"`
// Path to a file containing a TLS private key to server with
KeyFile string `json:"keyFile,omitempty"`
}
@@ -0,0 +1,122 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
Copyright The cert-manager Authors.
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.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
package v1alpha1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DynamicServingConfig) DeepCopyInto(out *DynamicServingConfig) {
*out = *in
if in.DNSNames != nil {
in, out := &in.DNSNames, &out.DNSNames
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DynamicServingConfig.
func (in *DynamicServingConfig) DeepCopy() *DynamicServingConfig {
if in == nil {
return nil
}
out := new(DynamicServingConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FilesystemServingConfig) DeepCopyInto(out *FilesystemServingConfig) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FilesystemServingConfig.
func (in *FilesystemServingConfig) DeepCopy() *FilesystemServingConfig {
if in == nil {
return nil
}
out := new(FilesystemServingConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TLSConfig) DeepCopyInto(out *TLSConfig) {
*out = *in
if in.CipherSuites != nil {
in, out := &in.CipherSuites, &out.CipherSuites
*out = make([]string, len(*in))
copy(*out, *in)
}
out.Filesystem = in.Filesystem
in.Dynamic.DeepCopyInto(&out.Dynamic)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSConfig.
func (in *TLSConfig) DeepCopy() *TLSConfig {
if in == nil {
return nil
}
out := new(TLSConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WebhookConfiguration) DeepCopyInto(out *WebhookConfiguration) {
*out = *in
out.TypeMeta = in.TypeMeta
if in.SecurePort != nil {
in, out := &in.SecurePort, &out.SecurePort
*out = new(int)
**out = **in
}
if in.HealthzPort != nil {
in, out := &in.HealthzPort, &out.HealthzPort
*out = new(int)
**out = **in
}
in.TLSConfig.DeepCopyInto(&out.TLSConfig)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookConfiguration.
func (in *WebhookConfiguration) DeepCopy() *WebhookConfiguration {
if in == nil {
return nil
}
out := new(WebhookConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *WebhookConfiguration) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
+1
View File
@@ -27,6 +27,7 @@ filegroup(
srcs = [
":package-srcs",
"//pkg/webhook/authority:all-srcs",
"//pkg/webhook/configfile:all-srcs",
"//pkg/webhook/handlers:all-srcs",
"//pkg/webhook/server:all-srcs",
],
+33
View File
@@ -0,0 +1,33 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["configfile.go"],
importpath = "github.com/jetstack/cert-manager/pkg/webhook/configfile",
visibility = ["//visibility:public"],
deps = [
"//internal/apis/config/webhook:go_default_library",
"//internal/apis/config/webhook/scheme:go_default_library",
"@io_k8s_apimachinery//pkg/runtime/serializer: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"],
)
go_test(
name = "go_default_test",
srcs = ["configfile_test.go"],
embed = [":go_default_library"],
)
+124
View File
@@ -0,0 +1,124 @@
/*
Copyright 2021 The cert-manager Authors.
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 configfile
import (
"fmt"
"io/ioutil"
"path/filepath"
"k8s.io/apimachinery/pkg/runtime/serializer"
config "github.com/jetstack/cert-manager/internal/apis/config/webhook"
"github.com/jetstack/cert-manager/internal/apis/config/webhook/scheme"
)
// Filesystem is an interface used to mock out calls to ReadFile
type Filesystem interface {
ReadFile(filename string) ([]byte, error)
}
type realFS struct{}
func (fs realFS) ReadFile(filename string) ([]byte, error) {
return ioutil.ReadFile(filename)
}
// NewRealFS builds a Filesystem that wraps around `ioutil.ReadFile`.
func NewRealFS() Filesystem {
return realFS{}
}
type Loader interface {
Load() (*config.WebhookConfiguration, error)
}
type fsLoader struct {
fs Filesystem
filename string
codec *serializer.CodecFactory
}
var _ Loader = &fsLoader{}
func (f *fsLoader) Load() (*config.WebhookConfiguration, error) {
data, err := f.fs.ReadFile(f.filename)
if err != nil {
return nil, fmt.Errorf("failed to read webhook config file %q, error: %v", f.filename, err)
}
if len(data) == 0 {
return nil, fmt.Errorf("webhook config file %q was empty", f.filename)
}
cfg, err := decodeWebhookConfiguration(f.codec, data)
if err != nil {
return nil, err
}
// make all paths absolute
resolveRelativePaths(webhookConfigurationPathRefs(cfg), filepath.Dir(f.filename))
return cfg, nil
}
func NewFSLoader(fs Filesystem, name string) (Loader, error) {
_, webhookCodec, err := scheme.NewSchemeAndCodecs(serializer.EnableStrict)
if err != nil {
return nil, err
}
return &fsLoader{
fs: fs,
filename: name,
codec: webhookCodec,
}, nil
}
func resolveRelativePaths(paths []*string, root string) {
for _, path := range paths {
// leave empty paths alone, "no path" is a valid input
// do not attempt to resolve paths that are already absolute
if len(*path) > 0 && !filepath.IsAbs(*path) {
*path = filepath.Join(root, *path)
}
}
}
func decodeWebhookConfiguration(codec *serializer.CodecFactory, data []byte) (*config.WebhookConfiguration, error) {
obj, gvk, err := codec.UniversalDecoder().Decode(data, nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to decode: %w", err)
}
internalObj, ok := obj.(*config.WebhookConfiguration)
if !ok {
return nil, fmt.Errorf("failed to cast object to WebhookConfiguration, unexpected type: %v", gvk)
}
return internalObj, nil
}
// webhookConfigurationPathRefs returns pointers to all the WebhookConfiguration fields that contain filepaths.
// You might use this, for example, to resolve all relative paths against some common root before
// passing the configuration to the application. This method must be kept up to date as new fields are added.
func webhookConfigurationPathRefs(cfg *config.WebhookConfiguration) []*string {
return []*string{
&cfg.TLSConfig.Filesystem.KeyFile,
&cfg.TLSConfig.Filesystem.CertFile,
&cfg.KubeConfig,
}
}
+63
View File
@@ -0,0 +1,63 @@
/*
Copyright 2021 The cert-manager Authors.
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 configfile
import (
"fmt"
"testing"
)
func TestFSLoader_Load(t *testing.T) {
const expectedFilename = "/path/to/config/file"
const kubeConfigPath = "path/to/kubeconfig/file"
loader, err := NewFSLoader(newFakeFS(func(filename string) ([]byte, error) {
if filename != expectedFilename {
t.Fatalf("unexpected filename %q passed to ReadFile", filename)
return nil, fmt.Errorf("unexpected filename %q", filename)
}
return []byte(fmt.Sprintf(`apiVersion: webhook.config.cert-manager.io/v1alpha1
kind: WebhookConfiguration
kubeConfig: %s`, kubeConfigPath)), nil
}), expectedFilename)
if err != nil {
t.Fatal(err)
}
cfg, err := loader.Load()
if err != nil {
t.Fatal(err)
}
// the config loader will force paths to be 'absolute' if they are provided as relative.
absKubeConfigPath := "/path/to/config/path/to/kubeconfig/file"
if cfg.KubeConfig != absKubeConfigPath {
t.Errorf("expected kubeConfig to be set to %q but got %q", absKubeConfigPath, cfg.KubeConfig)
}
}
func newFakeFS(readFileFunc func(string) ([]byte, error)) Filesystem {
return fakeFS{readFileFunc: readFileFunc}
}
type fakeFS struct {
readFileFunc func(string) ([]byte, error)
}
func (f fakeFS) ReadFile(filename string) ([]byte, error) {
return f.readFileFunc(filename)
}