14
vendor/k8s.io/apiserver/pkg/server/options/OWNERS
generated
vendored
Normal file
14
vendor/k8s.io/apiserver/pkg/server/options/OWNERS
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
reviewers:
|
||||
- smarterclayton
|
||||
- wojtek-t
|
||||
- deads2k
|
||||
- liggitt
|
||||
- nikhiljindal
|
||||
- sttts
|
||||
- jlowdermilk
|
||||
- soltysh
|
||||
- dims
|
||||
- cjcullen
|
||||
- ping035627
|
||||
- xiangpengzhao
|
||||
- enj
|
||||
234
vendor/k8s.io/apiserver/pkg/server/options/admission.go
generated
vendored
Normal file
234
vendor/k8s.io/apiserver/pkg/server/options/admission.go
generated
vendored
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes 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 (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/admission/initializer"
|
||||
admissionmetrics "k8s.io/apiserver/pkg/admission/metrics"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/initialization"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle"
|
||||
mutatingwebhook "k8s.io/apiserver/pkg/admission/plugin/webhook/mutating"
|
||||
validatingwebhook "k8s.io/apiserver/pkg/admission/plugin/webhook/validating"
|
||||
apiserverapi "k8s.io/apiserver/pkg/apis/apiserver"
|
||||
apiserverapiv1alpha1 "k8s.io/apiserver/pkg/apis/apiserver/v1alpha1"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
var configScheme = runtime.NewScheme()
|
||||
|
||||
func init() {
|
||||
utilruntime.Must(apiserverapi.AddToScheme(configScheme))
|
||||
utilruntime.Must(apiserverapiv1alpha1.AddToScheme(configScheme))
|
||||
}
|
||||
|
||||
// AdmissionOptions holds the admission options
|
||||
type AdmissionOptions struct {
|
||||
// RecommendedPluginOrder holds an ordered list of plugin names we recommend to use by default
|
||||
RecommendedPluginOrder []string
|
||||
// DefaultOffPlugins is a set of plugin names that is disabled by default
|
||||
DefaultOffPlugins sets.String
|
||||
|
||||
// EnablePlugins indicates plugins to be enabled passed through `--enable-admission-plugins`.
|
||||
EnablePlugins []string
|
||||
// DisablePlugins indicates plugins to be disabled passed through `--disable-admission-plugins`.
|
||||
DisablePlugins []string
|
||||
// ConfigFile is the file path with admission control configuration.
|
||||
ConfigFile string
|
||||
// Plugins contains all registered plugins.
|
||||
Plugins *admission.Plugins
|
||||
}
|
||||
|
||||
// NewAdmissionOptions creates a new instance of AdmissionOptions
|
||||
// Note:
|
||||
// In addition it calls RegisterAllAdmissionPlugins to register
|
||||
// all generic admission plugins.
|
||||
//
|
||||
// Provides the list of RecommendedPluginOrder that holds sane values
|
||||
// that can be used by servers that don't care about admission chain.
|
||||
// Servers that do care can overwrite/append that field after creation.
|
||||
func NewAdmissionOptions() *AdmissionOptions {
|
||||
options := &AdmissionOptions{
|
||||
Plugins: admission.NewPlugins(),
|
||||
// This list is mix of mutating admission plugins and validating
|
||||
// admission plugins. The apiserver always runs the validating ones
|
||||
// after all the mutating ones, so their relative order in this list
|
||||
// doesn't matter.
|
||||
RecommendedPluginOrder: []string{lifecycle.PluginName, initialization.PluginName, mutatingwebhook.PluginName, validatingwebhook.PluginName},
|
||||
DefaultOffPlugins: sets.NewString(initialization.PluginName),
|
||||
}
|
||||
server.RegisterAllAdmissionPlugins(options.Plugins)
|
||||
return options
|
||||
}
|
||||
|
||||
// AddFlags adds flags related to admission for a specific APIServer to the specified FlagSet
|
||||
func (a *AdmissionOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
if a == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fs.StringSliceVar(&a.EnablePlugins, "enable-admission-plugins", a.EnablePlugins, ""+
|
||||
"admission plugins that should be enabled in addition to default enabled ones ("+
|
||||
strings.Join(a.defaultEnabledPluginNames(), ", ")+"). "+
|
||||
"Comma-delimited list of admission plugins: "+strings.Join(a.Plugins.Registered(), ", ")+". "+
|
||||
"The order of plugins in this flag does not matter.")
|
||||
fs.StringSliceVar(&a.DisablePlugins, "disable-admission-plugins", a.DisablePlugins, ""+
|
||||
"admission plugins that should be disabled although they are in the default enabled plugins list ("+
|
||||
strings.Join(a.defaultEnabledPluginNames(), ", ")+"). "+
|
||||
"Comma-delimited list of admission plugins: "+strings.Join(a.Plugins.Registered(), ", ")+". "+
|
||||
"The order of plugins in this flag does not matter.")
|
||||
fs.StringVar(&a.ConfigFile, "admission-control-config-file", a.ConfigFile,
|
||||
"File with admission control configuration.")
|
||||
}
|
||||
|
||||
// ApplyTo adds the admission chain to the server configuration.
|
||||
// In case admission plugin names were not provided by a custer-admin they will be prepared from the recommended/default values.
|
||||
// In addition the method lazily initializes a generic plugin that is appended to the list of pluginInitializers
|
||||
// note this method uses:
|
||||
// genericconfig.Authorizer
|
||||
func (a *AdmissionOptions) ApplyTo(
|
||||
c *server.Config,
|
||||
informers informers.SharedInformerFactory,
|
||||
kubeAPIServerClientConfig *rest.Config,
|
||||
scheme *runtime.Scheme,
|
||||
pluginInitializers ...admission.PluginInitializer,
|
||||
) error {
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Admission need scheme to construct admission initializer.
|
||||
if scheme == nil {
|
||||
return fmt.Errorf("admission depends on a scheme, it cannot be nil")
|
||||
}
|
||||
|
||||
// Admission depends on CoreAPI to set SharedInformerFactory and ClientConfig.
|
||||
if informers == nil {
|
||||
return fmt.Errorf("admission depends on a Kubernetes core API shared informer, it cannot be nil")
|
||||
}
|
||||
|
||||
pluginNames := a.enabledPluginNames()
|
||||
|
||||
pluginsConfigProvider, err := admission.ReadAdmissionConfiguration(pluginNames, a.ConfigFile, configScheme)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read plugin config: %v", err)
|
||||
}
|
||||
|
||||
clientset, err := kubernetes.NewForConfig(kubeAPIServerClientConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
genericInitializer := initializer.New(clientset, informers, c.Authorization.Authorizer, scheme)
|
||||
initializersChain := admission.PluginInitializers{}
|
||||
pluginInitializers = append(pluginInitializers, genericInitializer)
|
||||
initializersChain = append(initializersChain, pluginInitializers...)
|
||||
|
||||
admissionChain, err := a.Plugins.NewFromPlugins(pluginNames, pluginsConfigProvider, initializersChain, admission.DecoratorFunc(admissionmetrics.WithControllerMetrics))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.AdmissionControl = admissionmetrics.WithStepMetrics(admissionChain)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate verifies flags passed to AdmissionOptions.
|
||||
func (a *AdmissionOptions) Validate() []error {
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errs := []error{}
|
||||
|
||||
registeredPlugins := sets.NewString(a.Plugins.Registered()...)
|
||||
for _, name := range a.EnablePlugins {
|
||||
if !registeredPlugins.Has(name) {
|
||||
errs = append(errs, fmt.Errorf("enable-admission-plugins plugin %q is unknown", name))
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range a.DisablePlugins {
|
||||
if !registeredPlugins.Has(name) {
|
||||
errs = append(errs, fmt.Errorf("disable-admission-plugins plugin %q is unknown", name))
|
||||
}
|
||||
}
|
||||
|
||||
enablePlugins := sets.NewString(a.EnablePlugins...)
|
||||
disablePlugins := sets.NewString(a.DisablePlugins...)
|
||||
if len(enablePlugins.Intersection(disablePlugins).List()) > 0 {
|
||||
errs = append(errs, fmt.Errorf("%v in enable-admission-plugins and disable-admission-plugins "+
|
||||
"overlapped", enablePlugins.Intersection(disablePlugins).List()))
|
||||
}
|
||||
|
||||
// Verify RecommendedPluginOrder.
|
||||
recommendPlugins := sets.NewString(a.RecommendedPluginOrder...)
|
||||
intersections := registeredPlugins.Intersection(recommendPlugins)
|
||||
if !intersections.Equal(recommendPlugins) {
|
||||
// Developer error, this should never run in.
|
||||
errs = append(errs, fmt.Errorf("plugins %v in RecommendedPluginOrder are not registered",
|
||||
recommendPlugins.Difference(intersections).List()))
|
||||
}
|
||||
if !intersections.Equal(registeredPlugins) {
|
||||
// Developer error, this should never run in.
|
||||
errs = append(errs, fmt.Errorf("plugins %v registered are not in RecommendedPluginOrder",
|
||||
registeredPlugins.Difference(intersections).List()))
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// enabledPluginNames makes use of RecommendedPluginOrder, DefaultOffPlugins,
|
||||
// EnablePlugins, DisablePlugins fields
|
||||
// to prepare a list of ordered plugin names that are enabled.
|
||||
func (a *AdmissionOptions) enabledPluginNames() []string {
|
||||
allOffPlugins := append(a.DefaultOffPlugins.List(), a.DisablePlugins...)
|
||||
disabledPlugins := sets.NewString(allOffPlugins...)
|
||||
enabledPlugins := sets.NewString(a.EnablePlugins...)
|
||||
disabledPlugins = disabledPlugins.Difference(enabledPlugins)
|
||||
|
||||
orderedPlugins := []string{}
|
||||
for _, plugin := range a.RecommendedPluginOrder {
|
||||
if !disabledPlugins.Has(plugin) {
|
||||
orderedPlugins = append(orderedPlugins, plugin)
|
||||
}
|
||||
}
|
||||
|
||||
return orderedPlugins
|
||||
}
|
||||
|
||||
//Return names of plugins which are enabled by default
|
||||
func (a *AdmissionOptions) defaultEnabledPluginNames() []string {
|
||||
defaultOnPluginNames := []string{}
|
||||
for _, pluginName := range a.RecommendedPluginOrder {
|
||||
if !a.DefaultOffPlugins.Has(pluginName) {
|
||||
defaultOnPluginNames = append(defaultOnPluginNames, pluginName)
|
||||
}
|
||||
}
|
||||
|
||||
return defaultOnPluginNames
|
||||
}
|
||||
111
vendor/k8s.io/apiserver/pkg/server/options/api_enablement.go
generated
vendored
Normal file
111
vendor/k8s.io/apiserver/pkg/server/options/api_enablement.go
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes 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 (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/apiserver/pkg/server/resourceconfig"
|
||||
serverstore "k8s.io/apiserver/pkg/server/storage"
|
||||
utilflag "k8s.io/apiserver/pkg/util/flag"
|
||||
)
|
||||
|
||||
// APIEnablementOptions contains the options for which resources to turn on and off.
|
||||
// Given small aggregated API servers, this option isn't required for "normal" API servers
|
||||
type APIEnablementOptions struct {
|
||||
RuntimeConfig utilflag.ConfigurationMap
|
||||
}
|
||||
|
||||
func NewAPIEnablementOptions() *APIEnablementOptions {
|
||||
return &APIEnablementOptions{
|
||||
RuntimeConfig: make(utilflag.ConfigurationMap),
|
||||
}
|
||||
}
|
||||
|
||||
// AddFlags adds flags for a specific APIServer to the specified FlagSet
|
||||
func (s *APIEnablementOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.Var(&s.RuntimeConfig, "runtime-config", ""+
|
||||
"A set of key=value pairs that describe runtime configuration that may be passed "+
|
||||
"to apiserver. <group>/<version> (or <version> for the core group) key can be used to "+
|
||||
"turn on/off specific api versions. api/all is special key to control all api versions, "+
|
||||
"be careful setting it false, unless you know what you do. api/legacy is deprecated, "+
|
||||
"we will remove it in the future, so stop using it.")
|
||||
}
|
||||
|
||||
// Validate validates RuntimeConfig with a list of registries.
|
||||
// Usually this list only has one element, the apiserver registry of the process.
|
||||
// But in the advanced (and usually not recommended) case of delegated apiservers there can be more.
|
||||
// Validate will filter out the known groups of each registry.
|
||||
// If anything is left over after that, an error is returned.
|
||||
func (s *APIEnablementOptions) Validate(registries ...GroupRegisty) []error {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errors := []error{}
|
||||
if s.RuntimeConfig["api/all"] == "false" && len(s.RuntimeConfig) == 1 {
|
||||
// Do not allow only set api/all=false, in such case apiserver startup has no meaning.
|
||||
return append(errors, fmt.Errorf("invalid key with only api/all=false"))
|
||||
}
|
||||
|
||||
groups, err := resourceconfig.ParseGroups(s.RuntimeConfig)
|
||||
if err != nil {
|
||||
return append(errors, err)
|
||||
}
|
||||
|
||||
for _, registry := range registries {
|
||||
// filter out known groups
|
||||
groups = unknownGroups(groups, registry)
|
||||
}
|
||||
if len(groups) != 0 {
|
||||
errors = append(errors, fmt.Errorf("unknown api groups %s", strings.Join(groups, ",")))
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
// ApplyTo override MergedResourceConfig with defaults and registry
|
||||
func (s *APIEnablementOptions) ApplyTo(c *server.Config, defaultResourceConfig *serverstore.ResourceConfig, registry resourceconfig.GroupVersionRegistry) error {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
mergedResourceConfig, err := resourceconfig.MergeAPIResourceConfigs(defaultResourceConfig, s.RuntimeConfig, registry)
|
||||
c.MergedResourceConfig = mergedResourceConfig
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func unknownGroups(groups []string, registry GroupRegisty) []string {
|
||||
unknownGroups := []string{}
|
||||
for _, group := range groups {
|
||||
if !registry.IsGroupRegistered(group) {
|
||||
unknownGroups = append(unknownGroups, group)
|
||||
}
|
||||
}
|
||||
return unknownGroups
|
||||
}
|
||||
|
||||
// GroupRegisty provides a method to check whether given group is registered.
|
||||
type GroupRegisty interface {
|
||||
// IsRegistered returns true if given group is registered.
|
||||
IsGroupRegistered(group string) bool
|
||||
}
|
||||
683
vendor/k8s.io/apiserver/pkg/server/options/audit.go
generated
vendored
Normal file
683
vendor/k8s.io/apiserver/pkg/server/options/audit.go
generated
vendored
Normal file
@@ -0,0 +1,683 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes 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 (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
"k8s.io/klog"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
auditinternal "k8s.io/apiserver/pkg/apis/audit"
|
||||
auditv1 "k8s.io/apiserver/pkg/apis/audit/v1"
|
||||
auditv1alpha1 "k8s.io/apiserver/pkg/apis/audit/v1alpha1"
|
||||
auditv1beta1 "k8s.io/apiserver/pkg/apis/audit/v1beta1"
|
||||
"k8s.io/apiserver/pkg/audit"
|
||||
"k8s.io/apiserver/pkg/audit/policy"
|
||||
"k8s.io/apiserver/pkg/features"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
pluginbuffered "k8s.io/apiserver/plugin/pkg/audit/buffered"
|
||||
plugindynamic "k8s.io/apiserver/plugin/pkg/audit/dynamic"
|
||||
pluginenforced "k8s.io/apiserver/plugin/pkg/audit/dynamic/enforced"
|
||||
pluginlog "k8s.io/apiserver/plugin/pkg/audit/log"
|
||||
plugintruncate "k8s.io/apiserver/plugin/pkg/audit/truncate"
|
||||
pluginwebhook "k8s.io/apiserver/plugin/pkg/audit/webhook"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
const (
|
||||
// Default configuration values for ModeBatch.
|
||||
defaultBatchBufferSize = 10000 // Buffer up to 10000 events before starting discarding.
|
||||
// These batch parameters are only used by the webhook backend.
|
||||
defaultBatchMaxSize = 400 // Only send up to 400 events at a time.
|
||||
defaultBatchMaxWait = 30 * time.Second // Send events at least twice a minute.
|
||||
defaultBatchThrottleQPS = 10 // Limit the send rate by 10 QPS.
|
||||
defaultBatchThrottleBurst = 15 // Allow up to 15 QPS burst.
|
||||
)
|
||||
|
||||
func appendBackend(existing, newBackend audit.Backend) audit.Backend {
|
||||
if existing == nil {
|
||||
return newBackend
|
||||
}
|
||||
if newBackend == nil {
|
||||
return existing
|
||||
}
|
||||
return audit.Union(existing, newBackend)
|
||||
}
|
||||
|
||||
type AuditOptions struct {
|
||||
// Policy configuration file for filtering audit events that are captured.
|
||||
// If unspecified, a default is provided.
|
||||
PolicyFile string
|
||||
|
||||
// Plugin options
|
||||
LogOptions AuditLogOptions
|
||||
WebhookOptions AuditWebhookOptions
|
||||
DynamicOptions AuditDynamicOptions
|
||||
}
|
||||
|
||||
const (
|
||||
// ModeBatch indicates that the audit backend should buffer audit events
|
||||
// internally, sending batch updates either once a certain number of
|
||||
// events have been received or a certain amount of time has passed.
|
||||
ModeBatch = "batch"
|
||||
// ModeBlocking causes the audit backend to block on every attempt to process
|
||||
// a set of events. This causes requests to the API server to wait for the
|
||||
// flush before sending a response.
|
||||
ModeBlocking = "blocking"
|
||||
// ModeBlockingStrict is the same as ModeBlocking, except when there is
|
||||
// a failure during audit logging at RequestReceived stage, the whole
|
||||
// request to apiserver will fail.
|
||||
ModeBlockingStrict = "blocking-strict"
|
||||
)
|
||||
|
||||
// AllowedModes is the modes known for audit backends.
|
||||
var AllowedModes = []string{
|
||||
ModeBatch,
|
||||
ModeBlocking,
|
||||
ModeBlockingStrict,
|
||||
}
|
||||
|
||||
type AuditBatchOptions struct {
|
||||
// Should the backend asynchronous batch events to the webhook backend or
|
||||
// should the backend block responses?
|
||||
//
|
||||
// Defaults to asynchronous batch events.
|
||||
Mode string
|
||||
// Configuration for batching backend. Only used in batch mode.
|
||||
BatchConfig pluginbuffered.BatchConfig
|
||||
}
|
||||
|
||||
type AuditTruncateOptions struct {
|
||||
// Whether truncating is enabled or not.
|
||||
Enabled bool
|
||||
|
||||
// Truncating configuration.
|
||||
TruncateConfig plugintruncate.Config
|
||||
}
|
||||
|
||||
// AuditLogOptions determines the output of the structured audit log by default.
|
||||
type AuditLogOptions struct {
|
||||
Path string
|
||||
MaxAge int
|
||||
MaxBackups int
|
||||
MaxSize int
|
||||
Format string
|
||||
|
||||
BatchOptions AuditBatchOptions
|
||||
TruncateOptions AuditTruncateOptions
|
||||
|
||||
// API group version used for serializing audit events.
|
||||
GroupVersionString string
|
||||
}
|
||||
|
||||
// AuditWebhookOptions control the webhook configuration for audit events.
|
||||
type AuditWebhookOptions struct {
|
||||
ConfigFile string
|
||||
InitialBackoff time.Duration
|
||||
|
||||
BatchOptions AuditBatchOptions
|
||||
TruncateOptions AuditTruncateOptions
|
||||
|
||||
// API group version used for serializing audit events.
|
||||
GroupVersionString string
|
||||
}
|
||||
|
||||
type AuditDynamicOptions struct {
|
||||
// Enabled tells whether the dynamic audit capability is enabled.
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
func NewAuditOptions() *AuditOptions {
|
||||
return &AuditOptions{
|
||||
WebhookOptions: AuditWebhookOptions{
|
||||
InitialBackoff: pluginwebhook.DefaultInitialBackoff,
|
||||
BatchOptions: AuditBatchOptions{
|
||||
Mode: ModeBatch,
|
||||
BatchConfig: defaultWebhookBatchConfig(),
|
||||
},
|
||||
TruncateOptions: NewAuditTruncateOptions(),
|
||||
GroupVersionString: "audit.k8s.io/v1",
|
||||
},
|
||||
LogOptions: AuditLogOptions{
|
||||
Format: pluginlog.FormatJson,
|
||||
BatchOptions: AuditBatchOptions{
|
||||
Mode: ModeBlocking,
|
||||
BatchConfig: defaultLogBatchConfig(),
|
||||
},
|
||||
TruncateOptions: NewAuditTruncateOptions(),
|
||||
GroupVersionString: "audit.k8s.io/v1",
|
||||
},
|
||||
DynamicOptions: AuditDynamicOptions{
|
||||
Enabled: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func NewAuditTruncateOptions() AuditTruncateOptions {
|
||||
return AuditTruncateOptions{
|
||||
Enabled: false,
|
||||
TruncateConfig: plugintruncate.Config{
|
||||
MaxBatchSize: 10 * 1024 * 1024, // 10MB
|
||||
MaxEventSize: 100 * 1024, // 100KB
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Validate checks invalid config combination
|
||||
func (o *AuditOptions) Validate() []error {
|
||||
if o == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var allErrors []error
|
||||
allErrors = append(allErrors, o.LogOptions.Validate()...)
|
||||
allErrors = append(allErrors, o.WebhookOptions.Validate()...)
|
||||
allErrors = append(allErrors, o.DynamicOptions.Validate()...)
|
||||
|
||||
return allErrors
|
||||
}
|
||||
|
||||
func validateBackendMode(pluginName string, mode string) error {
|
||||
for _, m := range AllowedModes {
|
||||
if m == mode {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("invalid audit %s mode %s, allowed modes are %q", pluginName, mode, strings.Join(AllowedModes, ","))
|
||||
}
|
||||
|
||||
func validateBackendBatchOptions(pluginName string, options AuditBatchOptions) error {
|
||||
if err := validateBackendMode(pluginName, options.Mode); err != nil {
|
||||
return err
|
||||
}
|
||||
if options.Mode != ModeBatch {
|
||||
// Don't validate the unused options.
|
||||
return nil
|
||||
}
|
||||
config := options.BatchConfig
|
||||
if config.BufferSize <= 0 {
|
||||
return fmt.Errorf("invalid audit batch %s buffer size %v, must be a positive number", pluginName, config.BufferSize)
|
||||
}
|
||||
if config.MaxBatchSize <= 0 {
|
||||
return fmt.Errorf("invalid audit batch %s max batch size %v, must be a positive number", pluginName, config.MaxBatchSize)
|
||||
}
|
||||
if config.ThrottleEnable {
|
||||
if config.ThrottleQPS <= 0 {
|
||||
return fmt.Errorf("invalid audit batch %s throttle QPS %v, must be a positive number", pluginName, config.ThrottleQPS)
|
||||
}
|
||||
if config.ThrottleBurst <= 0 {
|
||||
return fmt.Errorf("invalid audit batch %s throttle burst %v, must be a positive number", pluginName, config.ThrottleBurst)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var knownGroupVersions = []schema.GroupVersion{
|
||||
auditv1alpha1.SchemeGroupVersion,
|
||||
auditv1beta1.SchemeGroupVersion,
|
||||
auditv1.SchemeGroupVersion,
|
||||
}
|
||||
|
||||
func validateGroupVersionString(groupVersion string) error {
|
||||
gv, err := schema.ParseGroupVersion(groupVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !knownGroupVersion(gv) {
|
||||
return fmt.Errorf("invalid group version, allowed versions are %q", knownGroupVersions)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func knownGroupVersion(gv schema.GroupVersion) bool {
|
||||
for _, knownGv := range knownGroupVersions {
|
||||
if gv == knownGv {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (o *AuditOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
if o == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fs.StringVar(&o.PolicyFile, "audit-policy-file", o.PolicyFile,
|
||||
"Path to the file that defines the audit policy configuration.")
|
||||
|
||||
o.LogOptions.AddFlags(fs)
|
||||
o.LogOptions.BatchOptions.AddFlags(pluginlog.PluginName, fs)
|
||||
o.LogOptions.TruncateOptions.AddFlags(pluginlog.PluginName, fs)
|
||||
o.WebhookOptions.AddFlags(fs)
|
||||
o.WebhookOptions.BatchOptions.AddFlags(pluginwebhook.PluginName, fs)
|
||||
o.WebhookOptions.TruncateOptions.AddFlags(pluginwebhook.PluginName, fs)
|
||||
o.DynamicOptions.AddFlags(fs)
|
||||
}
|
||||
|
||||
func (o *AuditOptions) ApplyTo(
|
||||
c *server.Config,
|
||||
kubeClientConfig *restclient.Config,
|
||||
informers informers.SharedInformerFactory,
|
||||
processInfo *ProcessInfo,
|
||||
webhookOptions *WebhookOptions,
|
||||
) error {
|
||||
if o == nil {
|
||||
return nil
|
||||
}
|
||||
if c == nil {
|
||||
return fmt.Errorf("server config must be non-nil")
|
||||
}
|
||||
|
||||
// 1. Build policy checker
|
||||
checker, err := o.newPolicyChecker()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. Build log backend
|
||||
var logBackend audit.Backend
|
||||
if w := o.LogOptions.getWriter(); w != nil {
|
||||
if checker == nil {
|
||||
klog.V(2).Info("No audit policy file provided, no events will be recorded for log backend")
|
||||
} else {
|
||||
logBackend = o.LogOptions.newBackend(w)
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Build webhook backend
|
||||
var webhookBackend audit.Backend
|
||||
if o.WebhookOptions.enabled() {
|
||||
if checker == nil {
|
||||
klog.V(2).Info("No audit policy file provided, no events will be recorded for webhook backend")
|
||||
} else {
|
||||
webhookBackend, err = o.WebhookOptions.newUntruncatedBackend()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
groupVersion, err := schema.ParseGroupVersion(o.WebhookOptions.GroupVersionString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 4. Apply dynamic options.
|
||||
var dynamicBackend audit.Backend
|
||||
if o.DynamicOptions.enabled() {
|
||||
// if dynamic is enabled the webhook and log backends need to be wrapped in an enforced backend with the static policy
|
||||
if webhookBackend != nil {
|
||||
webhookBackend = pluginenforced.NewBackend(webhookBackend, checker)
|
||||
}
|
||||
if logBackend != nil {
|
||||
logBackend = pluginenforced.NewBackend(logBackend, checker)
|
||||
}
|
||||
// build dynamic backend
|
||||
dynamicBackend, checker, err = o.DynamicOptions.newBackend(c.ExternalAddress, kubeClientConfig, informers, processInfo, webhookOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// union dynamic and webhook backends so that truncate options can be applied to both
|
||||
dynamicBackend = appendBackend(webhookBackend, dynamicBackend)
|
||||
dynamicBackend = o.WebhookOptions.TruncateOptions.wrapBackend(dynamicBackend, groupVersion)
|
||||
} else if webhookBackend != nil {
|
||||
// if only webhook is enabled wrap it in the truncate options
|
||||
dynamicBackend = o.WebhookOptions.TruncateOptions.wrapBackend(webhookBackend, groupVersion)
|
||||
}
|
||||
|
||||
// 5. Set the policy checker
|
||||
c.AuditPolicyChecker = checker
|
||||
|
||||
// 6. Join the log backend with the webhooks
|
||||
c.AuditBackend = appendBackend(logBackend, dynamicBackend)
|
||||
|
||||
if c.AuditBackend != nil {
|
||||
klog.V(2).Infof("Using audit backend: %s", c.AuditBackend)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *AuditOptions) newPolicyChecker() (policy.Checker, error) {
|
||||
if o.PolicyFile == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
p, err := policy.LoadPolicyFromFile(o.PolicyFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("loading audit policy file: %v", err)
|
||||
}
|
||||
return policy.NewChecker(p), nil
|
||||
}
|
||||
|
||||
func (o *AuditBatchOptions) AddFlags(pluginName string, fs *pflag.FlagSet) {
|
||||
fs.StringVar(&o.Mode, fmt.Sprintf("audit-%s-mode", pluginName), o.Mode,
|
||||
"Strategy for sending audit events. Blocking indicates sending events should block"+
|
||||
" server responses. Batch causes the backend to buffer and write events"+
|
||||
" asynchronously. Known modes are "+strings.Join(AllowedModes, ",")+".")
|
||||
fs.IntVar(&o.BatchConfig.BufferSize, fmt.Sprintf("audit-%s-batch-buffer-size", pluginName),
|
||||
o.BatchConfig.BufferSize, "The size of the buffer to store events before "+
|
||||
"batching and writing. Only used in batch mode.")
|
||||
fs.IntVar(&o.BatchConfig.MaxBatchSize, fmt.Sprintf("audit-%s-batch-max-size", pluginName),
|
||||
o.BatchConfig.MaxBatchSize, "The maximum size of a batch. Only used in batch mode.")
|
||||
fs.DurationVar(&o.BatchConfig.MaxBatchWait, fmt.Sprintf("audit-%s-batch-max-wait", pluginName),
|
||||
o.BatchConfig.MaxBatchWait, "The amount of time to wait before force writing the "+
|
||||
"batch that hadn't reached the max size. Only used in batch mode.")
|
||||
fs.BoolVar(&o.BatchConfig.ThrottleEnable, fmt.Sprintf("audit-%s-batch-throttle-enable", pluginName),
|
||||
o.BatchConfig.ThrottleEnable, "Whether batching throttling is enabled. Only used in batch mode.")
|
||||
fs.Float32Var(&o.BatchConfig.ThrottleQPS, fmt.Sprintf("audit-%s-batch-throttle-qps", pluginName),
|
||||
o.BatchConfig.ThrottleQPS, "Maximum average number of batches per second. "+
|
||||
"Only used in batch mode.")
|
||||
fs.IntVar(&o.BatchConfig.ThrottleBurst, fmt.Sprintf("audit-%s-batch-throttle-burst", pluginName),
|
||||
o.BatchConfig.ThrottleBurst, "Maximum number of requests sent at the same "+
|
||||
"moment if ThrottleQPS was not utilized before. Only used in batch mode.")
|
||||
}
|
||||
|
||||
type ignoreErrorsBackend struct {
|
||||
audit.Backend
|
||||
}
|
||||
|
||||
func (i *ignoreErrorsBackend) ProcessEvents(ev ...*auditinternal.Event) bool {
|
||||
i.Backend.ProcessEvents(ev...)
|
||||
return true
|
||||
}
|
||||
|
||||
func (i *ignoreErrorsBackend) String() string {
|
||||
return fmt.Sprintf("ignoreErrors<%s>", i.Backend)
|
||||
}
|
||||
|
||||
func (o *AuditBatchOptions) wrapBackend(delegate audit.Backend) audit.Backend {
|
||||
if o.Mode == ModeBlockingStrict {
|
||||
return delegate
|
||||
}
|
||||
if o.Mode == ModeBlocking {
|
||||
return &ignoreErrorsBackend{Backend: delegate}
|
||||
}
|
||||
return pluginbuffered.NewBackend(delegate, o.BatchConfig)
|
||||
}
|
||||
|
||||
func (o *AuditTruncateOptions) Validate(pluginName string) error {
|
||||
config := o.TruncateConfig
|
||||
if config.MaxEventSize <= 0 {
|
||||
return fmt.Errorf("invalid audit truncate %s max event size %v, must be a positive number", pluginName, config.MaxEventSize)
|
||||
}
|
||||
if config.MaxBatchSize < config.MaxEventSize {
|
||||
return fmt.Errorf("invalid audit truncate %s max batch size %v, must be greater than "+
|
||||
"max event size (%v)", pluginName, config.MaxBatchSize, config.MaxEventSize)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *AuditTruncateOptions) AddFlags(pluginName string, fs *pflag.FlagSet) {
|
||||
fs.BoolVar(&o.Enabled, fmt.Sprintf("audit-%s-truncate-enabled", pluginName),
|
||||
o.Enabled, "Whether event and batch truncating is enabled.")
|
||||
fs.Int64Var(&o.TruncateConfig.MaxBatchSize, fmt.Sprintf("audit-%s-truncate-max-batch-size", pluginName),
|
||||
o.TruncateConfig.MaxBatchSize, "Maximum size of the batch sent to the underlying backend. "+
|
||||
"Actual serialized size can be several hundreds of bytes greater. If a batch exceeds this limit, "+
|
||||
"it is split into several batches of smaller size.")
|
||||
fs.Int64Var(&o.TruncateConfig.MaxEventSize, fmt.Sprintf("audit-%s-truncate-max-event-size", pluginName),
|
||||
o.TruncateConfig.MaxEventSize, "Maximum size of the audit event sent to the underlying backend. "+
|
||||
"If the size of an event is greater than this number, first request and response are removed, and "+
|
||||
"if this doesn't reduce the size enough, event is discarded.")
|
||||
}
|
||||
|
||||
func (o *AuditTruncateOptions) wrapBackend(delegate audit.Backend, gv schema.GroupVersion) audit.Backend {
|
||||
if !o.Enabled {
|
||||
return delegate
|
||||
}
|
||||
return plugintruncate.NewBackend(delegate, o.TruncateConfig, gv)
|
||||
}
|
||||
|
||||
func (o *AuditLogOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&o.Path, "audit-log-path", o.Path,
|
||||
"If set, all requests coming to the apiserver will be logged to this file. '-' means standard out.")
|
||||
fs.IntVar(&o.MaxAge, "audit-log-maxage", o.MaxAge,
|
||||
"The maximum number of days to retain old audit log files based on the timestamp encoded in their filename.")
|
||||
fs.IntVar(&o.MaxBackups, "audit-log-maxbackup", o.MaxBackups,
|
||||
"The maximum number of old audit log files to retain.")
|
||||
fs.IntVar(&o.MaxSize, "audit-log-maxsize", o.MaxSize,
|
||||
"The maximum size in megabytes of the audit log file before it gets rotated.")
|
||||
fs.StringVar(&o.Format, "audit-log-format", o.Format,
|
||||
"Format of saved audits. \"legacy\" indicates 1-line text format for each event."+
|
||||
" \"json\" indicates structured json format. Known formats are "+
|
||||
strings.Join(pluginlog.AllowedFormats, ",")+".")
|
||||
fs.StringVar(&o.GroupVersionString, "audit-log-version", o.GroupVersionString,
|
||||
"API group and version used for serializing audit events written to log.")
|
||||
}
|
||||
|
||||
func (o *AuditLogOptions) Validate() []error {
|
||||
// Check whether the log backend is enabled based on the options.
|
||||
if !o.enabled() {
|
||||
return nil
|
||||
}
|
||||
|
||||
var allErrors []error
|
||||
|
||||
if err := validateBackendBatchOptions(pluginlog.PluginName, o.BatchOptions); err != nil {
|
||||
allErrors = append(allErrors, err)
|
||||
}
|
||||
if err := o.TruncateOptions.Validate(pluginlog.PluginName); err != nil {
|
||||
allErrors = append(allErrors, err)
|
||||
}
|
||||
|
||||
if err := validateGroupVersionString(o.GroupVersionString); err != nil {
|
||||
allErrors = append(allErrors, err)
|
||||
}
|
||||
|
||||
// Check log format
|
||||
validFormat := false
|
||||
for _, f := range pluginlog.AllowedFormats {
|
||||
if f == o.Format {
|
||||
validFormat = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !validFormat {
|
||||
allErrors = append(allErrors, fmt.Errorf("invalid audit log format %s, allowed formats are %q", o.Format, strings.Join(pluginlog.AllowedFormats, ",")))
|
||||
}
|
||||
|
||||
// Check validities of MaxAge, MaxBackups and MaxSize of log options, if file log backend is enabled.
|
||||
if o.MaxAge < 0 {
|
||||
allErrors = append(allErrors, fmt.Errorf("--audit-log-maxage %v can't be a negative number", o.MaxAge))
|
||||
}
|
||||
if o.MaxBackups < 0 {
|
||||
allErrors = append(allErrors, fmt.Errorf("--audit-log-maxbackup %v can't be a negative number", o.MaxBackups))
|
||||
}
|
||||
if o.MaxSize < 0 {
|
||||
allErrors = append(allErrors, fmt.Errorf("--audit-log-maxsize %v can't be a negative number", o.MaxSize))
|
||||
}
|
||||
|
||||
return allErrors
|
||||
}
|
||||
|
||||
// Check whether the log backend is enabled based on the options.
|
||||
func (o *AuditLogOptions) enabled() bool {
|
||||
return o != nil && o.Path != ""
|
||||
}
|
||||
|
||||
func (o *AuditLogOptions) getWriter() io.Writer {
|
||||
if !o.enabled() {
|
||||
return nil
|
||||
}
|
||||
|
||||
var w io.Writer = os.Stdout
|
||||
if o.Path != "-" {
|
||||
w = &lumberjack.Logger{
|
||||
Filename: o.Path,
|
||||
MaxAge: o.MaxAge,
|
||||
MaxBackups: o.MaxBackups,
|
||||
MaxSize: o.MaxSize,
|
||||
}
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
func (o *AuditLogOptions) newBackend(w io.Writer) audit.Backend {
|
||||
groupVersion, _ := schema.ParseGroupVersion(o.GroupVersionString)
|
||||
log := pluginlog.NewBackend(w, o.Format, groupVersion)
|
||||
log = o.BatchOptions.wrapBackend(log)
|
||||
log = o.TruncateOptions.wrapBackend(log, groupVersion)
|
||||
return log
|
||||
}
|
||||
|
||||
func (o *AuditWebhookOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&o.ConfigFile, "audit-webhook-config-file", o.ConfigFile,
|
||||
"Path to a kubeconfig formatted file that defines the audit webhook configuration.")
|
||||
fs.DurationVar(&o.InitialBackoff, "audit-webhook-initial-backoff",
|
||||
o.InitialBackoff, "The amount of time to wait before retrying the first failed request.")
|
||||
fs.DurationVar(&o.InitialBackoff, "audit-webhook-batch-initial-backoff",
|
||||
o.InitialBackoff, "The amount of time to wait before retrying the first failed request.")
|
||||
fs.MarkDeprecated("audit-webhook-batch-initial-backoff",
|
||||
"Deprecated, use --audit-webhook-initial-backoff instead.")
|
||||
fs.StringVar(&o.GroupVersionString, "audit-webhook-version", o.GroupVersionString,
|
||||
"API group and version used for serializing audit events written to webhook.")
|
||||
}
|
||||
|
||||
func (o *AuditWebhookOptions) Validate() []error {
|
||||
if !o.enabled() {
|
||||
return nil
|
||||
}
|
||||
|
||||
var allErrors []error
|
||||
if err := validateBackendBatchOptions(pluginwebhook.PluginName, o.BatchOptions); err != nil {
|
||||
allErrors = append(allErrors, err)
|
||||
}
|
||||
if err := o.TruncateOptions.Validate(pluginwebhook.PluginName); err != nil {
|
||||
allErrors = append(allErrors, err)
|
||||
}
|
||||
|
||||
if err := validateGroupVersionString(o.GroupVersionString); err != nil {
|
||||
allErrors = append(allErrors, err)
|
||||
}
|
||||
return allErrors
|
||||
}
|
||||
|
||||
func (o *AuditWebhookOptions) enabled() bool {
|
||||
return o != nil && o.ConfigFile != ""
|
||||
}
|
||||
|
||||
// newUntruncatedBackend returns a webhook backend without the truncate options applied
|
||||
// this is done so that the same trucate backend can wrap both the webhook and dynamic backends
|
||||
func (o *AuditWebhookOptions) newUntruncatedBackend() (audit.Backend, error) {
|
||||
groupVersion, _ := schema.ParseGroupVersion(o.GroupVersionString)
|
||||
webhook, err := pluginwebhook.NewBackend(o.ConfigFile, groupVersion, o.InitialBackoff)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("initializing audit webhook: %v", err)
|
||||
}
|
||||
webhook = o.BatchOptions.wrapBackend(webhook)
|
||||
return webhook, nil
|
||||
}
|
||||
|
||||
func (o *AuditDynamicOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.BoolVar(&o.Enabled, "audit-dynamic-configuration", o.Enabled,
|
||||
"Enables dynamic audit configuration. This feature also requires the DynamicAuditing feature flag")
|
||||
}
|
||||
|
||||
func (o *AuditDynamicOptions) enabled() bool {
|
||||
return o.Enabled && utilfeature.DefaultFeatureGate.Enabled(features.DynamicAuditing)
|
||||
}
|
||||
|
||||
func (o *AuditDynamicOptions) Validate() []error {
|
||||
var allErrors []error
|
||||
if o.Enabled && !utilfeature.DefaultFeatureGate.Enabled(features.DynamicAuditing) {
|
||||
allErrors = append(allErrors, fmt.Errorf("--audit-dynamic-configuration set, but DynamicAuditing feature gate is not enabled"))
|
||||
}
|
||||
return allErrors
|
||||
}
|
||||
|
||||
func (o *AuditDynamicOptions) newBackend(
|
||||
hostname string,
|
||||
kubeClientConfig *restclient.Config,
|
||||
informers informers.SharedInformerFactory,
|
||||
processInfo *ProcessInfo,
|
||||
webhookOptions *WebhookOptions,
|
||||
) (audit.Backend, policy.Checker, error) {
|
||||
if err := validateProcessInfo(processInfo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
clientset, err := kubernetes.NewForConfig(kubeClientConfig)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if webhookOptions == nil {
|
||||
webhookOptions = NewWebhookOptions()
|
||||
}
|
||||
checker := policy.NewDynamicChecker()
|
||||
informer := informers.Auditregistration().V1alpha1().AuditSinks()
|
||||
eventSink := &v1core.EventSinkImpl{Interface: clientset.CoreV1().Events(processInfo.Namespace)}
|
||||
|
||||
dc := &plugindynamic.Config{
|
||||
Informer: informer,
|
||||
BufferedConfig: plugindynamic.NewDefaultWebhookBatchConfig(),
|
||||
EventConfig: plugindynamic.EventConfig{
|
||||
Sink: eventSink,
|
||||
Source: corev1.EventSource{
|
||||
Component: processInfo.Name,
|
||||
Host: hostname,
|
||||
},
|
||||
},
|
||||
WebhookConfig: plugindynamic.WebhookConfig{
|
||||
AuthInfoResolverWrapper: webhookOptions.AuthInfoResolverWrapper,
|
||||
ServiceResolver: webhookOptions.ServiceResolver,
|
||||
},
|
||||
}
|
||||
backend, err := plugindynamic.NewBackend(dc)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("could not create dynamic audit backend: %v", err)
|
||||
}
|
||||
return backend, checker, nil
|
||||
}
|
||||
|
||||
// defaultWebhookBatchConfig returns the default BatchConfig used by the Webhook backend.
|
||||
func defaultWebhookBatchConfig() pluginbuffered.BatchConfig {
|
||||
return pluginbuffered.BatchConfig{
|
||||
BufferSize: defaultBatchBufferSize,
|
||||
MaxBatchSize: defaultBatchMaxSize,
|
||||
MaxBatchWait: defaultBatchMaxWait,
|
||||
|
||||
ThrottleEnable: true,
|
||||
ThrottleQPS: defaultBatchThrottleQPS,
|
||||
ThrottleBurst: defaultBatchThrottleBurst,
|
||||
|
||||
AsyncDelegate: true,
|
||||
}
|
||||
}
|
||||
|
||||
// defaultLogBatchConfig returns the default BatchConfig used by the Log backend.
|
||||
func defaultLogBatchConfig() pluginbuffered.BatchConfig {
|
||||
return pluginbuffered.BatchConfig{
|
||||
BufferSize: defaultBatchBufferSize,
|
||||
// Batching is not useful for the log-file backend.
|
||||
// MaxBatchWait ignored.
|
||||
MaxBatchSize: 1,
|
||||
ThrottleEnable: false,
|
||||
// Asynchronous log threads just create lock contention.
|
||||
AsyncDelegate: false,
|
||||
}
|
||||
}
|
||||
401
vendor/k8s.io/apiserver/pkg/server/options/authentication.go
generated
vendored
Normal file
401
vendor/k8s.io/apiserver/pkg/server/options/authentication.go
generated
vendored
Normal file
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes 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 (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/klog"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apiserver/pkg/authentication/authenticatorfactory"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
openapicommon "k8s.io/kube-openapi/pkg/common"
|
||||
)
|
||||
|
||||
type RequestHeaderAuthenticationOptions struct {
|
||||
// ClientCAFile is the root certificate bundle to verify client certificates on incoming requests
|
||||
// before trusting usernames in headers.
|
||||
ClientCAFile string
|
||||
|
||||
UsernameHeaders []string
|
||||
GroupHeaders []string
|
||||
ExtraHeaderPrefixes []string
|
||||
AllowedNames []string
|
||||
}
|
||||
|
||||
func (s *RequestHeaderAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fs.StringSliceVar(&s.UsernameHeaders, "requestheader-username-headers", s.UsernameHeaders, ""+
|
||||
"List of request headers to inspect for usernames. X-Remote-User is common.")
|
||||
|
||||
fs.StringSliceVar(&s.GroupHeaders, "requestheader-group-headers", s.GroupHeaders, ""+
|
||||
"List of request headers to inspect for groups. X-Remote-Group is suggested.")
|
||||
|
||||
fs.StringSliceVar(&s.ExtraHeaderPrefixes, "requestheader-extra-headers-prefix", s.ExtraHeaderPrefixes, ""+
|
||||
"List of request header prefixes to inspect. X-Remote-Extra- is suggested.")
|
||||
|
||||
fs.StringVar(&s.ClientCAFile, "requestheader-client-ca-file", s.ClientCAFile, ""+
|
||||
"Root certificate bundle to use to verify client certificates on incoming requests "+
|
||||
"before trusting usernames in headers specified by --requestheader-username-headers. "+
|
||||
"WARNING: generally do not depend on authorization being already done for incoming requests.")
|
||||
|
||||
fs.StringSliceVar(&s.AllowedNames, "requestheader-allowed-names", s.AllowedNames, ""+
|
||||
"List of client certificate common names to allow to provide usernames in headers "+
|
||||
"specified by --requestheader-username-headers. If empty, any client certificate validated "+
|
||||
"by the authorities in --requestheader-client-ca-file is allowed.")
|
||||
}
|
||||
|
||||
// ToAuthenticationRequestHeaderConfig returns a RequestHeaderConfig config object for these options
|
||||
// if necessary, nil otherwise.
|
||||
func (s *RequestHeaderAuthenticationOptions) ToAuthenticationRequestHeaderConfig() *authenticatorfactory.RequestHeaderConfig {
|
||||
if len(s.ClientCAFile) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &authenticatorfactory.RequestHeaderConfig{
|
||||
UsernameHeaders: s.UsernameHeaders,
|
||||
GroupHeaders: s.GroupHeaders,
|
||||
ExtraHeaderPrefixes: s.ExtraHeaderPrefixes,
|
||||
ClientCA: s.ClientCAFile,
|
||||
AllowedClientNames: s.AllowedNames,
|
||||
}
|
||||
}
|
||||
|
||||
type ClientCertAuthenticationOptions struct {
|
||||
// ClientCA is the certificate bundle for all the signers that you'll recognize for incoming client certificates
|
||||
ClientCA string
|
||||
}
|
||||
|
||||
func (s *ClientCertAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&s.ClientCA, "client-ca-file", s.ClientCA, ""+
|
||||
"If set, any request presenting a client certificate signed by one of "+
|
||||
"the authorities in the client-ca-file is authenticated with an identity "+
|
||||
"corresponding to the CommonName of the client certificate.")
|
||||
}
|
||||
|
||||
// DelegatingAuthenticationOptions provides an easy way for composing API servers to delegate their authentication to
|
||||
// the root kube API server. The API federator will act as
|
||||
// a front proxy and direction connections will be able to delegate to the core kube API server
|
||||
type DelegatingAuthenticationOptions struct {
|
||||
// RemoteKubeConfigFile is the file to use to connect to a "normal" kube API server which hosts the
|
||||
// TokenAccessReview.authentication.k8s.io endpoint for checking tokens.
|
||||
RemoteKubeConfigFile string
|
||||
// RemoteKubeConfigFileOptional is specifying whether not specifying the kubeconfig or
|
||||
// a missing in-cluster config will be fatal.
|
||||
RemoteKubeConfigFileOptional bool
|
||||
|
||||
// CacheTTL is the length of time that a token authentication answer will be cached.
|
||||
CacheTTL time.Duration
|
||||
|
||||
ClientCert ClientCertAuthenticationOptions
|
||||
RequestHeader RequestHeaderAuthenticationOptions
|
||||
|
||||
// SkipInClusterLookup indicates missing authentication configuration should not be retrieved from the cluster configmap
|
||||
SkipInClusterLookup bool
|
||||
|
||||
// TolerateInClusterLookupFailure indicates failures to look up authentication configuration from the cluster configmap should not be fatal.
|
||||
// Setting this can result in an authenticator that will reject all requests.
|
||||
TolerateInClusterLookupFailure bool
|
||||
}
|
||||
|
||||
func NewDelegatingAuthenticationOptions() *DelegatingAuthenticationOptions {
|
||||
return &DelegatingAuthenticationOptions{
|
||||
// very low for responsiveness, but high enough to handle storms
|
||||
CacheTTL: 10 * time.Second,
|
||||
ClientCert: ClientCertAuthenticationOptions{},
|
||||
RequestHeader: RequestHeaderAuthenticationOptions{
|
||||
UsernameHeaders: []string{"x-remote-user"},
|
||||
GroupHeaders: []string{"x-remote-group"},
|
||||
ExtraHeaderPrefixes: []string{"x-remote-extra-"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DelegatingAuthenticationOptions) Validate() []error {
|
||||
allErrors := []error{}
|
||||
return allErrors
|
||||
}
|
||||
|
||||
func (s *DelegatingAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var optionalKubeConfigSentence string
|
||||
if s.RemoteKubeConfigFileOptional {
|
||||
optionalKubeConfigSentence = " This is optional. If empty, all token requests are considered to be anonymous and no client CA is looked up in the cluster."
|
||||
}
|
||||
fs.StringVar(&s.RemoteKubeConfigFile, "authentication-kubeconfig", s.RemoteKubeConfigFile, ""+
|
||||
"kubeconfig file pointing at the 'core' kubernetes server with enough rights to create "+
|
||||
"tokenaccessreviews.authentication.k8s.io."+optionalKubeConfigSentence)
|
||||
|
||||
fs.DurationVar(&s.CacheTTL, "authentication-token-webhook-cache-ttl", s.CacheTTL,
|
||||
"The duration to cache responses from the webhook token authenticator.")
|
||||
|
||||
s.ClientCert.AddFlags(fs)
|
||||
s.RequestHeader.AddFlags(fs)
|
||||
|
||||
fs.BoolVar(&s.SkipInClusterLookup, "authentication-skip-lookup", s.SkipInClusterLookup, ""+
|
||||
"If false, the authentication-kubeconfig will be used to lookup missing authentication "+
|
||||
"configuration from the cluster.")
|
||||
fs.BoolVar(&s.TolerateInClusterLookupFailure, "authentication-tolerate-lookup-failure", s.TolerateInClusterLookupFailure, ""+
|
||||
"If true, failures to look up missing authentication configuration from the cluster are not considered fatal. "+
|
||||
"Note that this can result in authentication that treats all requests as anonymous.")
|
||||
}
|
||||
|
||||
func (s *DelegatingAuthenticationOptions) ApplyTo(c *server.AuthenticationInfo, servingInfo *server.SecureServingInfo, openAPIConfig *openapicommon.Config) error {
|
||||
if s == nil {
|
||||
c.Authenticator = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
cfg := authenticatorfactory.DelegatingAuthenticatorConfig{
|
||||
Anonymous: true,
|
||||
CacheTTL: s.CacheTTL,
|
||||
}
|
||||
|
||||
client, err := s.getClient()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get delegated authentication kubeconfig: %v", err)
|
||||
}
|
||||
|
||||
// configure token review
|
||||
if client != nil {
|
||||
cfg.TokenAccessReviewClient = client.AuthenticationV1beta1().TokenReviews()
|
||||
}
|
||||
|
||||
// look into configmaps/external-apiserver-authentication for missing authn info
|
||||
if !s.SkipInClusterLookup {
|
||||
err := s.lookupMissingConfigInCluster(client)
|
||||
if err != nil {
|
||||
if s.TolerateInClusterLookupFailure {
|
||||
klog.Warningf("Error looking up in-cluster authentication configuration: %v", err)
|
||||
klog.Warningf("Continuing without authentication configuration. This may treat all requests as anonymous.")
|
||||
klog.Warningf("To require authentication configuration lookup to succeed, set --authentication-tolerate-lookup-failure=false")
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// configure AuthenticationInfo config
|
||||
cfg.ClientCAFile = s.ClientCert.ClientCA
|
||||
if err = c.ApplyClientCert(s.ClientCert.ClientCA, servingInfo); err != nil {
|
||||
return fmt.Errorf("unable to load client CA file: %v", err)
|
||||
}
|
||||
|
||||
cfg.RequestHeaderConfig = s.RequestHeader.ToAuthenticationRequestHeaderConfig()
|
||||
if err = c.ApplyClientCert(s.RequestHeader.ClientCAFile, servingInfo); err != nil {
|
||||
return fmt.Errorf("unable to load client CA file: %v", err)
|
||||
}
|
||||
|
||||
// create authenticator
|
||||
authenticator, securityDefinitions, err := cfg.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Authenticator = authenticator
|
||||
if openAPIConfig != nil {
|
||||
openAPIConfig.SecurityDefinitions = securityDefinitions
|
||||
}
|
||||
c.SupportsBasicAuth = false
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
authenticationConfigMapNamespace = metav1.NamespaceSystem
|
||||
// authenticationConfigMapName is the name of ConfigMap in the kube-system namespace holding the root certificate
|
||||
// bundle to use to verify client certificates on incoming requests before trusting usernames in headers specified
|
||||
// by --requestheader-username-headers. This is created in the cluster by the kube-apiserver.
|
||||
// "WARNING: generally do not depend on authorization being already done for incoming requests.")
|
||||
authenticationConfigMapName = "extension-apiserver-authentication"
|
||||
authenticationRoleName = "extension-apiserver-authentication-reader"
|
||||
)
|
||||
|
||||
func (s *DelegatingAuthenticationOptions) lookupMissingConfigInCluster(client kubernetes.Interface) error {
|
||||
if len(s.ClientCert.ClientCA) > 0 && len(s.RequestHeader.ClientCAFile) > 0 {
|
||||
return nil
|
||||
}
|
||||
if client == nil {
|
||||
if len(s.ClientCert.ClientCA) == 0 {
|
||||
klog.Warningf("No authentication-kubeconfig provided in order to lookup client-ca-file in configmap/%s in %s, so client certificate authentication won't work.", authenticationConfigMapName, authenticationConfigMapNamespace)
|
||||
}
|
||||
if len(s.RequestHeader.ClientCAFile) == 0 {
|
||||
klog.Warningf("No authentication-kubeconfig provided in order to lookup requestheader-client-ca-file in configmap/%s in %s, so request-header client certificate authentication won't work.", authenticationConfigMapName, authenticationConfigMapNamespace)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
authConfigMap, err := client.CoreV1().ConfigMaps(authenticationConfigMapNamespace).Get(authenticationConfigMapName, metav1.GetOptions{})
|
||||
switch {
|
||||
case errors.IsNotFound(err):
|
||||
// ignore, authConfigMap is nil now
|
||||
case errors.IsForbidden(err):
|
||||
klog.Warningf("Unable to get configmap/%s in %s. Usually fixed by "+
|
||||
"'kubectl create rolebinding -n %s ROLE_NAME --role=%s --serviceaccount=YOUR_NS:YOUR_SA'",
|
||||
authenticationConfigMapName, authenticationConfigMapNamespace, authenticationConfigMapNamespace, authenticationRoleName)
|
||||
return err
|
||||
case err != nil:
|
||||
return err
|
||||
}
|
||||
|
||||
if len(s.ClientCert.ClientCA) == 0 {
|
||||
if authConfigMap != nil {
|
||||
opt, err := inClusterClientCA(authConfigMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if opt != nil {
|
||||
s.ClientCert = *opt
|
||||
}
|
||||
}
|
||||
if len(s.ClientCert.ClientCA) == 0 {
|
||||
klog.Warningf("Cluster doesn't provide client-ca-file in configmap/%s in %s, so client certificate authentication won't work.", authenticationConfigMapName, authenticationConfigMapNamespace)
|
||||
}
|
||||
}
|
||||
|
||||
if len(s.RequestHeader.ClientCAFile) == 0 {
|
||||
if authConfigMap != nil {
|
||||
opt, err := inClusterRequestHeader(authConfigMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if opt != nil {
|
||||
s.RequestHeader = *opt
|
||||
}
|
||||
}
|
||||
if len(s.RequestHeader.ClientCAFile) == 0 {
|
||||
klog.Warningf("Cluster doesn't provide requestheader-client-ca-file in configmap/%s in %s, so request-header client certificate authentication won't work.", authenticationConfigMapName, authenticationConfigMapNamespace)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func inClusterClientCA(authConfigMap *v1.ConfigMap) (*ClientCertAuthenticationOptions, error) {
|
||||
clientCA, ok := authConfigMap.Data["client-ca-file"]
|
||||
if !ok {
|
||||
// not having a client-ca is fine, return nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
f, err := ioutil.TempFile("", "client-ca-file")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := ioutil.WriteFile(f.Name(), []byte(clientCA), 0600); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ClientCertAuthenticationOptions{ClientCA: f.Name()}, nil
|
||||
}
|
||||
|
||||
func inClusterRequestHeader(authConfigMap *v1.ConfigMap) (*RequestHeaderAuthenticationOptions, error) {
|
||||
requestHeaderCA, ok := authConfigMap.Data["requestheader-client-ca-file"]
|
||||
if !ok {
|
||||
// not having a requestheader-client-ca is fine, return nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
f, err := ioutil.TempFile("", "requestheader-client-ca-file")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := ioutil.WriteFile(f.Name(), []byte(requestHeaderCA), 0600); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
usernameHeaders, err := deserializeStrings(authConfigMap.Data["requestheader-username-headers"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
groupHeaders, err := deserializeStrings(authConfigMap.Data["requestheader-group-headers"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
extraHeaderPrefixes, err := deserializeStrings(authConfigMap.Data["requestheader-extra-headers-prefix"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
allowedNames, err := deserializeStrings(authConfigMap.Data["requestheader-allowed-names"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &RequestHeaderAuthenticationOptions{
|
||||
UsernameHeaders: usernameHeaders,
|
||||
GroupHeaders: groupHeaders,
|
||||
ExtraHeaderPrefixes: extraHeaderPrefixes,
|
||||
ClientCAFile: f.Name(),
|
||||
AllowedNames: allowedNames,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func deserializeStrings(in string) ([]string, error) {
|
||||
if len(in) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
var ret []string
|
||||
if err := json.Unmarshal([]byte(in), &ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// getClient returns a Kubernetes clientset. If s.RemoteKubeConfigFileOptional is true, nil will be returned
|
||||
// if no kubeconfig is specified by the user and the in-cluster config is not found.
|
||||
func (s *DelegatingAuthenticationOptions) getClient() (kubernetes.Interface, error) {
|
||||
var clientConfig *rest.Config
|
||||
var err error
|
||||
if len(s.RemoteKubeConfigFile) > 0 {
|
||||
loadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: s.RemoteKubeConfigFile}
|
||||
loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
|
||||
|
||||
clientConfig, err = loader.ClientConfig()
|
||||
} else {
|
||||
// without the remote kubeconfig file, try to use the in-cluster config. Most addon API servers will
|
||||
// use this path. If it is optional, ignore errors.
|
||||
clientConfig, err = rest.InClusterConfig()
|
||||
if err != nil && s.RemoteKubeConfigFileOptional {
|
||||
if err != rest.ErrNotInCluster {
|
||||
klog.Warningf("failed to read in-cluster kubeconfig for delegated authentication: %v", err)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get delegated authentication kubeconfig: %v", err)
|
||||
}
|
||||
|
||||
// set high qps/burst limits since this will effectively limit API server responsiveness
|
||||
clientConfig.QPS = 200
|
||||
clientConfig.Burst = 400
|
||||
|
||||
return kubernetes.NewForConfig(clientConfig)
|
||||
}
|
||||
191
vendor/k8s.io/apiserver/pkg/server/options/authorization.go
generated
vendored
Normal file
191
vendor/k8s.io/apiserver/pkg/server/options/authorization.go
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes 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 (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/klog"
|
||||
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizerfactory"
|
||||
"k8s.io/apiserver/pkg/authorization/path"
|
||||
"k8s.io/apiserver/pkg/authorization/union"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
// DelegatingAuthorizationOptions provides an easy way for composing API servers to delegate their authorization to
|
||||
// the root kube API server.
|
||||
// WARNING: never assume that every authenticated incoming request already does authorization.
|
||||
// The aggregator in the kube API server does this today, but this behaviour is not
|
||||
// guaranteed in the future.
|
||||
type DelegatingAuthorizationOptions struct {
|
||||
// RemoteKubeConfigFile is the file to use to connect to a "normal" kube API server which hosts the
|
||||
// SubjectAccessReview.authorization.k8s.io endpoint for checking tokens.
|
||||
RemoteKubeConfigFile string
|
||||
// RemoteKubeConfigFileOptional is specifying whether not specifying the kubeconfig or
|
||||
// a missing in-cluster config will be fatal.
|
||||
RemoteKubeConfigFileOptional bool
|
||||
|
||||
// AllowCacheTTL is the length of time that a successful authorization response will be cached
|
||||
AllowCacheTTL time.Duration
|
||||
|
||||
// DenyCacheTTL is the length of time that an unsuccessful authorization response will be cached.
|
||||
// You generally want more responsive, "deny, try again" flows.
|
||||
DenyCacheTTL time.Duration
|
||||
|
||||
// AlwaysAllowPaths are HTTP paths which are excluded from authorization. They can be plain
|
||||
// paths or end in * in which case prefix-match is applied. A leading / is optional.
|
||||
AlwaysAllowPaths []string
|
||||
|
||||
// AlwaysAllowGroups are groups which are allowed to take any actions. In kube, this is system:masters.
|
||||
AlwaysAllowGroups []string
|
||||
}
|
||||
|
||||
func NewDelegatingAuthorizationOptions() *DelegatingAuthorizationOptions {
|
||||
return &DelegatingAuthorizationOptions{
|
||||
// very low for responsiveness, but high enough to handle storms
|
||||
AllowCacheTTL: 10 * time.Second,
|
||||
DenyCacheTTL: 10 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
// WithAlwaysAllowGroups appends the list of paths to AlwaysAllowGroups
|
||||
func (s *DelegatingAuthorizationOptions) WithAlwaysAllowGroups(groups ...string) *DelegatingAuthorizationOptions {
|
||||
s.AlwaysAllowGroups = append(s.AlwaysAllowGroups, groups...)
|
||||
return s
|
||||
}
|
||||
|
||||
// WithAlwaysAllowPaths appends the list of paths to AlwaysAllowPaths
|
||||
func (s *DelegatingAuthorizationOptions) WithAlwaysAllowPaths(paths ...string) *DelegatingAuthorizationOptions {
|
||||
s.AlwaysAllowPaths = append(s.AlwaysAllowPaths, paths...)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *DelegatingAuthorizationOptions) Validate() []error {
|
||||
allErrors := []error{}
|
||||
return allErrors
|
||||
}
|
||||
|
||||
func (s *DelegatingAuthorizationOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var optionalKubeConfigSentence string
|
||||
if s.RemoteKubeConfigFileOptional {
|
||||
optionalKubeConfigSentence = " This is optional. If empty, all requests not skipped by authorization are forbidden."
|
||||
}
|
||||
fs.StringVar(&s.RemoteKubeConfigFile, "authorization-kubeconfig", s.RemoteKubeConfigFile,
|
||||
"kubeconfig file pointing at the 'core' kubernetes server with enough rights to create "+
|
||||
"subjectaccessreviews.authorization.k8s.io."+optionalKubeConfigSentence)
|
||||
|
||||
fs.DurationVar(&s.AllowCacheTTL, "authorization-webhook-cache-authorized-ttl",
|
||||
s.AllowCacheTTL,
|
||||
"The duration to cache 'authorized' responses from the webhook authorizer.")
|
||||
|
||||
fs.DurationVar(&s.DenyCacheTTL,
|
||||
"authorization-webhook-cache-unauthorized-ttl", s.DenyCacheTTL,
|
||||
"The duration to cache 'unauthorized' responses from the webhook authorizer.")
|
||||
|
||||
fs.StringSliceVar(&s.AlwaysAllowPaths, "authorization-always-allow-paths", s.AlwaysAllowPaths,
|
||||
"A list of HTTP paths to skip during authorization, i.e. these are authorized without "+
|
||||
"contacting the 'core' kubernetes server.")
|
||||
}
|
||||
|
||||
func (s *DelegatingAuthorizationOptions) ApplyTo(c *server.AuthorizationInfo) error {
|
||||
if s == nil {
|
||||
c.Authorizer = authorizerfactory.NewAlwaysAllowAuthorizer()
|
||||
return nil
|
||||
}
|
||||
|
||||
client, err := s.getClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Authorizer, err = s.toAuthorizer(client)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *DelegatingAuthorizationOptions) toAuthorizer(client kubernetes.Interface) (authorizer.Authorizer, error) {
|
||||
var authorizers []authorizer.Authorizer
|
||||
|
||||
if len(s.AlwaysAllowGroups) > 0 {
|
||||
authorizers = append(authorizers, authorizerfactory.NewPrivilegedGroups(s.AlwaysAllowGroups...))
|
||||
}
|
||||
|
||||
if len(s.AlwaysAllowPaths) > 0 {
|
||||
a, err := path.NewAuthorizer(s.AlwaysAllowPaths)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authorizers = append(authorizers, a)
|
||||
}
|
||||
|
||||
if client == nil {
|
||||
klog.Warningf("No authorization-kubeconfig provided, so SubjectAccessReview of authorization tokens won't work.")
|
||||
} else {
|
||||
cfg := authorizerfactory.DelegatingAuthorizerConfig{
|
||||
SubjectAccessReviewClient: client.AuthorizationV1beta1().SubjectAccessReviews(),
|
||||
AllowCacheTTL: s.AllowCacheTTL,
|
||||
DenyCacheTTL: s.DenyCacheTTL,
|
||||
}
|
||||
delegatedAuthorizer, err := cfg.New()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authorizers = append(authorizers, delegatedAuthorizer)
|
||||
}
|
||||
|
||||
return union.New(authorizers...), nil
|
||||
}
|
||||
|
||||
func (s *DelegatingAuthorizationOptions) getClient() (kubernetes.Interface, error) {
|
||||
var clientConfig *rest.Config
|
||||
var err error
|
||||
if len(s.RemoteKubeConfigFile) > 0 {
|
||||
loadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: s.RemoteKubeConfigFile}
|
||||
loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
|
||||
|
||||
clientConfig, err = loader.ClientConfig()
|
||||
} else {
|
||||
// without the remote kubeconfig file, try to use the in-cluster config. Most addon API servers will
|
||||
// use this path. If it is optional, ignore errors.
|
||||
clientConfig, err = rest.InClusterConfig()
|
||||
if err != nil && s.RemoteKubeConfigFileOptional {
|
||||
if err != rest.ErrNotInCluster {
|
||||
klog.Warningf("failed to read in-cluster kubeconfig for delegated authorization: %v", err)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get delegated authorization kubeconfig: %v", err)
|
||||
}
|
||||
|
||||
// set high qps/burst limits since this will effectively limit API server responsiveness
|
||||
clientConfig.QPS = 200
|
||||
clientConfig.Burst = 400
|
||||
|
||||
return kubernetes.NewForConfig(clientConfig)
|
||||
}
|
||||
84
vendor/k8s.io/apiserver/pkg/server/options/coreapi.go
generated
vendored
Normal file
84
vendor/k8s.io/apiserver/pkg/server/options/coreapi.go
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes 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 (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
clientgoinformers "k8s.io/client-go/informers"
|
||||
clientgoclientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
// CoreAPIOptions contains options to configure the connection to a core API Kubernetes apiserver.
|
||||
type CoreAPIOptions struct {
|
||||
// CoreAPIKubeconfigPath is a filename for a kubeconfig file to contact the core API server with.
|
||||
// If it is not set, the in cluster config is used.
|
||||
CoreAPIKubeconfigPath string
|
||||
}
|
||||
|
||||
func NewCoreAPIOptions() *CoreAPIOptions {
|
||||
return &CoreAPIOptions{}
|
||||
}
|
||||
|
||||
func (o *CoreAPIOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
if o == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fs.StringVar(&o.CoreAPIKubeconfigPath, "kubeconfig", o.CoreAPIKubeconfigPath,
|
||||
"kubeconfig file pointing at the 'core' kubernetes server.")
|
||||
}
|
||||
|
||||
func (o *CoreAPIOptions) ApplyTo(config *server.RecommendedConfig) error {
|
||||
if o == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// create shared informer for Kubernetes APIs
|
||||
var kubeconfig *rest.Config
|
||||
var err error
|
||||
if len(o.CoreAPIKubeconfigPath) > 0 {
|
||||
loadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: o.CoreAPIKubeconfigPath}
|
||||
loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
|
||||
kubeconfig, err = loader.ClientConfig()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load kubeconfig at %q: %v", o.CoreAPIKubeconfigPath, err)
|
||||
}
|
||||
} else {
|
||||
kubeconfig, err = rest.InClusterConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
clientgoExternalClient, err := clientgoclientset.NewForConfig(kubeconfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create Kubernetes clientset: %v", err)
|
||||
}
|
||||
config.ClientConfig = kubeconfig
|
||||
config.SharedInformerFactory = clientgoinformers.NewSharedInformerFactory(clientgoExternalClient, 10*time.Minute)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *CoreAPIOptions) Validate() []error {
|
||||
return nil
|
||||
}
|
||||
166
vendor/k8s.io/apiserver/pkg/server/options/deprecated_insecure_serving.go
generated
vendored
Normal file
166
vendor/k8s.io/apiserver/pkg/server/options/deprecated_insecure_serving.go
generated
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes 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 (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
// DeprecatedInsecureServingOptions are for creating an unauthenticated, unauthorized, insecure port.
|
||||
// No one should be using these anymore.
|
||||
// DEPRECATED: all insecure serving options are removed in a future version
|
||||
type DeprecatedInsecureServingOptions struct {
|
||||
BindAddress net.IP
|
||||
BindPort int
|
||||
// BindNetwork is the type of network to bind to - defaults to "tcp", accepts "tcp",
|
||||
// "tcp4", and "tcp6".
|
||||
BindNetwork string
|
||||
|
||||
// Listener is the secure server network listener.
|
||||
// either Listener or BindAddress/BindPort/BindNetwork is set,
|
||||
// if Listener is set, use it and omit BindAddress/BindPort/BindNetwork.
|
||||
Listener net.Listener
|
||||
|
||||
// ListenFunc can be overridden to create a custom listener, e.g. for mocking in tests.
|
||||
// It defaults to options.CreateListener.
|
||||
ListenFunc func(network, addr string) (net.Listener, int, error)
|
||||
}
|
||||
|
||||
// Validate ensures that the insecure port values within the range of the port.
|
||||
func (s *DeprecatedInsecureServingOptions) Validate() []error {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errors := []error{}
|
||||
|
||||
if s.BindPort < 0 || s.BindPort > 65335 {
|
||||
errors = append(errors, fmt.Errorf("insecure port %v must be between 0 and 65335, inclusive. 0 for turning off insecure (HTTP) port", s.BindPort))
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
// AddFlags adds flags related to insecure serving to the specified FlagSet.
|
||||
func (s *DeprecatedInsecureServingOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fs.IPVar(&s.BindAddress, "insecure-bind-address", s.BindAddress, ""+
|
||||
"The IP address on which to serve the --insecure-port (set to 0.0.0.0 for all IPv4 interfaces and :: for all IPv6 interfaces).")
|
||||
fs.MarkDeprecated("insecure-bind-address", "This flag will be removed in a future version.")
|
||||
fs.Lookup("insecure-bind-address").Hidden = false
|
||||
|
||||
fs.IntVar(&s.BindPort, "insecure-port", s.BindPort, ""+
|
||||
"The port on which to serve unsecured, unauthenticated access.")
|
||||
fs.MarkDeprecated("insecure-port", "This flag will be removed in a future version.")
|
||||
fs.Lookup("insecure-port").Hidden = false
|
||||
}
|
||||
|
||||
// AddUnqualifiedFlags adds flags related to insecure serving without the --insecure prefix to the specified FlagSet.
|
||||
func (s *DeprecatedInsecureServingOptions) AddUnqualifiedFlags(fs *pflag.FlagSet) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fs.IPVar(&s.BindAddress, "address", s.BindAddress,
|
||||
"The IP address on which to serve the insecure --port (set to 0.0.0.0 for all IPv4 interfaces and :: for all IPv6 interfaces).")
|
||||
fs.MarkDeprecated("address", "see --bind-address instead.")
|
||||
fs.Lookup("address").Hidden = false
|
||||
|
||||
fs.IntVar(&s.BindPort, "port", s.BindPort, "The port on which to serve unsecured, unauthenticated access. Set to 0 to disable.")
|
||||
fs.MarkDeprecated("port", "see --secure-port instead.")
|
||||
fs.Lookup("port").Hidden = false
|
||||
}
|
||||
|
||||
// ApplyTo adds DeprecatedInsecureServingOptions to the insecureserverinfo amd kube-controller manager configuration.
|
||||
// Note: the double pointer allows to set the *DeprecatedInsecureServingInfo to nil without referencing the struct hosting this pointer.
|
||||
func (s *DeprecatedInsecureServingOptions) ApplyTo(c **server.DeprecatedInsecureServingInfo) error {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
if s.BindPort <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if s.Listener == nil {
|
||||
var err error
|
||||
listen := CreateListener
|
||||
if s.ListenFunc != nil {
|
||||
listen = s.ListenFunc
|
||||
}
|
||||
addr := net.JoinHostPort(s.BindAddress.String(), fmt.Sprintf("%d", s.BindPort))
|
||||
s.Listener, s.BindPort, err = listen(s.BindNetwork, addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create listener: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
*c = &server.DeprecatedInsecureServingInfo{
|
||||
Listener: s.Listener,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithLoopback adds loopback functionality to the serving options.
|
||||
func (o *DeprecatedInsecureServingOptions) WithLoopback() *DeprecatedInsecureServingOptionsWithLoopback {
|
||||
return &DeprecatedInsecureServingOptionsWithLoopback{o}
|
||||
}
|
||||
|
||||
// DeprecatedInsecureServingOptionsWithLoopback adds loopback functionality to the DeprecatedInsecureServingOptions.
|
||||
// DEPRECATED: all insecure serving options are removed in a future version
|
||||
type DeprecatedInsecureServingOptionsWithLoopback struct {
|
||||
*DeprecatedInsecureServingOptions
|
||||
}
|
||||
|
||||
// ApplyTo fills up serving information in the server configuration.
|
||||
func (s *DeprecatedInsecureServingOptionsWithLoopback) ApplyTo(insecureServingInfo **server.DeprecatedInsecureServingInfo, loopbackClientConfig **rest.Config) error {
|
||||
if s == nil || s.DeprecatedInsecureServingOptions == nil || insecureServingInfo == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := s.DeprecatedInsecureServingOptions.ApplyTo(insecureServingInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if *insecureServingInfo == nil || loopbackClientConfig == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
secureLoopbackClientConfig, err := (*insecureServingInfo).NewLoopbackClientConfig()
|
||||
switch {
|
||||
// if we failed and there's no fallback loopback client config, we need to fail
|
||||
case err != nil && *loopbackClientConfig == nil:
|
||||
return err
|
||||
|
||||
// if we failed, but we already have a fallback loopback client config (usually insecure), allow it
|
||||
case err != nil && *loopbackClientConfig != nil:
|
||||
|
||||
default:
|
||||
*loopbackClientConfig = secureLoopbackClientConfig
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
21
vendor/k8s.io/apiserver/pkg/server/options/doc.go
generated
vendored
Normal file
21
vendor/k8s.io/apiserver/pkg/server/options/doc.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes 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 is the public flags and options used by a generic api
|
||||
// server. It takes a minimal set of dependencies and does not reference
|
||||
// implementations, in order to ensure it may be reused by multiple components
|
||||
// (such as CLI commands that wish to generate or validate config).
|
||||
package options // import "k8s.io/apiserver/pkg/server/options"
|
||||
304
vendor/k8s.io/apiserver/pkg/server/options/etcd.go
generated
vendored
Normal file
304
vendor/k8s.io/apiserver/pkg/server/options/etcd.go
generated
vendored
Normal file
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes 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 (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/apiserver/pkg/server/healthz"
|
||||
serverstorage "k8s.io/apiserver/pkg/server/storage"
|
||||
"k8s.io/apiserver/pkg/storage/storagebackend"
|
||||
storagefactory "k8s.io/apiserver/pkg/storage/storagebackend/factory"
|
||||
)
|
||||
|
||||
type EtcdOptions struct {
|
||||
// The value of Paging on StorageConfig will be overridden by the
|
||||
// calculated feature gate value.
|
||||
StorageConfig storagebackend.Config
|
||||
EncryptionProviderConfigFilepath string
|
||||
|
||||
EtcdServersOverrides []string
|
||||
|
||||
// To enable protobuf as storage format, it is enough
|
||||
// to set it to "application/vnd.kubernetes.protobuf".
|
||||
DefaultStorageMediaType string
|
||||
DeleteCollectionWorkers int
|
||||
EnableGarbageCollection bool
|
||||
|
||||
// Set EnableWatchCache to false to disable all watch caches
|
||||
EnableWatchCache bool
|
||||
// Set DefaultWatchCacheSize to zero to disable watch caches for those resources that have no explicit cache size set
|
||||
DefaultWatchCacheSize int
|
||||
// WatchCacheSizes represents override to a given resource
|
||||
WatchCacheSizes []string
|
||||
}
|
||||
|
||||
var storageTypes = sets.NewString(
|
||||
storagebackend.StorageTypeETCD3,
|
||||
)
|
||||
|
||||
func NewEtcdOptions(backendConfig *storagebackend.Config) *EtcdOptions {
|
||||
options := &EtcdOptions{
|
||||
StorageConfig: *backendConfig,
|
||||
DefaultStorageMediaType: "application/json",
|
||||
DeleteCollectionWorkers: 1,
|
||||
EnableGarbageCollection: true,
|
||||
EnableWatchCache: true,
|
||||
DefaultWatchCacheSize: 100,
|
||||
}
|
||||
options.StorageConfig.CountMetricPollPeriod = time.Minute
|
||||
return options
|
||||
}
|
||||
|
||||
func (s *EtcdOptions) Validate() []error {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
allErrors := []error{}
|
||||
if len(s.StorageConfig.ServerList) == 0 {
|
||||
allErrors = append(allErrors, fmt.Errorf("--etcd-servers must be specified"))
|
||||
}
|
||||
|
||||
if s.StorageConfig.Type != storagebackend.StorageTypeUnset && !storageTypes.Has(s.StorageConfig.Type) {
|
||||
allErrors = append(allErrors, fmt.Errorf("--storage-backend invalid, allowed values: %s. If not specified, it will default to 'etcd3'", strings.Join(storageTypes.List(), ", ")))
|
||||
}
|
||||
|
||||
for _, override := range s.EtcdServersOverrides {
|
||||
tokens := strings.Split(override, "#")
|
||||
if len(tokens) != 2 {
|
||||
allErrors = append(allErrors, fmt.Errorf("--etcd-servers-overrides invalid, must be of format: group/resource#servers, where servers are URLs, semicolon separated"))
|
||||
continue
|
||||
}
|
||||
|
||||
apiresource := strings.Split(tokens[0], "/")
|
||||
if len(apiresource) != 2 {
|
||||
allErrors = append(allErrors, fmt.Errorf("--etcd-servers-overrides invalid, must be of format: group/resource#servers, where servers are URLs, semicolon separated"))
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return allErrors
|
||||
}
|
||||
|
||||
// AddEtcdFlags adds flags related to etcd storage for a specific APIServer to the specified FlagSet
|
||||
func (s *EtcdOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fs.StringSliceVar(&s.EtcdServersOverrides, "etcd-servers-overrides", s.EtcdServersOverrides, ""+
|
||||
"Per-resource etcd servers overrides, comma separated. The individual override "+
|
||||
"format: group/resource#servers, where servers are URLs, semicolon separated.")
|
||||
|
||||
fs.StringVar(&s.DefaultStorageMediaType, "storage-media-type", s.DefaultStorageMediaType, ""+
|
||||
"The media type to use to store objects in storage. "+
|
||||
"Some resources or storage backends may only support a specific media type and will ignore this setting.")
|
||||
fs.IntVar(&s.DeleteCollectionWorkers, "delete-collection-workers", s.DeleteCollectionWorkers,
|
||||
"Number of workers spawned for DeleteCollection call. These are used to speed up namespace cleanup.")
|
||||
|
||||
fs.BoolVar(&s.EnableGarbageCollection, "enable-garbage-collector", s.EnableGarbageCollection, ""+
|
||||
"Enables the generic garbage collector. MUST be synced with the corresponding flag "+
|
||||
"of the kube-controller-manager.")
|
||||
|
||||
fs.BoolVar(&s.EnableWatchCache, "watch-cache", s.EnableWatchCache,
|
||||
"Enable watch caching in the apiserver")
|
||||
|
||||
fs.IntVar(&s.DefaultWatchCacheSize, "default-watch-cache-size", s.DefaultWatchCacheSize,
|
||||
"Default watch cache size. If zero, watch cache will be disabled for resources that do not have a default watch size set.")
|
||||
|
||||
fs.StringSliceVar(&s.WatchCacheSizes, "watch-cache-sizes", s.WatchCacheSizes, ""+
|
||||
"List of watch cache sizes for every resource (pods, nodes, etc.), comma separated. "+
|
||||
"The individual override format: resource[.group]#size, where resource is lowercase plural (no version), "+
|
||||
"group is optional, and size is a number. It takes effect when watch-cache is enabled. "+
|
||||
"Some resources (replicationcontrollers, endpoints, nodes, pods, services, apiservices.apiregistration.k8s.io) "+
|
||||
"have system defaults set by heuristics, others default to default-watch-cache-size")
|
||||
|
||||
fs.StringVar(&s.StorageConfig.Type, "storage-backend", s.StorageConfig.Type,
|
||||
"The storage backend for persistence. Options: 'etcd3' (default).")
|
||||
|
||||
dummyCacheSize := 0
|
||||
fs.IntVar(&dummyCacheSize, "deserialization-cache-size", 0, "Number of deserialized json objects to cache in memory.")
|
||||
fs.MarkDeprecated("deserialization-cache-size", "the deserialization cache was dropped in 1.13 with support for etcd2")
|
||||
|
||||
fs.StringSliceVar(&s.StorageConfig.ServerList, "etcd-servers", s.StorageConfig.ServerList,
|
||||
"List of etcd servers to connect with (scheme://ip:port), comma separated.")
|
||||
|
||||
fs.StringVar(&s.StorageConfig.Prefix, "etcd-prefix", s.StorageConfig.Prefix,
|
||||
"The prefix to prepend to all resource paths in etcd.")
|
||||
|
||||
fs.StringVar(&s.StorageConfig.KeyFile, "etcd-keyfile", s.StorageConfig.KeyFile,
|
||||
"SSL key file used to secure etcd communication.")
|
||||
|
||||
fs.StringVar(&s.StorageConfig.CertFile, "etcd-certfile", s.StorageConfig.CertFile,
|
||||
"SSL certification file used to secure etcd communication.")
|
||||
|
||||
fs.StringVar(&s.StorageConfig.CAFile, "etcd-cafile", s.StorageConfig.CAFile,
|
||||
"SSL Certificate Authority file used to secure etcd communication.")
|
||||
|
||||
fs.StringVar(&s.EncryptionProviderConfigFilepath, "experimental-encryption-provider-config", s.EncryptionProviderConfigFilepath,
|
||||
"The file containing configuration for encryption providers to be used for storing secrets in etcd")
|
||||
fs.MarkDeprecated("experimental-encryption-provider-config", "use --encryption-provider-config.")
|
||||
|
||||
fs.StringVar(&s.EncryptionProviderConfigFilepath, "encryption-provider-config", s.EncryptionProviderConfigFilepath,
|
||||
"The file containing configuration for encryption providers to be used for storing secrets in etcd")
|
||||
|
||||
fs.DurationVar(&s.StorageConfig.CompactionInterval, "etcd-compaction-interval", s.StorageConfig.CompactionInterval,
|
||||
"The interval of compaction requests. If 0, the compaction request from apiserver is disabled.")
|
||||
|
||||
fs.DurationVar(&s.StorageConfig.CountMetricPollPeriod, "etcd-count-metric-poll-period", s.StorageConfig.CountMetricPollPeriod, ""+
|
||||
"Frequency of polling etcd for number of resources per type. 0 disables the metric collection.")
|
||||
}
|
||||
|
||||
func (s *EtcdOptions) ApplyTo(c *server.Config) error {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
if err := s.addEtcdHealthEndpoint(c); err != nil {
|
||||
return err
|
||||
}
|
||||
c.RESTOptionsGetter = &SimpleRestOptionsFactory{Options: *s}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *EtcdOptions) ApplyWithStorageFactoryTo(factory serverstorage.StorageFactory, c *server.Config) error {
|
||||
if err := s.addEtcdHealthEndpoint(c); err != nil {
|
||||
return err
|
||||
}
|
||||
c.RESTOptionsGetter = &StorageFactoryRestOptionsFactory{Options: *s, StorageFactory: factory}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *EtcdOptions) addEtcdHealthEndpoint(c *server.Config) error {
|
||||
healthCheck, err := storagefactory.CreateHealthCheck(s.StorageConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.HealthzChecks = append(c.HealthzChecks, healthz.NamedCheck("etcd", func(r *http.Request) error {
|
||||
return healthCheck()
|
||||
}))
|
||||
return nil
|
||||
}
|
||||
|
||||
type SimpleRestOptionsFactory struct {
|
||||
Options EtcdOptions
|
||||
}
|
||||
|
||||
func (f *SimpleRestOptionsFactory) GetRESTOptions(resource schema.GroupResource) (generic.RESTOptions, error) {
|
||||
ret := generic.RESTOptions{
|
||||
StorageConfig: &f.Options.StorageConfig,
|
||||
Decorator: generic.UndecoratedStorage,
|
||||
EnableGarbageCollection: f.Options.EnableGarbageCollection,
|
||||
DeleteCollectionWorkers: f.Options.DeleteCollectionWorkers,
|
||||
ResourcePrefix: resource.Group + "/" + resource.Resource,
|
||||
CountMetricPollPeriod: f.Options.StorageConfig.CountMetricPollPeriod,
|
||||
}
|
||||
if f.Options.EnableWatchCache {
|
||||
sizes, err := ParseWatchCacheSizes(f.Options.WatchCacheSizes)
|
||||
if err != nil {
|
||||
return generic.RESTOptions{}, err
|
||||
}
|
||||
cacheSize, ok := sizes[resource]
|
||||
if !ok {
|
||||
cacheSize = f.Options.DefaultWatchCacheSize
|
||||
}
|
||||
ret.Decorator = genericregistry.StorageWithCacher(cacheSize)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
type StorageFactoryRestOptionsFactory struct {
|
||||
Options EtcdOptions
|
||||
StorageFactory serverstorage.StorageFactory
|
||||
}
|
||||
|
||||
func (f *StorageFactoryRestOptionsFactory) GetRESTOptions(resource schema.GroupResource) (generic.RESTOptions, error) {
|
||||
storageConfig, err := f.StorageFactory.NewConfig(resource)
|
||||
if err != nil {
|
||||
return generic.RESTOptions{}, fmt.Errorf("unable to find storage destination for %v, due to %v", resource, err.Error())
|
||||
}
|
||||
|
||||
ret := generic.RESTOptions{
|
||||
StorageConfig: storageConfig,
|
||||
Decorator: generic.UndecoratedStorage,
|
||||
DeleteCollectionWorkers: f.Options.DeleteCollectionWorkers,
|
||||
EnableGarbageCollection: f.Options.EnableGarbageCollection,
|
||||
ResourcePrefix: f.StorageFactory.ResourcePrefix(resource),
|
||||
CountMetricPollPeriod: f.Options.StorageConfig.CountMetricPollPeriod,
|
||||
}
|
||||
if f.Options.EnableWatchCache {
|
||||
sizes, err := ParseWatchCacheSizes(f.Options.WatchCacheSizes)
|
||||
if err != nil {
|
||||
return generic.RESTOptions{}, err
|
||||
}
|
||||
cacheSize, ok := sizes[resource]
|
||||
if !ok {
|
||||
cacheSize = f.Options.DefaultWatchCacheSize
|
||||
}
|
||||
ret.Decorator = genericregistry.StorageWithCacher(cacheSize)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// ParseWatchCacheSizes turns a list of cache size values into a map of group resources
|
||||
// to requested sizes.
|
||||
func ParseWatchCacheSizes(cacheSizes []string) (map[schema.GroupResource]int, error) {
|
||||
watchCacheSizes := make(map[schema.GroupResource]int)
|
||||
for _, c := range cacheSizes {
|
||||
tokens := strings.Split(c, "#")
|
||||
if len(tokens) != 2 {
|
||||
return nil, fmt.Errorf("invalid value of watch cache size: %s", c)
|
||||
}
|
||||
|
||||
size, err := strconv.Atoi(tokens[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid size of watch cache size: %s", c)
|
||||
}
|
||||
if size < 0 {
|
||||
return nil, fmt.Errorf("watch cache size cannot be negative: %s", c)
|
||||
}
|
||||
|
||||
watchCacheSizes[schema.ParseGroupResource(tokens[0])] = size
|
||||
}
|
||||
return watchCacheSizes, nil
|
||||
}
|
||||
|
||||
// WriteWatchCacheSizes turns a map of cache size values into a list of string specifications.
|
||||
func WriteWatchCacheSizes(watchCacheSizes map[schema.GroupResource]int) ([]string, error) {
|
||||
var cacheSizes []string
|
||||
|
||||
for resource, size := range watchCacheSizes {
|
||||
if size < 0 {
|
||||
return nil, fmt.Errorf("watch cache size cannot be negative for resource %s", resource)
|
||||
}
|
||||
cacheSizes = append(cacheSizes, fmt.Sprintf("%s#%d", resource.String(), size))
|
||||
}
|
||||
return cacheSizes, nil
|
||||
}
|
||||
56
vendor/k8s.io/apiserver/pkg/server/options/events.go
generated
vendored
Normal file
56
vendor/k8s.io/apiserver/pkg/server/options/events.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes 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 (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// ProcessInfo holds the apiserver process information used to send events
|
||||
type ProcessInfo struct {
|
||||
// Name of the api process to identify events
|
||||
Name string
|
||||
|
||||
// Namespace of the api process to send events
|
||||
Namespace string
|
||||
}
|
||||
|
||||
// NewProcessInfo returns a new process info with the hostname concatenated to the name given
|
||||
func NewProcessInfo(name, namespace string) *ProcessInfo {
|
||||
// try to concat the hostname if available
|
||||
host, _ := os.Hostname()
|
||||
if host != "" {
|
||||
name = fmt.Sprintf("%s-%s", name, host)
|
||||
}
|
||||
return &ProcessInfo{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
}
|
||||
}
|
||||
|
||||
// validateProcessInfo checks for a complete process info
|
||||
func validateProcessInfo(p *ProcessInfo) error {
|
||||
if p == nil {
|
||||
return fmt.Errorf("ProcessInfo must be set")
|
||||
} else if p.Name == "" {
|
||||
return fmt.Errorf("ProcessInfo name must be set")
|
||||
} else if p.Namespace == "" {
|
||||
return fmt.Errorf("ProcessInfo namespace must be set")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
74
vendor/k8s.io/apiserver/pkg/server/options/feature.go
generated
vendored
Normal file
74
vendor/k8s.io/apiserver/pkg/server/options/feature.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes 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 (
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
)
|
||||
|
||||
type FeatureOptions struct {
|
||||
EnableProfiling bool
|
||||
EnableContentionProfiling bool
|
||||
EnableSwaggerUI bool
|
||||
}
|
||||
|
||||
func NewFeatureOptions() *FeatureOptions {
|
||||
defaults := server.NewConfig(serializer.CodecFactory{})
|
||||
|
||||
return &FeatureOptions{
|
||||
EnableProfiling: defaults.EnableProfiling,
|
||||
EnableContentionProfiling: defaults.EnableContentionProfiling,
|
||||
EnableSwaggerUI: defaults.EnableSwaggerUI,
|
||||
}
|
||||
}
|
||||
|
||||
func (o *FeatureOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
if o == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fs.BoolVar(&o.EnableProfiling, "profiling", o.EnableProfiling,
|
||||
"Enable profiling via web interface host:port/debug/pprof/")
|
||||
fs.BoolVar(&o.EnableContentionProfiling, "contention-profiling", o.EnableContentionProfiling,
|
||||
"Enable lock contention profiling, if profiling is enabled")
|
||||
fs.BoolVar(&o.EnableSwaggerUI, "enable-swagger-ui", o.EnableSwaggerUI,
|
||||
"Enables swagger ui on the apiserver at /swagger-ui")
|
||||
}
|
||||
|
||||
func (o *FeatureOptions) ApplyTo(c *server.Config) error {
|
||||
if o == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.EnableProfiling = o.EnableProfiling
|
||||
c.EnableContentionProfiling = o.EnableContentionProfiling
|
||||
c.EnableSwaggerUI = o.EnableSwaggerUI
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *FeatureOptions) Validate() []error {
|
||||
if o == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errs := []error{}
|
||||
return errs
|
||||
}
|
||||
130
vendor/k8s.io/apiserver/pkg/server/options/recommended.go
generated
vendored
Normal file
130
vendor/k8s.io/apiserver/pkg/server/options/recommended.go
generated
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes 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 (
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/apiserver/pkg/storage/storagebackend"
|
||||
)
|
||||
|
||||
// RecommendedOptions contains the recommended options for running an API server.
|
||||
// If you add something to this list, it should be in a logical grouping.
|
||||
// Each of them can be nil to leave the feature unconfigured on ApplyTo.
|
||||
type RecommendedOptions struct {
|
||||
Etcd *EtcdOptions
|
||||
SecureServing *SecureServingOptionsWithLoopback
|
||||
Authentication *DelegatingAuthenticationOptions
|
||||
Authorization *DelegatingAuthorizationOptions
|
||||
Audit *AuditOptions
|
||||
Features *FeatureOptions
|
||||
CoreAPI *CoreAPIOptions
|
||||
|
||||
// ExtraAdmissionInitializers is called once after all ApplyTo from the options above, to pass the returned
|
||||
// admission plugin initializers to Admission.ApplyTo.
|
||||
ExtraAdmissionInitializers func(c *server.RecommendedConfig) ([]admission.PluginInitializer, error)
|
||||
Admission *AdmissionOptions
|
||||
// ProcessInfo is used to identify events created by the server.
|
||||
ProcessInfo *ProcessInfo
|
||||
Webhook *WebhookOptions
|
||||
}
|
||||
|
||||
func NewRecommendedOptions(prefix string, codec runtime.Codec, processInfo *ProcessInfo) *RecommendedOptions {
|
||||
sso := NewSecureServingOptions()
|
||||
|
||||
// We are composing recommended options for an aggregated api-server,
|
||||
// whose client is typically a proxy multiplexing many operations ---
|
||||
// notably including long-running ones --- into one HTTP/2 connection
|
||||
// into this server. So allow many concurrent operations.
|
||||
sso.HTTP2MaxStreamsPerConnection = 1000
|
||||
|
||||
return &RecommendedOptions{
|
||||
Etcd: NewEtcdOptions(storagebackend.NewDefaultConfig(prefix, codec)),
|
||||
SecureServing: sso.WithLoopback(),
|
||||
Authentication: NewDelegatingAuthenticationOptions(),
|
||||
Authorization: NewDelegatingAuthorizationOptions(),
|
||||
Audit: NewAuditOptions(),
|
||||
Features: NewFeatureOptions(),
|
||||
CoreAPI: NewCoreAPIOptions(),
|
||||
ExtraAdmissionInitializers: func(c *server.RecommendedConfig) ([]admission.PluginInitializer, error) { return nil, nil },
|
||||
Admission: NewAdmissionOptions(),
|
||||
ProcessInfo: processInfo,
|
||||
Webhook: NewWebhookOptions(),
|
||||
}
|
||||
}
|
||||
|
||||
func (o *RecommendedOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
o.Etcd.AddFlags(fs)
|
||||
o.SecureServing.AddFlags(fs)
|
||||
o.Authentication.AddFlags(fs)
|
||||
o.Authorization.AddFlags(fs)
|
||||
o.Audit.AddFlags(fs)
|
||||
o.Features.AddFlags(fs)
|
||||
o.CoreAPI.AddFlags(fs)
|
||||
o.Admission.AddFlags(fs)
|
||||
}
|
||||
|
||||
// ApplyTo adds RecommendedOptions to the server configuration.
|
||||
// scheme is the scheme of the apiserver types that are sent to the admission chain.
|
||||
// pluginInitializers can be empty, it is only need for additional initializers.
|
||||
func (o *RecommendedOptions) ApplyTo(config *server.RecommendedConfig, scheme *runtime.Scheme) error {
|
||||
if err := o.Etcd.ApplyTo(&config.Config); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.SecureServing.ApplyTo(&config.Config.SecureServing, &config.Config.LoopbackClientConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.Authentication.ApplyTo(&config.Config.Authentication, config.SecureServing, config.OpenAPIConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.Authorization.ApplyTo(&config.Config.Authorization); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.Audit.ApplyTo(&config.Config, config.ClientConfig, config.SharedInformerFactory, o.ProcessInfo, o.Webhook); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.Features.ApplyTo(&config.Config); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.CoreAPI.ApplyTo(config); err != nil {
|
||||
return err
|
||||
}
|
||||
if initializers, err := o.ExtraAdmissionInitializers(config); err != nil {
|
||||
return err
|
||||
} else if err := o.Admission.ApplyTo(&config.Config, config.SharedInformerFactory, config.ClientConfig, scheme, initializers...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *RecommendedOptions) Validate() []error {
|
||||
errors := []error{}
|
||||
errors = append(errors, o.Etcd.Validate()...)
|
||||
errors = append(errors, o.SecureServing.Validate()...)
|
||||
errors = append(errors, o.Authentication.Validate()...)
|
||||
errors = append(errors, o.Authorization.Validate()...)
|
||||
errors = append(errors, o.Audit.Validate()...)
|
||||
errors = append(errors, o.Features.Validate()...)
|
||||
errors = append(errors, o.CoreAPI.Validate()...)
|
||||
errors = append(errors, o.Admission.Validate()...)
|
||||
|
||||
return errors
|
||||
}
|
||||
178
vendor/k8s.io/apiserver/pkg/server/options/server_run_options.go
generated
vendored
Normal file
178
vendor/k8s.io/apiserver/pkg/server/options/server_run_options.go
generated
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes 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 (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
|
||||
// add the generic feature gates
|
||||
_ "k8s.io/apiserver/pkg/features"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
// ServerRunOptions contains the options while running a generic api server.
|
||||
type ServerRunOptions struct {
|
||||
AdvertiseAddress net.IP
|
||||
|
||||
CorsAllowedOriginList []string
|
||||
ExternalHost string
|
||||
MaxRequestsInFlight int
|
||||
MaxMutatingRequestsInFlight int
|
||||
RequestTimeout time.Duration
|
||||
MinRequestTimeout int
|
||||
// We intentionally did not add a flag for this option. Users of the
|
||||
// apiserver library can wire it to a flag.
|
||||
JSONPatchMaxCopyBytes int64
|
||||
// The limit on the request body size that would be accepted and
|
||||
// decoded in a write request. 0 means no limit.
|
||||
// We intentionally did not add a flag for this option. Users of the
|
||||
// apiserver library can wire it to a flag.
|
||||
MaxRequestBodyBytes int64
|
||||
TargetRAMMB int
|
||||
}
|
||||
|
||||
func NewServerRunOptions() *ServerRunOptions {
|
||||
defaults := server.NewConfig(serializer.CodecFactory{})
|
||||
return &ServerRunOptions{
|
||||
MaxRequestsInFlight: defaults.MaxRequestsInFlight,
|
||||
MaxMutatingRequestsInFlight: defaults.MaxMutatingRequestsInFlight,
|
||||
RequestTimeout: defaults.RequestTimeout,
|
||||
MinRequestTimeout: defaults.MinRequestTimeout,
|
||||
JSONPatchMaxCopyBytes: defaults.JSONPatchMaxCopyBytes,
|
||||
MaxRequestBodyBytes: defaults.MaxRequestBodyBytes,
|
||||
}
|
||||
}
|
||||
|
||||
// ApplyOptions applies the run options to the method receiver and returns self
|
||||
func (s *ServerRunOptions) ApplyTo(c *server.Config) error {
|
||||
c.CorsAllowedOriginList = s.CorsAllowedOriginList
|
||||
c.ExternalAddress = s.ExternalHost
|
||||
c.MaxRequestsInFlight = s.MaxRequestsInFlight
|
||||
c.MaxMutatingRequestsInFlight = s.MaxMutatingRequestsInFlight
|
||||
c.RequestTimeout = s.RequestTimeout
|
||||
c.MinRequestTimeout = s.MinRequestTimeout
|
||||
c.JSONPatchMaxCopyBytes = s.JSONPatchMaxCopyBytes
|
||||
c.MaxRequestBodyBytes = s.MaxRequestBodyBytes
|
||||
c.PublicAddress = s.AdvertiseAddress
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DefaultAdvertiseAddress sets the field AdvertiseAddress if unset. The field will be set based on the SecureServingOptions.
|
||||
func (s *ServerRunOptions) DefaultAdvertiseAddress(secure *SecureServingOptions) error {
|
||||
if secure == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if s.AdvertiseAddress == nil || s.AdvertiseAddress.IsUnspecified() {
|
||||
hostIP, err := secure.DefaultExternalAddress()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to find suitable network address.error='%v'. "+
|
||||
"Try to set the AdvertiseAddress directly or provide a valid BindAddress to fix this.", err)
|
||||
}
|
||||
s.AdvertiseAddress = hostIP
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate checks validation of ServerRunOptions
|
||||
func (s *ServerRunOptions) Validate() []error {
|
||||
errors := []error{}
|
||||
if s.TargetRAMMB < 0 {
|
||||
errors = append(errors, fmt.Errorf("--target-ram-mb can not be negative value"))
|
||||
}
|
||||
if s.MaxRequestsInFlight < 0 {
|
||||
errors = append(errors, fmt.Errorf("--max-requests-inflight can not be negative value"))
|
||||
}
|
||||
if s.MaxMutatingRequestsInFlight < 0 {
|
||||
errors = append(errors, fmt.Errorf("--max-mutating-requests-inflight can not be negative value"))
|
||||
}
|
||||
|
||||
if s.RequestTimeout.Nanoseconds() < 0 {
|
||||
errors = append(errors, fmt.Errorf("--request-timeout can not be negative value"))
|
||||
}
|
||||
|
||||
if s.MinRequestTimeout < 0 {
|
||||
errors = append(errors, fmt.Errorf("--min-request-timeout can not be negative value"))
|
||||
}
|
||||
|
||||
if s.JSONPatchMaxCopyBytes < 0 {
|
||||
errors = append(errors, fmt.Errorf("--json-patch-max-copy-bytes can not be negative value"))
|
||||
}
|
||||
|
||||
if s.MaxRequestBodyBytes < 0 {
|
||||
errors = append(errors, fmt.Errorf("--max-resource-write-bytes can not be negative value"))
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
// AddUniversalFlags adds flags for a specific APIServer to the specified FlagSet
|
||||
func (s *ServerRunOptions) AddUniversalFlags(fs *pflag.FlagSet) {
|
||||
// Note: the weird ""+ in below lines seems to be the only way to get gofmt to
|
||||
// arrange these text blocks sensibly. Grrr.
|
||||
|
||||
fs.IPVar(&s.AdvertiseAddress, "advertise-address", s.AdvertiseAddress, ""+
|
||||
"The IP address on which to advertise the apiserver to members of the cluster. This "+
|
||||
"address must be reachable by the rest of the cluster. If blank, the --bind-address "+
|
||||
"will be used. If --bind-address is unspecified, the host's default interface will "+
|
||||
"be used.")
|
||||
|
||||
fs.StringSliceVar(&s.CorsAllowedOriginList, "cors-allowed-origins", s.CorsAllowedOriginList, ""+
|
||||
"List of allowed origins for CORS, comma separated. An allowed origin can be a regular "+
|
||||
"expression to support subdomain matching. If this list is empty CORS will not be enabled.")
|
||||
|
||||
fs.IntVar(&s.TargetRAMMB, "target-ram-mb", s.TargetRAMMB,
|
||||
"Memory limit for apiserver in MB (used to configure sizes of caches, etc.)")
|
||||
|
||||
fs.StringVar(&s.ExternalHost, "external-hostname", s.ExternalHost,
|
||||
"The hostname to use when generating externalized URLs for this master (e.g. Swagger API Docs).")
|
||||
|
||||
deprecatedMasterServiceNamespace := metav1.NamespaceDefault
|
||||
fs.StringVar(&deprecatedMasterServiceNamespace, "master-service-namespace", deprecatedMasterServiceNamespace, ""+
|
||||
"DEPRECATED: the namespace from which the kubernetes master services should be injected into pods.")
|
||||
|
||||
fs.IntVar(&s.MaxRequestsInFlight, "max-requests-inflight", s.MaxRequestsInFlight, ""+
|
||||
"The maximum number of non-mutating requests in flight at a given time. When the server exceeds this, "+
|
||||
"it rejects requests. Zero for no limit.")
|
||||
|
||||
fs.IntVar(&s.MaxMutatingRequestsInFlight, "max-mutating-requests-inflight", s.MaxMutatingRequestsInFlight, ""+
|
||||
"The maximum number of mutating requests in flight at a given time. When the server exceeds this, "+
|
||||
"it rejects requests. Zero for no limit.")
|
||||
|
||||
fs.DurationVar(&s.RequestTimeout, "request-timeout", s.RequestTimeout, ""+
|
||||
"An optional field indicating the duration a handler must keep a request open before timing "+
|
||||
"it out. This is the default request timeout for requests but may be overridden by flags such as "+
|
||||
"--min-request-timeout for specific types of requests.")
|
||||
|
||||
fs.IntVar(&s.MinRequestTimeout, "min-request-timeout", s.MinRequestTimeout, ""+
|
||||
"An optional field indicating the minimum number of seconds a handler must keep "+
|
||||
"a request open before timing it out. Currently only honored by the watch request "+
|
||||
"handler, which picks a randomized value above this number as the connection timeout, "+
|
||||
"to spread out load.")
|
||||
|
||||
utilfeature.DefaultFeatureGate.AddFlag(fs)
|
||||
}
|
||||
342
vendor/k8s.io/apiserver/pkg/server/options/serving.go
generated
vendored
Normal file
342
vendor/k8s.io/apiserver/pkg/server/options/serving.go
generated
vendored
Normal file
@@ -0,0 +1,342 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes 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 (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/klog"
|
||||
|
||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
utilflag "k8s.io/apiserver/pkg/util/flag"
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
)
|
||||
|
||||
type SecureServingOptions struct {
|
||||
BindAddress net.IP
|
||||
// BindPort is ignored when Listener is set, will serve https even with 0.
|
||||
BindPort int
|
||||
// BindNetwork is the type of network to bind to - defaults to "tcp", accepts "tcp",
|
||||
// "tcp4", and "tcp6".
|
||||
BindNetwork string
|
||||
// Required set to true means that BindPort cannot be zero.
|
||||
Required bool
|
||||
// ExternalAddress is the address advertised, even if BindAddress is a loopback. By default this
|
||||
// is set to BindAddress if the later no loopback, or to the first host interface address.
|
||||
ExternalAddress net.IP
|
||||
|
||||
// Listener is the secure server network listener.
|
||||
// either Listener or BindAddress/BindPort/BindNetwork is set,
|
||||
// if Listener is set, use it and omit BindAddress/BindPort/BindNetwork.
|
||||
Listener net.Listener
|
||||
|
||||
// ServerCert is the TLS cert info for serving secure traffic
|
||||
ServerCert GeneratableKeyCert
|
||||
// SNICertKeys are named CertKeys for serving secure traffic with SNI support.
|
||||
SNICertKeys []utilflag.NamedCertKey
|
||||
// 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).
|
||||
CipherSuites []string
|
||||
// MinTLSVersion is the minimum TLS version supported.
|
||||
// Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).
|
||||
MinTLSVersion string
|
||||
|
||||
// HTTP2MaxStreamsPerConnection is the limit that the api server imposes on each client.
|
||||
// A value of zero means to use the default provided by golang's HTTP/2 support.
|
||||
HTTP2MaxStreamsPerConnection int
|
||||
}
|
||||
|
||||
type CertKey struct {
|
||||
// CertFile is a file containing a PEM-encoded certificate, and possibly the complete certificate chain
|
||||
CertFile string
|
||||
// KeyFile is a file containing a PEM-encoded private key for the certificate specified by CertFile
|
||||
KeyFile string
|
||||
}
|
||||
|
||||
type GeneratableKeyCert struct {
|
||||
// CertKey allows setting an explicit cert/key file to use.
|
||||
CertKey CertKey
|
||||
|
||||
// CertDirectory specifies a directory to write generated certificates to if CertFile/KeyFile aren't explicitly set.
|
||||
// PairName is used to determine the filenames within CertDirectory.
|
||||
// If CertDirectory and PairName are not set, an in-memory certificate will be generated.
|
||||
CertDirectory string
|
||||
// PairName is the name which will be used with CertDirectory to make a cert and key filenames.
|
||||
// It becomes CertDirectory/PairName.crt and CertDirectory/PairName.key
|
||||
PairName string
|
||||
|
||||
// GeneratedCert holds an in-memory generated certificate if CertFile/KeyFile aren't explicitly set, and CertDirectory/PairName are not set.
|
||||
GeneratedCert *tls.Certificate
|
||||
|
||||
// FixtureDirectory is a directory that contains test fixture used to avoid regeneration of certs during tests.
|
||||
// The format is:
|
||||
// <host>_<ip>-<ip>_<alternateDNS>-<alternateDNS>.crt
|
||||
// <host>_<ip>-<ip>_<alternateDNS>-<alternateDNS>.key
|
||||
FixtureDirectory string
|
||||
}
|
||||
|
||||
func NewSecureServingOptions() *SecureServingOptions {
|
||||
return &SecureServingOptions{
|
||||
BindAddress: net.ParseIP("0.0.0.0"),
|
||||
BindPort: 443,
|
||||
ServerCert: GeneratableKeyCert{
|
||||
PairName: "apiserver",
|
||||
CertDirectory: "apiserver.local.config/certificates",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SecureServingOptions) DefaultExternalAddress() (net.IP, error) {
|
||||
if !s.ExternalAddress.IsUnspecified() {
|
||||
return s.ExternalAddress, nil
|
||||
}
|
||||
return utilnet.ChooseBindAddress(s.BindAddress)
|
||||
}
|
||||
|
||||
func (s *SecureServingOptions) Validate() []error {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errors := []error{}
|
||||
|
||||
if s.Required && s.BindPort < 1 || s.BindPort > 65535 {
|
||||
errors = append(errors, fmt.Errorf("--secure-port %v must be between 1 and 65535, inclusive. It cannot be turned off with 0", s.BindPort))
|
||||
} else if s.BindPort < 0 || s.BindPort > 65535 {
|
||||
errors = append(errors, fmt.Errorf("--secure-port %v must be between 0 and 65535, inclusive. 0 for turning off secure port", s.BindPort))
|
||||
}
|
||||
|
||||
if (len(s.ServerCert.CertKey.CertFile) != 0 || len(s.ServerCert.CertKey.KeyFile) != 0) && s.ServerCert.GeneratedCert != nil {
|
||||
errors = append(errors, fmt.Errorf("cert/key file and in-memory certificate cannot both be set"))
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
func (s *SecureServingOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fs.IPVar(&s.BindAddress, "bind-address", s.BindAddress, ""+
|
||||
"The IP address on which to listen for the --secure-port port. The "+
|
||||
"associated interface(s) must be reachable by the rest of the cluster, and by CLI/web "+
|
||||
"clients. If blank, all interfaces will be used (0.0.0.0 for all IPv4 interfaces and :: for all IPv6 interfaces).")
|
||||
|
||||
desc := "The port on which to serve HTTPS with authentication and authorization."
|
||||
if s.Required {
|
||||
desc += "It cannot be switched off with 0."
|
||||
} else {
|
||||
desc += "If 0, don't serve HTTPS at all."
|
||||
}
|
||||
fs.IntVar(&s.BindPort, "secure-port", s.BindPort, desc)
|
||||
|
||||
fs.StringVar(&s.ServerCert.CertDirectory, "cert-dir", s.ServerCert.CertDirectory, ""+
|
||||
"The directory where the TLS certs are located. "+
|
||||
"If --tls-cert-file and --tls-private-key-file are provided, this flag will be ignored.")
|
||||
|
||||
fs.StringVar(&s.ServerCert.CertKey.CertFile, "tls-cert-file", s.ServerCert.CertKey.CertFile, ""+
|
||||
"File containing the default x509 Certificate for HTTPS. (CA cert, if any, concatenated "+
|
||||
"after server cert). If HTTPS serving is enabled, and --tls-cert-file and "+
|
||||
"--tls-private-key-file are not provided, a self-signed certificate and key "+
|
||||
"are generated for the public address and saved to the directory specified by --cert-dir.")
|
||||
|
||||
fs.StringVar(&s.ServerCert.CertKey.KeyFile, "tls-private-key-file", s.ServerCert.CertKey.KeyFile,
|
||||
"File containing the default x509 private key matching --tls-cert-file.")
|
||||
|
||||
tlsCipherPossibleValues := utilflag.TLSCipherPossibleValues()
|
||||
fs.StringSliceVar(&s.CipherSuites, "tls-cipher-suites", s.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 := utilflag.TLSPossibleVersions()
|
||||
fs.StringVar(&s.MinTLSVersion, "tls-min-version", s.MinTLSVersion,
|
||||
"Minimum TLS version supported. "+
|
||||
"Possible values: "+strings.Join(tlsPossibleVersions, ", "))
|
||||
|
||||
fs.Var(utilflag.NewNamedCertKeyArray(&s.SNICertKeys), "tls-sni-cert-key", ""+
|
||||
"A pair of x509 certificate and private key file paths, optionally suffixed with a list of "+
|
||||
"domain patterns which are fully qualified domain names, possibly with prefixed wildcard "+
|
||||
"segments. If no domain patterns are provided, the names of the certificate are "+
|
||||
"extracted. Non-wildcard matches trump over wildcard matches, explicit domain patterns "+
|
||||
"trump over extracted names. For multiple key/certificate pairs, use the "+
|
||||
"--tls-sni-cert-key multiple times. "+
|
||||
"Examples: \"example.crt,example.key\" or \"foo.crt,foo.key:*.foo.com,foo.com\".")
|
||||
|
||||
fs.IntVar(&s.HTTP2MaxStreamsPerConnection, "http2-max-streams-per-connection", s.HTTP2MaxStreamsPerConnection, ""+
|
||||
"The limit that the server gives to clients for "+
|
||||
"the maximum number of streams in an HTTP/2 connection. "+
|
||||
"Zero means to use golang's default.")
|
||||
}
|
||||
|
||||
// ApplyTo fills up serving information in the server configuration.
|
||||
func (s *SecureServingOptions) ApplyTo(config **server.SecureServingInfo) error {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
if s.BindPort <= 0 && s.Listener == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if s.Listener == nil {
|
||||
var err error
|
||||
addr := net.JoinHostPort(s.BindAddress.String(), strconv.Itoa(s.BindPort))
|
||||
s.Listener, s.BindPort, err = CreateListener(s.BindNetwork, addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create listener: %v", err)
|
||||
}
|
||||
} else {
|
||||
if _, ok := s.Listener.Addr().(*net.TCPAddr); !ok {
|
||||
return fmt.Errorf("failed to parse ip and port from listener")
|
||||
}
|
||||
s.BindPort = s.Listener.Addr().(*net.TCPAddr).Port
|
||||
s.BindAddress = s.Listener.Addr().(*net.TCPAddr).IP
|
||||
}
|
||||
|
||||
*config = &server.SecureServingInfo{
|
||||
Listener: s.Listener,
|
||||
HTTP2MaxStreamsPerConnection: s.HTTP2MaxStreamsPerConnection,
|
||||
}
|
||||
c := *config
|
||||
|
||||
serverCertFile, serverKeyFile := s.ServerCert.CertKey.CertFile, s.ServerCert.CertKey.KeyFile
|
||||
// load main cert
|
||||
if len(serverCertFile) != 0 || len(serverKeyFile) != 0 {
|
||||
tlsCert, err := tls.LoadX509KeyPair(serverCertFile, serverKeyFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load server certificate: %v", err)
|
||||
}
|
||||
c.Cert = &tlsCert
|
||||
} else if s.ServerCert.GeneratedCert != nil {
|
||||
c.Cert = s.ServerCert.GeneratedCert
|
||||
}
|
||||
|
||||
if len(s.CipherSuites) != 0 {
|
||||
cipherSuites, err := utilflag.TLSCipherSuites(s.CipherSuites)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.CipherSuites = cipherSuites
|
||||
}
|
||||
|
||||
var err error
|
||||
c.MinTLSVersion, err = utilflag.TLSVersion(s.MinTLSVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// load SNI certs
|
||||
namedTLSCerts := make([]server.NamedTLSCert, 0, len(s.SNICertKeys))
|
||||
for _, nck := range s.SNICertKeys {
|
||||
tlsCert, err := tls.LoadX509KeyPair(nck.CertFile, nck.KeyFile)
|
||||
namedTLSCerts = append(namedTLSCerts, server.NamedTLSCert{
|
||||
TLSCert: tlsCert,
|
||||
Names: nck.Names,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load SNI cert and key: %v", err)
|
||||
}
|
||||
}
|
||||
c.SNICerts, err = server.GetNamedCertificateMap(namedTLSCerts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SecureServingOptions) MaybeDefaultWithSelfSignedCerts(publicAddress string, alternateDNS []string, alternateIPs []net.IP) error {
|
||||
if s == nil || (s.BindPort == 0 && s.Listener == nil) {
|
||||
return nil
|
||||
}
|
||||
keyCert := &s.ServerCert.CertKey
|
||||
if len(keyCert.CertFile) != 0 || len(keyCert.KeyFile) != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
canReadCertAndKey := false
|
||||
if len(s.ServerCert.CertDirectory) > 0 {
|
||||
if len(s.ServerCert.PairName) == 0 {
|
||||
return fmt.Errorf("PairName is required if CertDirectory is set")
|
||||
}
|
||||
keyCert.CertFile = path.Join(s.ServerCert.CertDirectory, s.ServerCert.PairName+".crt")
|
||||
keyCert.KeyFile = path.Join(s.ServerCert.CertDirectory, s.ServerCert.PairName+".key")
|
||||
if canRead, err := certutil.CanReadCertAndKey(keyCert.CertFile, keyCert.KeyFile); err != nil {
|
||||
return err
|
||||
} else {
|
||||
canReadCertAndKey = canRead
|
||||
}
|
||||
}
|
||||
|
||||
if !canReadCertAndKey {
|
||||
// add either the bind address or localhost to the valid alternates
|
||||
bindIP := s.BindAddress.String()
|
||||
if bindIP == "0.0.0.0" {
|
||||
alternateDNS = append(alternateDNS, "localhost")
|
||||
} else {
|
||||
alternateIPs = append(alternateIPs, s.BindAddress)
|
||||
}
|
||||
|
||||
if cert, key, err := certutil.GenerateSelfSignedCertKeyWithFixtures(publicAddress, alternateIPs, alternateDNS, s.ServerCert.FixtureDirectory); err != nil {
|
||||
return fmt.Errorf("unable to generate self signed cert: %v", err)
|
||||
} else if len(keyCert.CertFile) > 0 && len(keyCert.KeyFile) > 0 {
|
||||
if err := certutil.WriteCert(keyCert.CertFile, cert); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := certutil.WriteKey(keyCert.KeyFile, key); err != nil {
|
||||
return err
|
||||
}
|
||||
klog.Infof("Generated self-signed cert (%s, %s)", keyCert.CertFile, keyCert.KeyFile)
|
||||
} else {
|
||||
tlsCert, err := tls.X509KeyPair(cert, key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to generate self signed cert: %v", err)
|
||||
}
|
||||
s.ServerCert.GeneratedCert = &tlsCert
|
||||
klog.Infof("Generated self-signed cert in-memory")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateListener(network, addr string) (net.Listener, int, error) {
|
||||
if len(network) == 0 {
|
||||
network = "tcp"
|
||||
}
|
||||
ln, err := net.Listen(network, addr)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("failed to listen on %v: %v", addr, err)
|
||||
}
|
||||
|
||||
// get port
|
||||
tcpAddr, ok := ln.Addr().(*net.TCPAddr)
|
||||
if !ok {
|
||||
ln.Close()
|
||||
return nil, 0, fmt.Errorf("invalid listen address: %q", ln.Addr().String())
|
||||
}
|
||||
|
||||
return ln, tcpAddr.Port, nil
|
||||
}
|
||||
78
vendor/k8s.io/apiserver/pkg/server/options/serving_with_loopback.go
generated
vendored
Normal file
78
vendor/k8s.io/apiserver/pkg/server/options/serving_with_loopback.go
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes 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 (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
|
||||
"github.com/pborman/uuid"
|
||||
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/client-go/rest"
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
)
|
||||
|
||||
type SecureServingOptionsWithLoopback struct {
|
||||
*SecureServingOptions
|
||||
}
|
||||
|
||||
func (o *SecureServingOptions) WithLoopback() *SecureServingOptionsWithLoopback {
|
||||
return &SecureServingOptionsWithLoopback{o}
|
||||
}
|
||||
|
||||
// ApplyTo fills up serving information in the server configuration.
|
||||
func (s *SecureServingOptionsWithLoopback) ApplyTo(secureServingInfo **server.SecureServingInfo, loopbackClientConfig **rest.Config) error {
|
||||
if s == nil || s.SecureServingOptions == nil || secureServingInfo == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := s.SecureServingOptions.ApplyTo(secureServingInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if *secureServingInfo == nil || loopbackClientConfig == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// create self-signed cert+key with the fake server.LoopbackClientServerNameOverride and
|
||||
// let the server return it when the loopback client connects.
|
||||
certPem, keyPem, err := certutil.GenerateSelfSignedCertKey(server.LoopbackClientServerNameOverride, nil, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate self-signed certificate for loopback connection: %v", err)
|
||||
}
|
||||
tlsCert, err := tls.X509KeyPair(certPem, keyPem)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate self-signed certificate for loopback connection: %v", err)
|
||||
}
|
||||
|
||||
secureLoopbackClientConfig, err := (*secureServingInfo).NewLoopbackClientConfig(uuid.NewRandom().String(), certPem)
|
||||
switch {
|
||||
// if we failed and there's no fallback loopback client config, we need to fail
|
||||
case err != nil && *loopbackClientConfig == nil:
|
||||
return err
|
||||
|
||||
// if we failed, but we already have a fallback loopback client config (usually insecure), allow it
|
||||
case err != nil && *loopbackClientConfig != nil:
|
||||
|
||||
default:
|
||||
*loopbackClientConfig = secureLoopbackClientConfig
|
||||
(*secureServingInfo).SNICerts[server.LoopbackClientServerNameOverride] = &tlsCert
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
34
vendor/k8s.io/apiserver/pkg/server/options/webhook.go
generated
vendored
Normal file
34
vendor/k8s.io/apiserver/pkg/server/options/webhook.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes 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 (
|
||||
utilwebhook "k8s.io/apiserver/pkg/util/webhook"
|
||||
)
|
||||
|
||||
// WebhookOptions holds the outgoing webhook options
|
||||
type WebhookOptions struct {
|
||||
ServiceResolver utilwebhook.ServiceResolver
|
||||
AuthInfoResolverWrapper utilwebhook.AuthenticationInfoResolverWrapper
|
||||
}
|
||||
|
||||
// NewWebhookOptions returns the default options for outgoing webhooks
|
||||
func NewWebhookOptions() *WebhookOptions {
|
||||
return &WebhookOptions{
|
||||
ServiceResolver: utilwebhook.NewDefaultServiceResolver(),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user