return nil, err } if actions == nil { actions = &DefaultLimitRangerActions{} } return &limitRanger{ Handler: admission.NewHandler(admission.Create, admission.Update), actions: actions, liveLookupCache: liveLookupCache, liveTTL: time.Duration(30 * time.Second), }, nil } var _ = kubeapiserveradmission.WantsInternalClientSet(&limitRanger{}) func (a *limitRanger) SetInternalClientSet(client internalclientset.Interface) { a.client = client } // defaultContainerResourceRequirements returns the default requirements for a container // the requirement.Limits are taken from the LimitRange defaults (if specified) // the requirement.Requests are taken from the LimitRange default request (if specified) func defaultContainerResourceRequirements(limitRange *api.LimitRange) api.ResourceRequirements { requirements := api.ResourceRequirements{} requirements.Requests = api.ResourceList{} requirements.Limits = api.ResourceList{} for i := range limitRange.Spec.Limits { limit := limitRange.Spec.Limits[i]
pluginConfig := readConfig(config) plugin := NewPodNodeSelector(pluginConfig.PodNodeSelectorPluginConfig) return plugin, nil }) } // podNodeSelector is an implementation of admission.Interface. type podNodeSelector struct { *admission.Handler client internalclientset.Interface namespaceInformer cache.SharedIndexInformer // global default node selector and namespace whitelists in a cluster. clusterNodeSelectors map[string]string } var _ = kubeapiserveradmission.WantsInternalClientSet(&podNodeSelector{}) type pluginConfig struct { PodNodeSelectorPluginConfig map[string]string } // readConfig reads default value of clusterDefaultNodeSelector // from the file provided with --admission-control-config-file // If the file is not supplied, it defaults to "" // The format in a file: // podNodeSelectorPluginConfig: // clusterDefaultNodeSelector: <node-selectors-labels> // namespace1: <node-selectors-labels> // namespace2: <node-selectors-labels> func readConfig(config io.Reader) *pluginConfig { defaultConfig := &pluginConfig{}
admission.RegisterPlugin("NamespaceExists", func(config io.Reader) (admission.Interface, error) { return NewExists(), nil }) } // exists is an implementation of admission.Interface. // It rejects all incoming requests in a namespace context if the namespace does not exist. // It is useful in deployments that want to enforce pre-declaration of a Namespace resource. type exists struct { *admission.Handler client internalclientset.Interface namespaceInformer cache.SharedIndexInformer } var _ = kubeapiserveradmission.WantsInformerFactory(&exists{}) var _ = kubeapiserveradmission.WantsInternalClientSet(&exists{}) func (e *exists) Admit(a admission.Attributes) (err error) { // if we're here, then we've already passed authentication, so we're allowed to do what we're trying to do // if we're here, then the API server has found a route, which means that if we have a non-empty namespace // its a namespaced resource. if len(a.GetNamespace()) == 0 || a.GetKind().GroupKind() == api.Kind("Namespace") { return nil } // we need to wait for our caches to warm if !e.WaitForReady() { return admission.NewForbidden(a, fmt.Errorf("not yet ready to handle request")) } namespace := &api.Namespace{ ObjectMeta: metav1.ObjectMeta{
registry := install.NewRegistry(nil, nil) return NewResourceQuota(registry, 5, make(chan struct{})) }) } // quotaAdmission implements an admission controller that can enforce quota constraints type quotaAdmission struct { *admission.Handler stopCh <-chan struct{} registry quota.Registry numEvaluators int evaluator Evaluator } var _ = kubeapiserveradmission.WantsInternalClientSet("aAdmission{}) type liveLookupEntry struct { expiry time.Time items []*api.ResourceQuota } // NewResourceQuota configures an admission controller that can enforce quota constraints // using the provided registry. The registry must have the capability to handle group/kinds that // are persisted by the server this admission controller is intercepting func NewResourceQuota(registry quota.Registry, numEvaluators int, stopCh <-chan struct{}) (admission.Interface, error) { return "aAdmission{ Handler: admission.NewHandler(admission.Create, admission.Update), stopCh: stopCh, registry: registry, numEvaluators: numEvaluators,
}) } // denyExec is an implementation of admission.Interface which says no to a pod/exec on // a pod using host based configurations. type denyExec struct { *admission.Handler client internalclientset.Interface // these flags control which items will be checked to deny exec/attach hostIPC bool hostPID bool privileged bool } var _ = kubeapiserveradmission.WantsInternalClientSet(&denyExec{}) // NewDenyEscalatingExec creates a new admission controller that denies an exec operation on a pod // using host based configurations. func NewDenyEscalatingExec() admission.Interface { return &denyExec{ Handler: admission.NewHandler(admission.Connect), hostIPC: true, hostPID: true, privileged: true, } } // NewDenyExecOnPrivileged creates a new admission controller that is only checking the privileged // option. This is for legacy support of the DenyExecOnPrivileged admission controller. Most // of the time NewDenyEscalatingExec should be preferred.
type lifecycle struct { *admission.Handler client internalclientset.Interface immortalNamespaces sets.String namespaceInformer cache.SharedIndexInformer // forceLiveLookupCache holds a list of entries for namespaces that we have a strong reason to believe are stale in our local cache. // if a namespace is in this cache, then we will ignore our local state and always fetch latest from api server. forceLiveLookupCache *utilcache.LRUExpireCache } type forceLiveLookupEntry struct { expiry time.Time } var _ = kubeapiserveradmission.WantsInformerFactory(&lifecycle{}) var _ = kubeapiserveradmission.WantsInternalClientSet(&lifecycle{}) func makeNamespaceKey(namespace string) *api.Namespace { return &api.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: namespace, Namespace: "", }, } } func (l *lifecycle) Admit(a admission.Attributes) error { // prevent deletion of immortal namespaces if a.GetOperation() == admission.Delete && a.GetKind().GroupKind() == api.Kind("Namespace") && l.immortalNamespaces.Has(a.GetName()) { return errors.NewForbidden(a.GetResource().GroupResource(), a.GetName(), fmt.Errorf("this namespace may not be deleted")) }
return plugin, nil }) } // claimDefaulterPlugin holds state for and implements the admission plugin. type claimDefaulterPlugin struct { *admission.Handler client internalclientset.Interface reflector *cache.Reflector stopChan chan struct{} store cache.Store } var _ admission.Interface = &claimDefaulterPlugin{} var _ = kubeapiserveradmission.WantsInternalClientSet(&claimDefaulterPlugin{}) // newPlugin creates a new admission plugin. func newPlugin() *claimDefaulterPlugin { return &claimDefaulterPlugin{ Handler: admission.NewHandler(admission.Create), } } func (a *claimDefaulterPlugin) SetInternalClientSet(client internalclientset.Interface) { a.client = client a.store = cache.NewStore(cache.MetaNamespaceKeyFunc) a.reflector = cache.NewReflector( &cache.ListWatch{ ListFunc: func(options v1.ListOptions) (runtime.Object, error) { internalOptions := api.ListOptions{}
// RequireAPIToken determines whether pod creation attempts are rejected if no API token exists for the pod's service account RequireAPIToken bool // MountServiceAccountToken creates Volume and VolumeMounts for the first referenced ServiceAccountToken for the pod's service account MountServiceAccountToken bool client internalclientset.Interface serviceAccounts cache.Indexer secrets cache.Indexer stopChan chan struct{} serviceAccountsReflector *cache.Reflector secretsReflector *cache.Reflector } var _ = kubeapiserveradmission.WantsInternalClientSet(&serviceAccount{}) // NewServiceAccount returns an admission.Interface implementation which limits admission of Pod CREATE requests based on the pod's ServiceAccount: // 1. If the pod does not specify a ServiceAccount, it sets the pod's ServiceAccount to "default" // 2. It ensures the ServiceAccount referenced by the pod exists // 3. If LimitSecretReferences is true, it rejects the pod if the pod references Secret objects which the pod's ServiceAccount does not reference // 4. If the pod does not contain any ImagePullSecrets, the ImagePullSecrets of the service account are added. // 5. If MountServiceAccountToken is true, it adds a VolumeMount with the pod's ServiceAccount's api token secret to containers func NewServiceAccount() *serviceAccount { return &serviceAccount{ Handler: admission.NewHandler(admission.Create), // TODO: enable this once we've swept secret usage to account for adding secret references to service accounts LimitSecretReferences: false, // Auto mount service account API token secrets MountServiceAccountToken: true, // Reject pod creation until a service account token is available