CINXE.COM
API Access Control | Kubernetes
<!doctype html><html itemscope itemtype=http://schema.org/WebPage lang=en class=no-js><head><meta name=robots content="noindex, nofollow"><link rel=alternate hreflang=zh-cn href=https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/><link rel=alternate hreflang=fr href=https://kubernetes.io/fr/docs/reference/access-authn-authz/><link rel=alternate hreflang=de href=https://kubernetes.io/de/docs/reference/access-authn-authz/><link rel=alternate hreflang=id href=https://kubernetes.io/id/docs/reference/access-authn-authz/><link rel=alternate hreflang=ko href=https://kubernetes.io/ko/docs/reference/access-authn-authz/><link rel=alternate hreflang=es href=https://kubernetes.io/es/docs/reference/access-authn-authz/><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=generator content="Hugo 0.133.0"><link rel=canonical type=text/html href=https://kubernetes.io/docs/reference/access-authn-authz/><link rel="shortcut icon" type=image/png href=/images/kubernetes.png><link rel=icon type=image/png sizes=64x64 href=/icons/favicon-64.png><link rel=icon type=image/png sizes=32x32 href=/icons/favicon-32.png><link rel=icon type=image/png sizes=16x16 href=/icons/favicon-16.png><link rel=apple-touch-icon-256x256 href=/icons/apple-touch-icon-256x256.png><link rel=apple-touch-icon-196x196 href=/icons/apple-touch-icon-196x196.png><link rel=apple-touch-icon-192x192 href=/icons/apple-touch-icon-192x192.png><link rel=apple-touch-icon-180x180 href=/icons/apple-touch-icon-180x180.png><link rel=apple-touch-icon-167x167 href=/icons/apple-touch-icon-167x167.png><link rel=apple-touch-icon-160x160 href=/icons/apple-touch-icon-160x160.png><link rel=apple-touch-icon-152x152 href=/icons/apple-touch-icon-152x152.png><link rel=apple-touch-icon-120x120 href=/icons/apple-touch-icon-120x120.png><link rel=apple-touch-icon-76x76 href=/icons/apple-touch-icon-76x76.png><link rel=icon type=image/png href=/icons/icon-128x128.png sizes=128x128><meta name=theme-color content="#326de6"><title>API Access Control | Kubernetes</title><meta property="og:url" content="https://kubernetes.io/docs/reference/access-authn-authz/"> <meta property="og:site_name" content="Kubernetes"><meta property="og:title" content="API Access Control"><meta property="og:description" content="For an introduction to how Kubernetes implements and controls API access, read Controlling Access to the Kubernetes API. Reference documentation: Authenticating Authenticating with Bootstrap Tokens Admission Controllers Dynamic Admission Control Authorization Role Based Access Control Attribute Based Access Control Node Authorization Webhook Authorization Certificate Signing Requests including CSR approval and certificate signing Service accounts Developer guide Administration Kubelet Authentication & Authorization including kubelet TLS bootstrapping "><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta itemprop=name content="API Access Control"><meta itemprop=description content="For an introduction to how Kubernetes implements and controls API access, read Controlling Access to the Kubernetes API. Reference documentation: Authenticating Authenticating with Bootstrap Tokens Admission Controllers Dynamic Admission Control Authorization Role Based Access Control Attribute Based Access Control Node Authorization Webhook Authorization Certificate Signing Requests including CSR approval and certificate signing Service accounts Developer guide Administration Kubelet Authentication & Authorization including kubelet TLS bootstrapping "><meta itemprop=dateModified content="2022-11-04T11:37:59-04:00"><meta itemprop=wordCount content="65"><meta name=twitter:card content="summary"><meta name=twitter:title content="API Access Control"><meta name=twitter:description content="For an introduction to how Kubernetes implements and controls API access, read Controlling Access to the Kubernetes API. Reference documentation: Authenticating Authenticating with Bootstrap Tokens Admission Controllers Dynamic Admission Control Authorization Role Based Access Control Attribute Based Access Control Node Authorization Webhook Authorization Certificate Signing Requests including CSR approval and certificate signing Service accounts Developer guide Administration Kubelet Authentication & Authorization including kubelet TLS bootstrapping "><script async src="https://www.googletagmanager.com/gtag/js?id=G-JPP6RFM2BP"></script><script>var dnt,doNotTrack=!1;if(!1&&(dnt=navigator.doNotTrack||window.doNotTrack||navigator.msDoNotTrack,doNotTrack=dnt=="1"||dnt=="yes"),!doNotTrack){window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments)}gtag("js",new Date),gtag("config","G-JPP6RFM2BP")}</script><link rel=preload href=/scss/main.min.47937ebb3d7d851e399e3c75540ff973622d74e30e9fc7960b93a9572eface54.css as=style><link href=/scss/main.min.47937ebb3d7d851e399e3c75540ff973622d74e30e9fc7960b93a9572eface54.css rel=stylesheet integrity><script type=application/ld+json>{"@context":"https://schema.org","@type":"Organization","url":"https://kubernetes.io","logo":"https://kubernetes.io/images/favicon.png","potentialAction":{"@type":"SearchAction","target":"https://kubernetes.io/search/?q={search_term_string}","query-input":"required name=search_term_string"}}</script><meta name=theme-color content="#326ce5"><style>.gutter{background-color:#eee;background-repeat:no-repeat;background-position:50%}.gutter.gutter-horizontal{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==);cursor:col-resize}#sidebarnav,#maindoc{max-width:100%}#maindoc{overflow-wrap:break-word}@media(max-width:768px){#sidebarnav{padding-left:15px;padding-right:15px}}</style><meta name=description content="For an introduction to how Kubernetes implements and controls API access, read Controlling Access to the Kubernetes API. Reference documentation: Authenticating Authenticating with Bootstrap Tokens Admission Controllers Dynamic Admission Control Authorization Role Based Access Control Attribute Based Access Control Node Authorization Webhook Authorization Certificate Signing Requests including CSR approval and certificate signing Service accounts Developer guide Administration Kubelet Authentication & Authorization including kubelet TLS bootstrapping "><meta property="og:description" content="For an introduction to how Kubernetes implements and controls API access, read Controlling Access to the Kubernetes API. Reference documentation: Authenticating Authenticating with Bootstrap Tokens Admission Controllers Dynamic Admission Control Authorization Role Based Access Control Attribute Based Access Control Node Authorization Webhook Authorization Certificate Signing Requests including CSR approval and certificate signing Service accounts Developer guide Administration Kubelet Authentication & Authorization including kubelet TLS bootstrapping "><meta name=twitter:description content="For an introduction to how Kubernetes implements and controls API access, read Controlling Access to the Kubernetes API. Reference documentation: Authenticating Authenticating with Bootstrap Tokens Admission Controllers Dynamic Admission Control Authorization Role Based Access Control Attribute Based Access Control Node Authorization Webhook Authorization Certificate Signing Requests including CSR approval and certificate signing Service accounts Developer guide Administration Kubelet Authentication & Authorization including kubelet TLS bootstrapping "><meta property="og:url" content="https://kubernetes.io/docs/reference/access-authn-authz/"><meta property="og:title" content="API Access Control"><meta name=twitter:title content="API Access Control"><meta name=twitter:image content="https://kubernetes.io/images/favicon.png"><meta name=twitter:image:alt content="Kubernetes"><meta property="og:image" content="/images/kubernetes-horizontal-color.png"><meta property="og:type" content="article"><script src=/js/jquery-3.6.0.min.js intregrity=sha384-vtXRMe3mGCbOeY7l30aIg8H9p3GdeSe4IFlP6G8JMa7o7lXvnz3GFKzPxzJdPfGK crossorigin=anonymous></script><script src=/js/split-1.6.0.js intregrity=sha384-0blL3GqHy6+9fw0cyY2Aoiwg4onHAtslAs4OkqZY7UQBrR65/K4gI+hxLdWDrjpz></script><link rel=manifest href=/manifest.webmanifest><script defer src=/js/banner-dismiss.min.095ea25f4d8d1299fa348e27bfc7822ce87d0fba4f01d3660670afc7b3184df0.js integrity="sha256-CV6iX02NEpn6NI4nv8eCLOh9D7pPAdNmBnCvx7MYTfA=" crossorigin=anonymous></script></head><body class=td-section><header><nav class="js-navbar-scroll navbar navbar-expand navbar-dark flex-column flex-md-row td-navbar" data-auto-burger=primary><a class="navbar-brand img-fluid" href=/></a><div class="td-navbar-nav-scroll ml-md-auto" id=main_navbar><ul class="navbar-nav mt-2 mt-lg-0"><li class="nav-item mr-2 mb-lg-0"><a class="nav-link active" href=/docs/>Documentation</a></li><li class="nav-item mr-2 mb-lg-0"><a class=nav-link href=/blog/>Kubernetes Blog</a></li><li class="nav-item mr-2 mb-lg-0"><a class=nav-link href=/training/>Training</a></li><li class="nav-item mr-2 mb-lg-0"><a class=nav-link href=/partners/>Partners</a></li><li class="nav-item mr-2 mb-lg-0"><a class=nav-link href=/community/>Community</a></li><li class="nav-item mr-2 mb-lg-0"><a class=nav-link href=/case-studies/>Case Studies</a></li><li class="nav-item mr-n3 mr-lg-0 dropdown"><a class="nav-link dropdown-toggle" href=# id=navbarDropdown role=button data-toggle=dropdown aria-haspopup=true aria-expanded=false>Versions</a><div class="dropdown-menu dropdown-menu-right" aria-labelledby=navbarDropdownMenuLink><a class=dropdown-item href=/releases>Release Information</a> <a class=dropdown-item href=https://kubernetes.io/docs/reference/access-authn-authz/>v1.31</a> <a class=dropdown-item href=https://v1-30.docs.kubernetes.io/docs/reference/access-authn-authz/>v1.30</a> <a class=dropdown-item href=https://v1-29.docs.kubernetes.io/docs/reference/access-authn-authz/>v1.29</a> <a class=dropdown-item href=https://v1-28.docs.kubernetes.io/docs/reference/access-authn-authz/>v1.28</a> <a class=dropdown-item href=https://v1-27.docs.kubernetes.io/docs/reference/access-authn-authz/>v1.27</a></div></li><li class="nav-item mr-n4 mr-lg-0 dropdown"><a class="nav-link dropdown-toggle" href=# id=navbarDropdownMenuLink role=button data-toggle=dropdown aria-haspopup=true aria-expanded=false>English</a><div class="dropdown-menu dropdown-menu-right" aria-labelledby=navbarDropdownMenuLink><a class=dropdown-item href=/zh-cn/docs/reference/access-authn-authz/>中文 (Chinese)</a> <a class=dropdown-item href=/fr/docs/reference/access-authn-authz/>Français (French)</a> <a class=dropdown-item href=/de/docs/reference/access-authn-authz/>Deutsch (German)</a> <a class=dropdown-item href=/id/docs/reference/access-authn-authz/>Bahasa Indonesia (Indonesian)</a> <a class=dropdown-item href=/ko/docs/reference/access-authn-authz/>한국어 (Korean)</a> <a class=dropdown-item href=/es/docs/reference/access-authn-authz/>Español (Spanish)</a></div></li><li class="search-item nav-item mr-n4 mr-lg-0"><div class=search-bar><i class="search-icon fas fa-search"></i> <input type=search name=q data-search-page=/search/ class="search-input td-search-input" placeholder="Search this site" aria-label="Search this site" autocomplete=off></div></li></ul></div><button id=hamburger onclick=kub.toggleMenu() data-auto-burger-exclude><div></div></button></nav></header><div class="container-fluid td-outer"><div class=td-main><div class="row flex-xl-nowrap"><main class="col-12 col-md-9 col-xl-8 pl-md-5" role=main><div class=td-content><div class="pageinfo pageinfo-primary d-print-none"><p>This is the multi-page printable view of this section. <a href=# onclick="return print(),!1">Click here to print</a>.</p><p><a href=/docs/reference/access-authn-authz/>Return to the regular view of this page</a>.</p></div><h1 class=title>API Access Control</h1><ul><li>1: <a href=#pg-a6264859a5ad6e2f4a6e4cff9ce4fa8b>Authenticating</a></li><li>2: <a href=#pg-de45b6ca7419a0e308044425b2ac52bb>Authenticating with Bootstrap Tokens</a></li><li>3: <a href=#pg-342be69d36f174f762c36f4fe11fcb20>Authorization</a></li><li>4: <a href=#pg-954776b47f2d90515f375623a0ce98e1>Using RBAC Authorization</a></li><li>5: <a href=#pg-9cbb97d4d9f08d67931a1baae4e6519c>Using Node Authorization</a></li><li>6: <a href=#pg-215c25173044b8f97e9b0494b0c7e53f>Webhook Mode</a></li><li>7: <a href=#pg-a5bdc757c01991e5e6ab1a82b90639ea>Using ABAC Authorization</a></li><li>8: <a href=#pg-518807b9b00bda46d7c7e6e0b17c18f8>Admission Controllers Reference</a></li><li>9: <a href=#pg-d04751f776f1faa6a82bbb7f0a200950>Dynamic Admission Control</a></li><li>10: <a href=#pg-bea207258f3576b8ec7444a20d498e1d>Managing Service Accounts</a></li><li>11: <a href=#pg-3d0c14d1e3cfade38febc343cd044c73>Certificates and Certificate Signing Requests</a></li><li>12: <a href=#pg-643e4cec52a8577e9454649bdaac84d0>Mapping PodSecurityPolicies to Pod Security Standards</a></li><li>13: <a href=#pg-36e1423f0b5caa8eafeb6f53c175d13c>Kubelet authentication/authorization</a></li><li>14: <a href=#pg-d17c42b1760f6d5c333fc91ca9b453f4>TLS bootstrapping</a></li><li>15: <a href=#pg-7b9fccf8215aea0edc5c97e72f1f72e4>Validating Admission Policy</a></li></ul><div class=content><p>For an introduction to how Kubernetes implements and controls API access, read <a href=/docs/concepts/security/controlling-access/>Controlling Access to the Kubernetes API</a>.</p><p>Reference documentation:</p><ul><li><a href=/docs/reference/access-authn-authz/authentication/>Authenticating</a><ul><li><a href=/docs/reference/access-authn-authz/bootstrap-tokens/>Authenticating with Bootstrap Tokens</a></li></ul></li><li><a href=/docs/reference/access-authn-authz/admission-controllers/>Admission Controllers</a><ul><li><a href=/docs/reference/access-authn-authz/extensible-admission-controllers/>Dynamic Admission Control</a></li></ul></li><li><a href=/docs/reference/access-authn-authz/authorization/>Authorization</a><ul><li><a href=/docs/reference/access-authn-authz/rbac/>Role Based Access Control</a></li><li><a href=/docs/reference/access-authn-authz/abac/>Attribute Based Access Control</a></li><li><a href=/docs/reference/access-authn-authz/node/>Node Authorization</a></li><li><a href=/docs/reference/access-authn-authz/webhook/>Webhook Authorization</a></li></ul></li><li><a href=/docs/reference/access-authn-authz/certificate-signing-requests/>Certificate Signing Requests</a><ul><li>including <a href=/docs/reference/access-authn-authz/certificate-signing-requests/#approval-rejection>CSR approval</a> and <a href=/docs/reference/access-authn-authz/certificate-signing-requests/#signing>certificate signing</a></li></ul></li><li>Service accounts<ul><li><a href=/docs/tasks/configure-pod-container/configure-service-account/>Developer guide</a></li><li><a href=/docs/reference/access-authn-authz/service-accounts-admin/>Administration</a></li></ul></li><li><a href=/docs/reference/access-authn-authz/kubelet-authn-authz/>Kubelet Authentication & Authorization</a><ul><li>including kubelet <a href=/docs/reference/access-authn-authz/kubelet-tls-bootstrapping/>TLS bootstrapping</a></li></ul></li></ul></div></div><div class=td-content style=page-break-before:always><h1 id=pg-a6264859a5ad6e2f4a6e4cff9ce4fa8b>1 - Authenticating</h1><p>This page provides an overview of authentication.</p><h2 id=users-in-kubernetes>Users in Kubernetes</h2><p>All Kubernetes clusters have two categories of users: service accounts managed by Kubernetes, and normal users.</p><p>It is assumed that a cluster-independent service manages normal users in the following ways:</p><ul><li>an administrator distributing private keys</li><li>a user store like Keystone or Google Accounts</li><li>a file with a list of usernames and passwords</li></ul><p>In this regard, <em>Kubernetes does not have objects which represent normal user accounts.</em> Normal users cannot be added to a cluster through an API call.</p><p>Even though a normal user cannot be added via an API call, any user that presents a valid certificate signed by the cluster's certificate authority (CA) is considered authenticated. In this configuration, Kubernetes determines the username from the common name field in the 'subject' of the cert (e.g., "/CN=bob"). From there, the role based access control (RBAC) sub-system would determine whether the user is authorized to perform a specific operation on a resource. For more details, refer to the normal users topic in <a href=/docs/reference/access-authn-authz/certificate-signing-requests/#normal-user>certificate request</a> for more details about this.</p><p>In contrast, service accounts are users managed by the Kubernetes API. They are bound to specific namespaces, and created automatically by the API server or manually through API calls. Service accounts are tied to a set of credentials stored as <code>Secrets</code>, which are mounted into pods allowing in-cluster processes to talk to the Kubernetes API.</p><p>API requests are tied to either a normal user or a service account, or are treated as <a href=#anonymous-requests>anonymous requests</a>. This means every process inside or outside the cluster, from a human user typing <code>kubectl</code> on a workstation, to <code>kubelets</code> on nodes, to members of the control plane, must authenticate when making requests to the API server, or be treated as an anonymous user.</p><h2 id=authentication-strategies>Authentication strategies</h2><p>Kubernetes uses client certificates, bearer tokens, or an authenticating proxy to authenticate API requests through authentication plugins. As HTTP requests are made to the API server, plugins attempt to associate the following attributes with the request:</p><ul><li>Username: a string which identifies the end user. Common values might be <code>kube-admin</code> or <code>jane@example.com</code>.</li><li>UID: a string which identifies the end user and attempts to be more consistent and unique than username.</li><li>Groups: a set of strings, each of which indicates the user's membership in a named logical collection of users. Common values might be <code>system:masters</code> or <code>devops-team</code>.</li><li>Extra fields: a map of strings to list of strings which holds additional information authorizers may find useful.</li></ul><p>All values are opaque to the authentication system and only hold significance when interpreted by an <a href=/docs/reference/access-authn-authz/authorization/>authorizer</a>.</p><p>You can enable multiple authentication methods at once. You should usually use at least two methods:</p><ul><li>service account tokens for service accounts</li><li>at least one other method for user authentication.</li></ul><p>When multiple authenticator modules are enabled, the first module to successfully authenticate the request short-circuits evaluation. The API server does not guarantee the order authenticators run in.</p><p>The <code>system:authenticated</code> group is included in the list of groups for all authenticated users.</p><p>Integrations with other authentication protocols (LDAP, SAML, Kerberos, alternate x509 schemes, etc) can be accomplished using an <a href=#authenticating-proxy>authenticating proxy</a> or the <a href=#webhook-token-authentication>authentication webhook</a>.</p><h3 id=x509-client-certificates>X509 client certificates</h3><p>Client certificate authentication is enabled by passing the <code>--client-ca-file=SOMEFILE</code> option to API server. The referenced file must contain one or more certificate authorities to use to validate client certificates presented to the API server. If a client certificate is presented and verified, the common name of the subject is used as the user name for the request. As of Kubernetes 1.4, client certificates can also indicate a user's group memberships using the certificate's organization fields. To include multiple group memberships for a user, include multiple organization fields in the certificate.</p><p>For example, using the <code>openssl</code> command line tool to generate a certificate signing request:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>openssl req -new -key jbeda.pem -out jbeda-csr.pem -subj <span style=color:#b44>"/CN=jbeda/O=app1/O=app2"</span> </span></span></code></pre></div><p>This would create a CSR for the username "jbeda", belonging to two groups, "app1" and "app2".</p><p>See <a href=/docs/tasks/administer-cluster/certificates/>Managing Certificates</a> for how to generate a client cert.</p><h3 id=static-token-file>Static token file</h3><p>The API server reads bearer tokens from a file when given the <code>--token-auth-file=SOMEFILE</code> option on the command line. Currently, tokens last indefinitely, and the token list cannot be changed without restarting the API server.</p><p>The token file is a csv file with a minimum of 3 columns: token, user name, user uid, followed by optional group names.</p><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4><p>If you have more than one group, the column must be double quoted e.g.</p><pre tabindex=0><code class=language-conf data-lang=conf>token,user,uid,"group1,group2,group3" </code></pre></div><h4 id=putting-a-bearer-token-in-a-request>Putting a bearer token in a request</h4><p>When using bearer token authentication from an http client, the API server expects an <code>Authorization</code> header with a value of <code>Bearer <token></code>. The bearer token must be a character sequence that can be put in an HTTP header value using no more than the encoding and quoting facilities of HTTP. For example: if the bearer token is <code>31ada4fd-adec-460c-809a-9e56ceb75269</code> then it would appear in an HTTP header as shown below.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-http data-lang=http><span style=display:flex><span><span>Authorization: Bearer 31ada4fd-adec-460c-809a-9e56ceb75269 </span></span></span></code></pre></div><h3 id=bootstrap-tokens>Bootstrap tokens</h3><div class="feature-state-notice feature-stable"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.18 [stable]</code></div><p>To allow for streamlined bootstrapping for new clusters, Kubernetes includes a dynamically-managed Bearer token type called a <em>Bootstrap Token</em>. These tokens are stored as Secrets in the <code>kube-system</code> namespace, where they can be dynamically managed and created. Controller Manager contains a TokenCleaner controller that deletes bootstrap tokens as they expire.</p><p>The tokens are of the form <code>[a-z0-9]{6}.[a-z0-9]{16}</code>. The first component is a Token ID and the second component is the Token Secret. You specify the token in an HTTP header as follows:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-http data-lang=http><span style=display:flex><span><span>Authorization: Bearer 781292.db7bc3a58fc5f07e </span></span></span></code></pre></div><p>You must enable the Bootstrap Token Authenticator with the <code>--enable-bootstrap-token-auth</code> flag on the API Server. You must enable the TokenCleaner controller via the <code>--controllers</code> flag on the Controller Manager. This is done with something like <code>--controllers=*,tokencleaner</code>. <code>kubeadm</code> will do this for you if you are using it to bootstrap a cluster.</p><p>The authenticator authenticates as <code>system:bootstrap:<Token ID></code>. It is included in the <code>system:bootstrappers</code> group. The naming and groups are intentionally limited to discourage users from using these tokens past bootstrapping. The user names and group can be used (and are used by <code>kubeadm</code>) to craft the appropriate authorization policies to support bootstrapping a cluster.</p><p>Please see <a href=/docs/reference/access-authn-authz/bootstrap-tokens/>Bootstrap Tokens</a> for in depth documentation on the Bootstrap Token authenticator and controllers along with how to manage these tokens with <code>kubeadm</code>.</p><h3 id=service-account-tokens>Service account tokens</h3><p>A service account is an automatically enabled authenticator that uses signed bearer tokens to verify requests. The plugin takes two optional flags:</p><ul><li><code>--service-account-key-file</code> File containing PEM-encoded x509 RSA or ECDSA private or public keys, used to verify ServiceAccount tokens. The specified file can contain multiple keys, and the flag can be specified multiple times with different files. If unspecified, --tls-private-key-file is used.</li><li><code>--service-account-lookup</code> If enabled, tokens which are deleted from the API will be revoked.</li></ul><p>Service accounts are usually created automatically by the API server and associated with pods running in the cluster through the <code>ServiceAccount</code> <a href=/docs/reference/access-authn-authz/admission-controllers/>Admission Controller</a>. Bearer tokens are mounted into pods at well-known locations, and allow in-cluster processes to talk to the API server. Accounts may be explicitly associated with pods using the <code>serviceAccountName</code> field of a <code>PodSpec</code>.</p><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4><code>serviceAccountName</code> is usually omitted because this is done automatically.</div><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>apps/v1<span style=color:#bbb> </span><span style=color:#080;font-style:italic># this apiVersion is relevant as of Kubernetes 1.9</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Deployment<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>nginx-deployment<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespace</span>:<span style=color:#bbb> </span>default<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>spec</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>replicas</span>:<span style=color:#bbb> </span><span style=color:#666>3</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>template</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># ...</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>spec</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>serviceAccountName</span>:<span style=color:#bbb> </span>bob-the-bot<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>containers</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>nginx<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>image</span>:<span style=color:#bbb> </span>nginx:1.14.2<span style=color:#bbb> </span></span></span></code></pre></div><p>Service account bearer tokens are perfectly valid to use outside the cluster and can be used to create identities for long standing jobs that wish to talk to the Kubernetes API. To manually create a service account, use the <code>kubectl create serviceaccount (NAME)</code> command. This creates a service account in the current namespace.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>kubectl create serviceaccount jenkins </span></span></code></pre></div><pre tabindex=0><code class=language-none data-lang=none>serviceaccount/jenkins created </code></pre><p>Create an associated token:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>kubectl create token jenkins </span></span></code></pre></div><pre tabindex=0><code class=language-none data-lang=none>eyJhbGciOiJSUzI1NiIsImtp... </code></pre><p>The created token is a signed JSON Web Token (JWT).</p><p>The signed JWT can be used as a bearer token to authenticate as the given service account. See <a href=#putting-a-bearer-token-in-a-request>above</a> for how the token is included in a request. Normally these tokens are mounted into pods for in-cluster access to the API server, but can be used from outside the cluster as well.</p><p>Service accounts authenticate with the username <code>system:serviceaccount:(NAMESPACE):(SERVICEACCOUNT)</code>, and are assigned to the groups <code>system:serviceaccounts</code> and <code>system:serviceaccounts:(NAMESPACE)</code>.</p><div class="alert alert-danger" role=alert><h4 class=alert-heading>Warning:</h4>Because service account tokens can also be stored in Secret API objects, any user with write access to Secrets can request a token, and any user with read access to those Secrets can authenticate as the service account. Be cautious when granting permissions to service accounts and read or write capabilities for Secrets.</div><h3 id=openid-connect-tokens>OpenID Connect Tokens</h3><p><a href=https://openid.net/connect/>OpenID Connect</a> is a flavor of OAuth2 supported by some OAuth2 providers, notably Microsoft Entra ID, Salesforce, and Google. The protocol's main extension of OAuth2 is an additional field returned with the access token called an <a href=https://openid.net/specs/openid-connect-core-1_0.html#IDToken>ID Token</a>. This token is a JSON Web Token (JWT) with well known fields, such as a user's email, signed by the server.</p><p>To identify the user, the authenticator uses the <code>id_token</code> (not the <code>access_token</code>) from the OAuth2 <a href=https://openid.net/specs/openid-connect-core-1_0.html#TokenResponse>token response</a> as a bearer token. See <a href=#putting-a-bearer-token-in-a-request>above</a> for how the token is included in a request.</p><figure><div class=mermaid>sequenceDiagram participant user as User participant idp as Identity Provider participant kube as kubectl participant api as API Server user ->> idp: 1. Log in to IdP activate idp idp -->> user: 2. Provide access_token,<br>id_token, and refresh_token deactivate idp activate user user ->> kube: 3. Call kubectl<br>with --token being the id_token<br>OR add tokens to .kube/config deactivate user activate kube kube ->> api: 4. Authorization: Bearer... deactivate kube activate api api ->> api: 5. Is JWT signature valid? api ->> api: 6. Has the JWT expired? (iat+exp) api ->> api: 7. User authorized? api -->> kube: 8. Authorized: Perform<br>action and return result deactivate api activate kube kube --x user: 9. Return result deactivate kube</div></figure><noscript><div class="alert alert-secondary callout" role=alert><em class=javascript-required>JavaScript must be <a href=https://www.enable-javascript.com/>enabled</a> to view this content</em></div></noscript><ol><li><p>Log in to your identity provider</p></li><li><p>Your identity provider will provide you with an <code>access_token</code>, <code>id_token</code> and a <code>refresh_token</code></p></li><li><p>When using <code>kubectl</code>, use your <code>id_token</code> with the <code>--token</code> flag or add it directly to your <code>kubeconfig</code></p></li><li><p><code>kubectl</code> sends your <code>id_token</code> in a header called Authorization to the API server</p></li><li><p>The API server will make sure the JWT signature is valid</p></li><li><p>Check to make sure the <code>id_token</code> hasn't expired</p><p>Perform claim and/or user validation if CEL expressions are configured with <code>AuthenticationConfiguration</code>.</p></li><li><p>Make sure the user is authorized</p></li><li><p>Once authorized the API server returns a response to <code>kubectl</code></p></li><li><p><code>kubectl</code> provides feedback to the user</p></li></ol><p>Since all of the data needed to validate who you are is in the <code>id_token</code>, Kubernetes doesn't need to "phone home" to the identity provider. In a model where every request is stateless this provides a very scalable solution for authentication. It does offer a few challenges:</p><ol><li>Kubernetes has no "web interface" to trigger the authentication process. There is no browser or interface to collect credentials which is why you need to authenticate to your identity provider first.</li><li>The <code>id_token</code> can't be revoked, it's like a certificate so it should be short-lived (only a few minutes) so it can be very annoying to have to get a new token every few minutes.</li><li>To authenticate to the Kubernetes dashboard, you must use the <code>kubectl proxy</code> command or a reverse proxy that injects the <code>id_token</code>.</li></ol><h4 id=configuring-the-api-server>Configuring the API Server</h4><h5 id=using-flags>Using flags</h5><p>To enable the plugin, configure the following flags on the API server:</p><table><thead><tr><th>Parameter</th><th>Description</th><th>Example</th><th>Required</th></tr></thead><tbody><tr><td><code>--oidc-issuer-url</code></td><td>URL of the provider that allows the API server to discover public signing keys. Only URLs that use the <code>https://</code> scheme are accepted. This is typically the provider's discovery URL, changed to have an empty path.</td><td>If the issuer's OIDC discovery URL is <code>https://accounts.provider.example/.well-known/openid-configuration</code>, the value should be <code>https://accounts.provider.example</code></td><td>Yes</td></tr><tr><td><code>--oidc-client-id</code></td><td>A client id that all tokens must be issued for.</td><td>kubernetes</td><td>Yes</td></tr><tr><td><code>--oidc-username-claim</code></td><td>JWT claim to use as the user name. By default <code>sub</code>, which is expected to be a unique identifier of the end user. Admins can choose other claims, such as <code>email</code> or <code>name</code>, depending on their provider. However, claims other than <code>email</code> will be prefixed with the issuer URL to prevent naming clashes with other plugins.</td><td>sub</td><td>No</td></tr><tr><td><code>--oidc-username-prefix</code></td><td>Prefix prepended to username claims to prevent clashes with existing names (such as <code>system:</code> users). For example, the value <code>oidc:</code> will create usernames like <code>oidc:jane.doe</code>. If this flag isn't provided and <code>--oidc-username-claim</code> is a value other than <code>email</code> the prefix defaults to <code>( Issuer URL )#</code> where <code>( Issuer URL )</code> is the value of <code>--oidc-issuer-url</code>. The value <code>-</code> can be used to disable all prefixing.</td><td><code>oidc:</code></td><td>No</td></tr><tr><td><code>--oidc-groups-claim</code></td><td>JWT claim to use as the user's group. If the claim is present it must be an array of strings.</td><td>groups</td><td>No</td></tr><tr><td><code>--oidc-groups-prefix</code></td><td>Prefix prepended to group claims to prevent clashes with existing names (such as <code>system:</code> groups). For example, the value <code>oidc:</code> will create group names like <code>oidc:engineering</code> and <code>oidc:infra</code>.</td><td><code>oidc:</code></td><td>No</td></tr><tr><td><code>--oidc-required-claim</code></td><td>A key=value pair that describes a required claim in the ID Token. If set, the claim is verified to be present in the ID Token with a matching value. Repeat this flag to specify multiple claims.</td><td><code>claim=value</code></td><td>No</td></tr><tr><td><code>--oidc-ca-file</code></td><td>The path to the certificate for the CA that signed your identity provider's web certificate. Defaults to the host's root CAs.</td><td><code>/etc/kubernetes/ssl/kc-ca.pem</code></td><td>No</td></tr><tr><td><code>--oidc-signing-algs</code></td><td>The signing algorithms accepted. Default is "RS256".</td><td><code>RS512</code></td><td>No</td></tr></tbody></table><h5 id=using-authentication-configuration>Authentication configuration from a file</h5><div class="feature-state-notice feature-beta" title="Feature Gate: StructuredAuthenticationConfiguration"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.30 [beta]</code> (enabled by default: true)</div><p>JWT Authenticator is an authenticator to authenticate Kubernetes users using JWT compliant tokens. The authenticator will attempt to parse a raw ID token, verify it's been signed by the configured issuer. The public key to verify the signature is discovered from the issuer's public endpoint using OIDC discovery.</p><p>The minimum valid JWT payload must contain the following claims:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"iss"</span>: <span style=color:#b44>"https://example.com"</span>, <span style=color:#080;font-style:italic>// must match the issuer.url </span></span></span><span style=display:flex><span><span style=color:#080;font-style:italic></span> <span style=color:green;font-weight:700>"aud"</span>: [<span style=color:#b44>"my-app"</span>], <span style=color:#080;font-style:italic>// at least one of the entries in issuer.audiences must match the "aud" claim in presented JWTs. </span></span></span><span style=display:flex><span><span style=color:#080;font-style:italic></span> <span style=color:green;font-weight:700>"exp"</span>: <span style=color:#666>1234567890</span>, <span style=color:#080;font-style:italic>// token expiration as Unix time (the number of seconds elapsed since January 1, 1970 UTC) </span></span></span><span style=display:flex><span><span style=color:#080;font-style:italic></span> <span style=color:green;font-weight:700>"<username-claim>"</span>: <span style=color:#b44>"user"</span> <span style=color:#080;font-style:italic>// this is the username claim configured in the claimMappings.username.claim or claimMappings.username.expression </span></span></span><span style=display:flex><span><span style=color:#080;font-style:italic></span>} </span></span></code></pre></div><p>The configuration file approach allows you to configure multiple JWT authenticators, each with a unique <code>issuer.url</code> and <code>issuer.discoveryURL</code>. The configuration file even allows you to specify <a href=/docs/reference/using-api/cel/>CEL</a> expressions to map claims to user attributes, and to validate claims and user information. The API server also automatically reloads the authenticators when the configuration file is modified. You can use <code>apiserver_authentication_config_controller_automatic_reload_last_timestamp_seconds</code> metric to monitor the last time the configuration was reloaded by the API server.</p><p>You must specify the path to the authentication configuration using the <code>--authentication-config</code> flag on the API server. If you want to use command line flags instead of the configuration file, those will continue to work as-is. To access the new capabilities like configuring multiple authenticators, setting multiple audiences for an issuer, switch to using the configuration file.</p><p>For Kubernetes v1.31, the structured authentication configuration file format is beta-level, and the mechanism for using that configuration is also beta. Provided you didn't specifically disable the <code>StructuredAuthenticationConfiguration</code> <a href=/docs/reference/command-line-tools-reference/feature-gates/>feature gate</a> for your cluster, you can turn on structured authentication by specifying the <code>--authentication-config</code> command line argument to the kube-apiserver. An example of the structured authentication configuration file is shown below.</p><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>If you specify <code>--authentication-config</code> along with any of the <code>--oidc-*</code> command line arguments, this is a misconfiguration. In this situation, the API server reports an error and then immediately exits. If you want to switch to using structured authentication configuration, you have to remove the <code>--oidc-*</code> command line arguments, and use the configuration file instead.</div><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:#00f;font-weight:700>---</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># CAUTION: this is an example configuration.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># Do not use this for your own cluster!</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>apiserver.config.k8s.io/v1beta1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>AuthenticationConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># list of authenticators to authenticate Kubernetes users using JWT compliant tokens.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># the maximum number of allowed authenticators is 64.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>jwt</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>issuer</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># url must be unique across all authenticators.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># url must not conflict with issuer configured in --service-account-issuer.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>url</span>:<span style=color:#bbb> </span>https://example.com<span style=color:#bbb> </span><span style=color:#080;font-style:italic># Same as --oidc-issuer-url.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># discoveryURL, if specified, overrides the URL used to fetch discovery</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># information instead of using "{url}/.well-known/openid-configuration".</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># The exact value specified is used, so "/.well-known/openid-configuration"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># must be included in discoveryURL if needed.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># The "issuer" field in the fetched discovery information must match the "issuer.url" field</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># in the AuthenticationConfiguration and will be used to validate the "iss" claim in the presented JWT.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># This is for scenarios where the well-known and jwks endpoints are hosted at a different</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># location than the issuer (such as locally in the cluster).</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># discoveryURL must be different from url if specified and must be unique across all authenticators.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>discoveryURL</span>:<span style=color:#bbb> </span>https://discovery.example.com/.well-known/openid-configuration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># PEM encoded CA certificates used to validate the connection when fetching</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># discovery information. If not set, the system verifier will be used.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Same value as the content of the file referenced by the --oidc-ca-file flag.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>certificateAuthority</span>:<span style=color:#bbb> </span><PEM encoded CA certificates> <span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># audiences is the set of acceptable audiences the JWT must be issued to.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># At least one of the entries must match the "aud" claim in presented JWTs.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>audiences</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- my-app<span style=color:#bbb> </span><span style=color:#080;font-style:italic># Same as --oidc-client-id.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- my-other-app<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># this is required to be set to "MatchAny" when multiple audiences are specified.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>audienceMatchPolicy</span>:<span style=color:#bbb> </span>MatchAny<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># rules applied to validate token claims to authenticate users.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>claimValidationRules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Same as --oidc-required-claim key=value.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>claim</span>:<span style=color:#bbb> </span>hd<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>requiredValue</span>:<span style=color:#bbb> </span>example.com<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Instead of claim and requiredValue, you can use expression to validate the claim.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># expression is a CEL expression that evaluates to a boolean.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># all the expressions must evaluate to true for validation to succeed.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>'claims.hd == "example.com"'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Message customizes the error message seen in the API server logs when the validation fails.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>message</span>:<span style=color:#bbb> </span>the hd claim must be set to example.com<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>'claims.exp - claims.nbf <= 86400'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>message</span>:<span style=color:#bbb> </span>total token lifetime must not exceed 24 hours<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>claimMappings</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># username represents an option for the username attribute.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># This is the only required attribute.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>username</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Same as --oidc-username-claim. Mutually exclusive with username.expression.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>claim</span>:<span style=color:#bbb> </span><span style=color:#b44>"sub"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Same as --oidc-username-prefix. Mutually exclusive with username.expression.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># if username.claim is set, username.prefix is required.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Explicitly set it to "" if no prefix is desired.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>prefix</span>:<span style=color:#bbb> </span><span style=color:#b44>""</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Mutually exclusive with username.claim and username.prefix.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># expression is a CEL expression that evaluates to a string.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># 1. If username.expression uses 'claims.email', then 'claims.email_verified' must be used in</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># username.expression or extra[*].valueExpression or claimValidationRules[*].expression.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># An example claim validation rule expression that matches the validation automatically</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># applied when username.claim is set to 'email' is 'claims.?email_verified.orValue(true)'.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># 2. If the username asserted based on username.expression is the empty string, the authentication</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># request will fail.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>'claims.username + ":external-user"'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># groups represents an option for the groups attribute.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>groups</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Same as --oidc-groups-claim. Mutually exclusive with groups.expression.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>claim</span>:<span style=color:#bbb> </span><span style=color:#b44>"sub"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Same as --oidc-groups-prefix. Mutually exclusive with groups.expression.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># if groups.claim is set, groups.prefix is required.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Explicitly set it to "" if no prefix is desired.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>prefix</span>:<span style=color:#bbb> </span><span style=color:#b44>""</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Mutually exclusive with groups.claim and groups.prefix.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># expression is a CEL expression that evaluates to a string or a list of strings.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>'claims.roles.split(",")'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># uid represents an option for the uid attribute.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>uid</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Mutually exclusive with uid.expression.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>claim</span>:<span style=color:#bbb> </span><span style=color:#b44>'sub'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Mutually exclusive with uid.claim</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># expression is a CEL expression that evaluates to a string.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>'claims.sub'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># extra attributes to be added to the UserInfo object. Keys must be domain-prefix path and must be unique.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>extra</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>key</span>:<span style=color:#bbb> </span><span style=color:#b44>'example.com/tenant'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># valueExpression is a CEL expression that evaluates to a string or a list of strings.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>valueExpression</span>:<span style=color:#bbb> </span><span style=color:#b44>'claims.tenant'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># validation rules applied to the final user object.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>userValidationRules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># expression is a CEL expression that evaluates to a boolean.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># all the expressions must evaluate to true for the user to be valid.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>"!user.username.startsWith('system:')"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Message customizes the error message seen in the API server logs when the validation fails.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>message: 'username cannot used reserved system</span>:<span style=color:#bbb> </span>prefix'<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>"user.groups.all(group, !group.startsWith('system:'))"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>message: 'groups cannot used reserved system</span>:<span style=color:#bbb> </span>prefix'<span style=color:#bbb> </span></span></span></code></pre></div><ul><li><p>Claim validation rule expression</p><p><code>jwt.claimValidationRules[i].expression</code> represents the expression which will be evaluated by CEL. CEL expressions have access to the contents of the token payload, organized into <code>claims</code> CEL variable. <code>claims</code> is a map of claim names (as strings) to claim values (of any type).</p></li><li><p>User validation rule expression</p><p><code>jwt.userValidationRules[i].expression</code> represents the expression which will be evaluated by CEL. CEL expressions have access to the contents of <code>userInfo</code>, organized into <code>user</code> CEL variable. Refer to the <a href=/docs/reference/generated/kubernetes-api/v1.31/#userinfo-v1-authentication-k8s-io>UserInfo</a> API documentation for the schema of <code>user</code>.</p></li><li><p>Claim mapping expression</p><p><code>jwt.claimMappings.username.expression</code>, <code>jwt.claimMappings.groups.expression</code>, <code>jwt.claimMappings.uid.expression</code> <code>jwt.claimMappings.extra[i].valueExpression</code> represents the expression which will be evaluated by CEL. CEL expressions have access to the contents of the token payload, organized into <code>claims</code> CEL variable. <code>claims</code> is a map of claim names (as strings) to claim values (of any type).</p><p>To learn more, see the <a href=/docs/reference/using-api/cel/>Documentation on CEL</a></p><p>Here are examples of the <code>AuthenticationConfiguration</code> with different token payloads.</p><ul class="nav nav-tabs" id=example-configuration role=tablist><li class=nav-item><a data-toggle=tab class="nav-link active" href=#example-configuration-0 role=tab aria-controls=example-configuration-0 aria-selected=true>Valid token</a></li><li class=nav-item><a data-toggle=tab class=nav-link href=#example-configuration-1 role=tab aria-controls=example-configuration-1>Fails claim validation</a></li><li class=nav-item><a data-toggle=tab class=nav-link href=#example-configuration-2 role=tab aria-controls=example-configuration-2>Fails user validation</a></li></ul><div class=tab-content id=example-configuration><div id=example-configuration-0 class="tab-pane show active" role=tabpanel aria-labelledby=example-configuration-0><p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>apiserver.config.k8s.io/v1beta1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>AuthenticationConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>jwt</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>issuer</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>url</span>:<span style=color:#bbb> </span>https://example.com<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>audiences</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- my-app<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>claimMappings</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>username</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>'claims.username + ":external-user"'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>groups</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>'claims.roles.split(",")'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>uid</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>'claims.sub'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>extra</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>key</span>:<span style=color:#bbb> </span><span style=color:#b44>'example.com/tenant'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>valueExpression</span>:<span style=color:#bbb> </span><span style=color:#b44>'claims.tenant'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>userValidationRules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>"!user.username.startsWith('system:')"</span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># the expression will evaluate to true, so validation will succeed.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>message: 'username cannot used reserved system</span>:<span style=color:#bbb> </span>prefix'<span style=color:#bbb> </span></span></span></code></pre></div><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span><span style=color:#b8860b>TOKEN</span><span style=color:#666>=</span>eyJhbGciOiJSUzI1NiIsImtpZCI6ImY3dF9tOEROWmFTQk1oWGw5QXZTWGhBUC04Y0JmZ0JVbFVpTG5oQkgxdXMiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJrdWJlcm5ldGVzIiwiZXhwIjoxNzAzMjMyOTQ5LCJpYXQiOjE3MDExMDcyMzMsImlzcyI6Imh0dHBzOi8vZXhhbXBsZS5jb20iLCJqdGkiOiI3YzMzNzk0MjgwN2U3M2NhYTJjMzBjODY4YWMwY2U5MTBiY2UwMmRkY2JmZWJlOGMyM2I4YjVmMjdhZDYyODczIiwibmJmIjoxNzAxMTA3MjMzLCJyb2xlcyI6InVzZXIsYWRtaW4iLCJzdWIiOiJhdXRoIiwidGVuYW50IjoiNzJmOTg4YmYtODZmMS00MWFmLTkxYWItMmQ3Y2QwMTFkYjRhIiwidXNlcm5hbWUiOiJmb28ifQ.TBWF2RkQHm4QQz85AYPcwLxSk-VLvQW-mNDHx7SEOSv9LVwcPYPuPajJpuQn9C_gKq1R94QKSQ5F6UgHMILz8OfmPKmX_00wpwwNVGeevJ79ieX2V-__W56iNR5gJ-i9nn6FYk5pwfVREB0l4HSlpTOmu80gbPWAXY5hLW0ZtcE1JTEEmefORHV2ge8e3jp1xGafNy6LdJWabYuKiw8d7Qga__HxtKB-t0kRMNzLRS7rka_SfQg0dSYektuxhLbiDkqhmRffGlQKXGVzUsuvFw7IGM5ZWnZgEMDzCI357obHeM3tRqpn5WRjtB8oM7JgnCymaJi-P3iCd88iu1xnzA </span></span></code></pre></div><p>where the token payload is:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span> { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"aud"</span>: <span style=color:#b44>"kubernetes"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"exp"</span>: <span style=color:#666>1703232949</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"iat"</span>: <span style=color:#666>1701107233</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"iss"</span>: <span style=color:#b44>"https://example.com"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"jti"</span>: <span style=color:#b44>"7c337942807e73caa2c30c868ac0ce910bce02ddcbfebe8c23b8b5f27ad62873"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"nbf"</span>: <span style=color:#666>1701107233</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"roles"</span>: <span style=color:#b44>"user,admin"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"sub"</span>: <span style=color:#b44>"auth"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"tenant"</span>: <span style=color:#b44>"72f988bf-86f1-41af-91ab-2d7cd011db4a"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"username"</span>: <span style=color:#b44>"foo"</span> </span></span><span style=display:flex><span> } </span></span></code></pre></div><p>The token with the above <code>AuthenticationConfiguration</code> will produce the following <code>UserInfo</code> object and successfully authenticate the user.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"username"</span>: <span style=color:#b44>"foo:external-user"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"uid"</span>: <span style=color:#b44>"auth"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"groups"</span>: [ </span></span><span style=display:flex><span> <span style=color:#b44>"user"</span>, </span></span><span style=display:flex><span> <span style=color:#b44>"admin"</span> </span></span><span style=display:flex><span> ], </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"extra"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"example.com/tenant"</span>: <span style=color:#b44>"72f988bf-86f1-41af-91ab-2d7cd011db4a"</span> </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div></div><div id=example-configuration-1 class=tab-pane role=tabpanel aria-labelledby=example-configuration-1><p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>apiserver.config.k8s.io/v1beta1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>AuthenticationConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>jwt</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>issuer</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>url</span>:<span style=color:#bbb> </span>https://example.com<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>audiences</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- my-app<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>claimValidationRules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>'claims.hd == "example.com"'</span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># the token below does not have this claim, so validation will fail.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>message</span>:<span style=color:#bbb> </span>the hd claim must be set to example.com<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>claimMappings</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>username</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>'claims.username + ":external-user"'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>groups</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>'claims.roles.split(",")'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>uid</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>'claims.sub'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>extra</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>key</span>:<span style=color:#bbb> </span><span style=color:#b44>'example.com/tenant'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>valueExpression</span>:<span style=color:#bbb> </span><span style=color:#b44>'claims.tenant'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>userValidationRules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>"!user.username.startsWith('system:')"</span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># the expression will evaluate to true, so validation will succeed.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>message: 'username cannot used reserved system</span>:<span style=color:#bbb> </span>prefix'<span style=color:#bbb> </span></span></span></code></pre></div><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span><span style=color:#b8860b>TOKEN</span><span style=color:#666>=</span>eyJhbGciOiJSUzI1NiIsImtpZCI6ImY3dF9tOEROWmFTQk1oWGw5QXZTWGhBUC04Y0JmZ0JVbFVpTG5oQkgxdXMiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJrdWJlcm5ldGVzIiwiZXhwIjoxNzAzMjMyOTQ5LCJpYXQiOjE3MDExMDcyMzMsImlzcyI6Imh0dHBzOi8vZXhhbXBsZS5jb20iLCJqdGkiOiI3YzMzNzk0MjgwN2U3M2NhYTJjMzBjODY4YWMwY2U5MTBiY2UwMmRkY2JmZWJlOGMyM2I4YjVmMjdhZDYyODczIiwibmJmIjoxNzAxMTA3MjMzLCJyb2xlcyI6InVzZXIsYWRtaW4iLCJzdWIiOiJhdXRoIiwidGVuYW50IjoiNzJmOTg4YmYtODZmMS00MWFmLTkxYWItMmQ3Y2QwMTFkYjRhIiwidXNlcm5hbWUiOiJmb28ifQ.TBWF2RkQHm4QQz85AYPcwLxSk-VLvQW-mNDHx7SEOSv9LVwcPYPuPajJpuQn9C_gKq1R94QKSQ5F6UgHMILz8OfmPKmX_00wpwwNVGeevJ79ieX2V-__W56iNR5gJ-i9nn6FYk5pwfVREB0l4HSlpTOmu80gbPWAXY5hLW0ZtcE1JTEEmefORHV2ge8e3jp1xGafNy6LdJWabYuKiw8d7Qga__HxtKB-t0kRMNzLRS7rka_SfQg0dSYektuxhLbiDkqhmRffGlQKXGVzUsuvFw7IGM5ZWnZgEMDzCI357obHeM3tRqpn5WRjtB8oM7JgnCymaJi-P3iCd88iu1xnzA </span></span></code></pre></div><p>where the token payload is:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span> { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"aud"</span>: <span style=color:#b44>"kubernetes"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"exp"</span>: <span style=color:#666>1703232949</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"iat"</span>: <span style=color:#666>1701107233</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"iss"</span>: <span style=color:#b44>"https://example.com"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"jti"</span>: <span style=color:#b44>"7c337942807e73caa2c30c868ac0ce910bce02ddcbfebe8c23b8b5f27ad62873"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"nbf"</span>: <span style=color:#666>1701107233</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"roles"</span>: <span style=color:#b44>"user,admin"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"sub"</span>: <span style=color:#b44>"auth"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"tenant"</span>: <span style=color:#b44>"72f988bf-86f1-41af-91ab-2d7cd011db4a"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"username"</span>: <span style=color:#b44>"foo"</span> </span></span><span style=display:flex><span> } </span></span></code></pre></div><p>The token with the above <code>AuthenticationConfiguration</code> will fail to authenticate because the <code>hd</code> claim is not set to <code>example.com</code>. The API server will return <code>401 Unauthorized</code> error.</p></div><div id=example-configuration-2 class=tab-pane role=tabpanel aria-labelledby=example-configuration-2><p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>apiserver.config.k8s.io/v1beta1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>AuthenticationConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>jwt</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>issuer</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>url</span>:<span style=color:#bbb> </span>https://example.com<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>audiences</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- my-app<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>claimValidationRules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>'claims.hd == "example.com"'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>message</span>:<span style=color:#bbb> </span>the hd claim must be set to example.com<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>claimMappings</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>username</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>'"system:" + claims.username'</span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># this will prefix the username with "system:" and will fail user validation.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>groups</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>'claims.roles.split(",")'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>uid</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>'claims.sub'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>extra</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>key</span>:<span style=color:#bbb> </span><span style=color:#b44>'example.com/tenant'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>valueExpression</span>:<span style=color:#bbb> </span><span style=color:#b44>'claims.tenant'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>userValidationRules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>"!user.username.startsWith('system:')"</span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># the username will be system:foo and expression will evaluate to false, so validation will fail.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>message: 'username cannot used reserved system</span>:<span style=color:#bbb> </span>prefix'<span style=color:#bbb> </span></span></span></code></pre></div><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span><span style=color:#b8860b>TOKEN</span><span style=color:#666>=</span>eyJhbGciOiJSUzI1NiIsImtpZCI6ImY3dF9tOEROWmFTQk1oWGw5QXZTWGhBUC04Y0JmZ0JVbFVpTG5oQkgxdXMiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJrdWJlcm5ldGVzIiwiZXhwIjoxNzAzMjMyOTQ5LCJoZCI6ImV4YW1wbGUuY29tIiwiaWF0IjoxNzAxMTEzMTAxLCJpc3MiOiJodHRwczovL2V4YW1wbGUuY29tIiwianRpIjoiYjViMDY1MjM3MmNkMjBlMzQ1YjZmZGZmY2RjMjE4MWY0YWZkNmYyNTlhYWI0YjdlMzU4ODEyMzdkMjkyMjBiYyIsIm5iZiI6MTcwMTExMzEwMSwicm9sZXMiOiJ1c2VyLGFkbWluIiwic3ViIjoiYXV0aCIsInRlbmFudCI6IjcyZjk4OGJmLTg2ZjEtNDFhZi05MWFiLTJkN2NkMDExZGI0YSIsInVzZXJuYW1lIjoiZm9vIn0.FgPJBYLobo9jnbHreooBlvpgEcSPWnKfX6dc0IvdlRB-F0dCcgy91oCJeK_aBk-8zH5AKUXoFTlInfLCkPivMOJqMECA1YTrMUwt_IVqwb116AqihfByUYIIqzMjvUbthtbpIeHQm2fF0HbrUqa_Q0uaYwgy8mD807h7sBcUMjNd215ff_nFIHss-9zegH8GI1d9fiBf-g6zjkR1j987EP748khpQh9IxPjMJbSgG_uH5x80YFuqgEWwq-aYJPQxXX6FatP96a2EAn7wfPpGlPRt0HcBOvq5pCnudgCgfVgiOJiLr_7robQu4T1bis0W75VPEvwWtgFcLnvcQx0JWg </span></span></code></pre></div><p>where the token payload is:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span> { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"aud"</span>: <span style=color:#b44>"kubernetes"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"exp"</span>: <span style=color:#666>1703232949</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"hd"</span>: <span style=color:#b44>"example.com"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"iat"</span>: <span style=color:#666>1701113101</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"iss"</span>: <span style=color:#b44>"https://example.com"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"jti"</span>: <span style=color:#b44>"b5b0652372cd20e345b6fdffcdc2181f4afd6f259aab4b7e35881237d29220bc"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"nbf"</span>: <span style=color:#666>1701113101</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"roles"</span>: <span style=color:#b44>"user,admin"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"sub"</span>: <span style=color:#b44>"auth"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"tenant"</span>: <span style=color:#b44>"72f988bf-86f1-41af-91ab-2d7cd011db4a"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"username"</span>: <span style=color:#b44>"foo"</span> </span></span><span style=display:flex><span> } </span></span></code></pre></div><p>The token with the above <code>AuthenticationConfiguration</code> will produce the following <code>UserInfo</code> object:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"username"</span>: <span style=color:#b44>"system:foo"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"uid"</span>: <span style=color:#b44>"auth"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"groups"</span>: [ </span></span><span style=display:flex><span> <span style=color:#b44>"user"</span>, </span></span><span style=display:flex><span> <span style=color:#b44>"admin"</span> </span></span><span style=display:flex><span> ], </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"extra"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"example.com/tenant"</span>: <span style=color:#b44>"72f988bf-86f1-41af-91ab-2d7cd011db4a"</span> </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div><p>which will fail user validation because the username starts with <code>system:</code>. The API server will return <code>401 Unauthorized</code> error.</p></div></div></li></ul><h6 id=limitations>Limitations</h6><ol><li>Distributed claims do not work via <a href=/docs/reference/using-api/cel/>CEL</a> expressions.</li><li>Egress selector configuration is not supported for calls to <code>issuer.url</code> and <code>issuer.discoveryURL</code>.</li></ol><p>Kubernetes does not provide an OpenID Connect Identity Provider. You can use an existing public OpenID Connect Identity Provider (such as Google, or <a href=https://connect2id.com/products/nimbus-oauth-openid-connect-sdk/openid-connect-providers>others</a>). Or, you can run your own Identity Provider, such as <a href=https://dexidp.io/>dex</a>, <a href=https://github.com/keycloak/keycloak>Keycloak</a>, CloudFoundry <a href=https://github.com/cloudfoundry/uaa>UAA</a>, or Tremolo Security's <a href=https://openunison.github.io/>OpenUnison</a>.</p><p>For an identity provider to work with Kubernetes it must:</p><ol><li><p>Support <a href=https://openid.net/specs/openid-connect-discovery-1_0.html>OpenID connect discovery</a></p><p>The public key to verify the signature is discovered from the issuer's public endpoint using OIDC discovery. If you're using the authentication configuration file, the identity provider doesn't need to publicly expose the discovery endpoint. You can host the discovery endpoint at a different location than the issuer (such as locally in the cluster) and specify the <code>issuer.discoveryURL</code> in the configuration file.</p></li><li><p>Run in TLS with non-obsolete ciphers</p></li><li><p>Have a CA signed certificate (even if the CA is not a commercial CA or is self signed)</p></li></ol><p>A note about requirement #3 above, requiring a CA signed certificate. If you deploy your own identity provider (as opposed to one of the cloud providers like Google or Microsoft) you MUST have your identity provider's web server certificate signed by a certificate with the <code>CA</code> flag set to <code>TRUE</code>, even if it is self signed. This is due to GoLang's TLS client implementation being very strict to the standards around certificate validation. If you don't have a CA handy, you can use the <a href=https://github.com/dexidp/dex/blob/master/examples/k8s/gencert.sh>gencert script</a> from the Dex team to create a simple CA and a signed certificate and key pair. Or you can use <a href=https://raw.githubusercontent.com/TremoloSecurity/openunison-qs-kubernetes/master/src/main/bash/makessl.sh>this similar script</a> that generates SHA256 certs with a longer life and larger key size.</p><p>Refer to setup instructions for specific systems:</p><ul><li><a href=https://docs.cloudfoundry.org/concepts/architecture/uaa.html>UAA</a></li><li><a href=https://dexidp.io/docs/kubernetes/>Dex</a></li><li><a href=https://www.tremolosecurity.com/orchestra-k8s/>OpenUnison</a></li></ul><h4 id=using-kubectl>Using kubectl</h4><h5 id=option-1-oidc-authenticator>Option 1 - OIDC Authenticator</h5><p>The first option is to use the kubectl <code>oidc</code> authenticator, which sets the <code>id_token</code> as a bearer token for all requests and refreshes the token once it expires. After you've logged into your provider, use kubectl to add your <code>id_token</code>, <code>refresh_token</code>, <code>client_id</code>, and <code>client_secret</code> to configure the plugin.</p><p>Providers that don't return an <code>id_token</code> as part of their refresh token response aren't supported by this plugin and should use "Option 2" below.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>kubectl config set-credentials USER_NAME <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --auth-provider<span style=color:#666>=</span>oidc <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --auth-provider-arg<span style=color:#666>=</span>idp-issuer-url<span style=color:#666>=(</span> issuer url <span style=color:#666>)</span> <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --auth-provider-arg<span style=color:#666>=</span>client-id<span style=color:#666>=(</span> your client id <span style=color:#666>)</span> <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --auth-provider-arg<span style=color:#666>=</span>client-secret<span style=color:#666>=(</span> your client secret <span style=color:#666>)</span> <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --auth-provider-arg<span style=color:#666>=</span>refresh-token<span style=color:#666>=(</span> your refresh token <span style=color:#666>)</span> <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --auth-provider-arg<span style=color:#666>=</span>idp-certificate-authority<span style=color:#666>=(</span> path to your ca certificate <span style=color:#666>)</span> <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --auth-provider-arg<span style=color:#666>=</span>id-token<span style=color:#666>=(</span> your id_token <span style=color:#666>)</span> </span></span></code></pre></div><p>As an example, running the below command after authenticating to your identity provider:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>kubectl config set-credentials mmosley <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --auth-provider<span style=color:#666>=</span>oidc <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --auth-provider-arg<span style=color:#666>=</span>idp-issuer-url<span style=color:#666>=</span>https://oidcidp.tremolo.lan:8443/auth/idp/OidcIdP <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --auth-provider-arg<span style=color:#666>=</span>client-id<span style=color:#666>=</span>kubernetes <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --auth-provider-arg<span style=color:#666>=</span>client-secret<span style=color:#666>=</span>1db158f6-177d-4d9c-8a8b-d36869918ec5 <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --auth-provider-arg<span style=color:#666>=</span>refresh-token<span style=color:#666>=</span>q1bKLFOyUiosTfawzA93TzZIDzH2TNa2SMm0zEiPKTUwME6BkEo6Sql5yUWVBSWpKUGphaWpxSVAfekBOZbBhaEW+VlFUeVRGcluyVF5JT4+haZmPsluFoFu5XkpXk5BXqHega4GAXlF+ma+vmYpFcHe5eZR+slBFpZKtQA<span style=color:#666>=</span> <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --auth-provider-arg<span style=color:#666>=</span>idp-certificate-authority<span style=color:#666>=</span>/root/ca.pem <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --auth-provider-arg<span style=color:#666>=</span>id-token<span style=color:#666>=</span>eyJraWQiOiJDTj1vaWRjaWRwLnRyZW1vbG8ubGFuLCBPVT1EZW1vLCBPPVRybWVvbG8gU2VjdXJpdHksIEw9QXJsaW5ndG9uLCBTVD1WaXJnaW5pYSwgQz1VUy1DTj1rdWJlLWNhLTEyMDIxNDc5MjEwMzYwNzMyMTUyIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL29pZGNpZHAudHJlbW9sby5sYW46ODQ0My9hdXRoL2lkcC9PaWRjSWRQIiwiYXVkIjoia3ViZXJuZXRlcyIsImV4cCI6MTQ4MzU0OTUxMSwianRpIjoiMm96US15TXdFcHV4WDlHZUhQdy1hZyIsImlhdCI6MTQ4MzU0OTQ1MSwibmJmIjoxNDgzNTQ5MzMxLCJzdWIiOiI0YWViMzdiYS1iNjQ1LTQ4ZmQtYWIzMC0xYTAxZWU0MWUyMTgifQ.w6p4J_6qQ1HzTG9nrEOrubxIMb9K5hzcMPxc9IxPx2K4xO9l-oFiUw93daH3m5pluP6K7eOE6txBuRVfEcpJSwlelsOsW8gb8VJcnzMS9EnZpeA0tW_p-mnkFc3VcfyXuhe5R3G7aa5d8uHv70yJ9Y3-UhjiN9EhpMdfPAoEB9fYKKkJRzF7utTTIPGrSaSU6d2pcpfYKaxIwePzEkT4DfcQthoZdy9ucNvvLoi1DIC-UocFD8HLs8LYKEqSxQvOcvnThbObJ9af71EwmuE21fO5KzMW20KtAeget1gnldOosPtz1G5EwvaQ401-RPQzPGMVBld0_zMCAwZttJ4knw </span></span></code></pre></div><p>Which would produce the below configuration:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>users</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>mmosley<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>user</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>auth-provider</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>config</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>client-id</span>:<span style=color:#bbb> </span>kubernetes<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>client-secret</span>:<span style=color:#bbb> </span>1db158f6-177d-4d9c-8a8b-d36869918ec5<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>id-token</span>:<span style=color:#bbb> </span>eyJraWQiOiJDTj1vaWRjaWRwLnRyZW1vbG8ubGFuLCBPVT1EZW1vLCBPPVRybWVvbG8gU2VjdXJpdHksIEw9QXJsaW5ndG9uLCBTVD1WaXJnaW5pYSwgQz1VUy1DTj1rdWJlLWNhLTEyMDIxNDc5MjEwMzYwNzMyMTUyIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL29pZGNpZHAudHJlbW9sby5sYW46ODQ0My9hdXRoL2lkcC9PaWRjSWRQIiwiYXVkIjoia3ViZXJuZXRlcyIsImV4cCI6MTQ4MzU0OTUxMSwianRpIjoiMm96US15TXdFcHV4WDlHZUhQdy1hZyIsImlhdCI6MTQ4MzU0OTQ1MSwibmJmIjoxNDgzNTQ5MzMxLCJzdWIiOiI0YWViMzdiYS1iNjQ1LTQ4ZmQtYWIzMC0xYTAxZWU0MWUyMTgifQ.w6p4J_6qQ1HzTG9nrEOrubxIMb9K5hzcMPxc9IxPx2K4xO9l-oFiUw93daH3m5pluP6K7eOE6txBuRVfEcpJSwlelsOsW8gb8VJcnzMS9EnZpeA0tW_p-mnkFc3VcfyXuhe5R3G7aa5d8uHv70yJ9Y3-UhjiN9EhpMdfPAoEB9fYKKkJRzF7utTTIPGrSaSU6d2pcpfYKaxIwePzEkT4DfcQthoZdy9ucNvvLoi1DIC-UocFD8HLs8LYKEqSxQvOcvnThbObJ9af71EwmuE21fO5KzMW20KtAeget1gnldOosPtz1G5EwvaQ401-RPQzPGMVBld0_zMCAwZttJ4knw<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>idp-certificate-authority</span>:<span style=color:#bbb> </span>/root/ca.pem<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>idp-issuer-url</span>:<span style=color:#bbb> </span>https://oidcidp.tremolo.lan:8443/auth/idp/OidcIdP<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>refresh-token</span>:<span style=color:#bbb> </span>q1bKLFOyUiosTfawzA93TzZIDzH2TNa2SMm0zEiPKTUwME6BkEo6Sql5yUWVBSWpKUGphaWpxSVAfekBOZbBhaEW+VlFUeVRGcluyVF5JT4+haZmPsluFoFu5XkpXk5BXq<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>oidc<span style=color:#bbb> </span></span></span></code></pre></div><p>Once your <code>id_token</code> expires, <code>kubectl</code> will attempt to refresh your <code>id_token</code> using your <code>refresh_token</code> and <code>client_secret</code> storing the new values for the <code>refresh_token</code> and <code>id_token</code> in your <code>.kube/config</code>.</p><h5 id=option-2-use-the-token-option>Option 2 - Use the <code>--token</code> Option</h5><p>The <code>kubectl</code> command lets you pass in a token using the <code>--token</code> option. Copy and paste the <code>id_token</code> into this option:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>kubectl --token<span style=color:#666>=</span>eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL21sYi50cmVtb2xvLmxhbjo4MDQzL2F1dGgvaWRwL29pZGMiLCJhdWQiOiJrdWJlcm5ldGVzIiwiZXhwIjoxNDc0NTk2NjY5LCJqdGkiOiI2RDUzNXoxUEpFNjJOR3QxaWVyYm9RIiwiaWF0IjoxNDc0NTk2MzY5LCJuYmYiOjE0NzQ1OTYyNDksInN1YiI6Im13aW5kdSIsInVzZXJfcm9sZSI6WyJ1c2VycyIsIm5ldy1uYW1lc3BhY2Utdmlld2VyIl0sImVtYWlsIjoibXdpbmR1QG5vbW9yZWplZGkuY29tIn0.f2As579n9VNoaKzoF-dOQGmXkFKf1FMyNV0-va_B63jn-_n9LGSCca_6IVMP8pO-Zb4KvRqGyTP0r3HkHxYy5c81AnIh8ijarruczl-TK_yF5akjSTHFZD-0gRzlevBDiH8Q79NAr-ky0P4iIXS8lY9Vnjch5MF74Zx0c3alKJHJUnnpjIACByfF2SCaYzbWFMUNat-K1PaUk5-ujMBG7yYnr95xD-63n8CO8teGUAAEMx6zRjzfhnhbzX-ajwZLGwGUBT4WqjMs70-6a7_8gZmLZb2az1cZynkFRj2BaCkVT3A2RrjeEwZEtGXlMqKJ1_I2ulrOVsYx01_yD35-rw get nodes </span></span></code></pre></div><h3 id=webhook-token-authentication>Webhook Token Authentication</h3><p>Webhook authentication is a hook for verifying bearer tokens.</p><ul><li><code>--authentication-token-webhook-config-file</code> a configuration file describing how to access the remote webhook service.</li><li><code>--authentication-token-webhook-cache-ttl</code> how long to cache authentication decisions. Defaults to two minutes.</li><li><code>--authentication-token-webhook-version</code> determines whether to use <code>authentication.k8s.io/v1beta1</code> or <code>authentication.k8s.io/v1</code> <code>TokenReview</code> objects to send/receive information from the webhook. Defaults to <code>v1beta1</code>.</li></ul><p>The configuration file uses the <a href=/docs/concepts/configuration/organize-cluster-access-kubeconfig/>kubeconfig</a> file format. Within the file, <code>clusters</code> refers to the remote service and <code>users</code> refers to the API server webhook. An example would be:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:#080;font-style:italic># Kubernetes API version</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># kind of the API object</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Config<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># clusters refers to the remote service.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>clusters</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>name-of-remote-authn-service<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>cluster</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>certificate-authority</span>:<span style=color:#bbb> </span>/path/to/ca.pem <span style=color:#bbb> </span><span style=color:#080;font-style:italic># CA for verifying the remote service.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>server</span>:<span style=color:#bbb> </span>https://authn.example.com/authenticate<span style=color:#bbb> </span><span style=color:#080;font-style:italic># URL of remote service to query. 'https' recommended for production.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># users refers to the API server's webhook configuration.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>users</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>name-of-api-server<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>user</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>client-certificate</span>:<span style=color:#bbb> </span>/path/to/cert.pem<span style=color:#bbb> </span><span style=color:#080;font-style:italic># cert for the webhook plugin to use</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>client-key</span>:<span style=color:#bbb> </span>/path/to/key.pem <span style=color:#bbb> </span><span style=color:#080;font-style:italic># key matching the cert</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># kubeconfig files require a context. Provide one for the API server.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>current-context</span>:<span style=color:#bbb> </span>webhook<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>contexts</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>context</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>cluster</span>:<span style=color:#bbb> </span>name-of-remote-authn-service<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>user</span>:<span style=color:#bbb> </span>name-of-api-server<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>webhook<span style=color:#bbb> </span></span></span></code></pre></div><p>When a client attempts to authenticate with the API server using a bearer token as discussed <a href=#putting-a-bearer-token-in-a-request>above</a>, the authentication webhook POSTs a JSON-serialized <code>TokenReview</code> object containing the token to the remote service.</p><p>Note that webhook API objects are subject to the same <a href=/docs/concepts/overview/kubernetes-api/>versioning compatibility rules</a> as other Kubernetes API objects. Implementers should check the <code>apiVersion</code> field of the request to ensure correct deserialization, and <strong>must</strong> respond with a <code>TokenReview</code> object of the same version as the request.</p><ul class="nav nav-tabs" id=tokenreview-request role=tablist><li class=nav-item><a data-toggle=tab class="nav-link active" href=#tokenreview-request-0 role=tab aria-controls=tokenreview-request-0 aria-selected=true>authentication.k8s.io/v1</a></li><li class=nav-item><a data-toggle=tab class=nav-link href=#tokenreview-request-1 role=tab aria-controls=tokenreview-request-1>authentication.k8s.io/v1beta1</a></li></ul><div class=tab-content id=tokenreview-request><div id=tokenreview-request-0 class="tab-pane show active" role=tabpanel aria-labelledby=tokenreview-request-0><p><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>The Kubernetes API server defaults to sending <code>authentication.k8s.io/v1beta1</code> token reviews for backwards compatibility. To opt into receiving <code>authentication.k8s.io/v1</code> token reviews, the API server must be started with <code>--authentication-token-webhook-version=v1</code>.</div><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"apiVersion": </span><span style=color:#b44>"authentication.k8s.io/v1"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"kind": </span><span style=color:#b44>"TokenReview"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"spec": </span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Opaque bearer token sent to the API server</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"token": </span><span style=color:#b44>"014fbff9a07c..."</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Optional list of the audience identifiers for the server the token was presented to.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Audience-aware token authenticators (for example, OIDC token authenticators)</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># should verify the token was intended for at least one of the audiences in this list,</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># and return the intersection of this list and the valid audiences for the token in the response status.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># This ensures the token is valid to authenticate to the server it was presented to.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># If no audiences are provided, the token should be validated to authenticate to the Kubernetes API server.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"audiences": </span>[<span style=color:#b44>"https://myserver.example.com"</span>,<span style=color:#bbb> </span><span style=color:#b44>"https://myserver.internal.example.com"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>}<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>}<span style=color:#bbb> </span></span></span></code></pre></div></div><div id=tokenreview-request-1 class=tab-pane role=tabpanel aria-labelledby=tokenreview-request-1><p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"apiVersion": </span><span style=color:#b44>"authentication.k8s.io/v1beta1"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"kind": </span><span style=color:#b44>"TokenReview"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"spec": </span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Opaque bearer token sent to the API server</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"token": </span><span style=color:#b44>"014fbff9a07c..."</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Optional list of the audience identifiers for the server the token was presented to.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Audience-aware token authenticators (for example, OIDC token authenticators)</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># should verify the token was intended for at least one of the audiences in this list,</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># and return the intersection of this list and the valid audiences for the token in the response status.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># This ensures the token is valid to authenticate to the server it was presented to.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># If no audiences are provided, the token should be validated to authenticate to the Kubernetes API server.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"audiences": </span>[<span style=color:#b44>"https://myserver.example.com"</span>,<span style=color:#bbb> </span><span style=color:#b44>"https://myserver.internal.example.com"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>}<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>}<span style=color:#bbb> </span></span></span></code></pre></div></div></div><p>The remote service is expected to fill the <code>status</code> field of the request to indicate the success of the login. The response body's <code>spec</code> field is ignored and may be omitted. The remote service must return a response using the same <code>TokenReview</code> API version that it received. A successful validation of the bearer token would return:</p><ul class="nav nav-tabs" id=tokenreview-response-success role=tablist><li class=nav-item><a data-toggle=tab class="nav-link active" href=#tokenreview-response-success-0 role=tab aria-controls=tokenreview-response-success-0 aria-selected=true>authentication.k8s.io/v1</a></li><li class=nav-item><a data-toggle=tab class=nav-link href=#tokenreview-response-success-1 role=tab aria-controls=tokenreview-response-success-1>authentication.k8s.io/v1beta1</a></li></ul><div class=tab-content id=tokenreview-response-success><div id=tokenreview-response-success-0 class="tab-pane show active" role=tabpanel aria-labelledby=tokenreview-response-success-0><p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"apiVersion": </span><span style=color:#b44>"authentication.k8s.io/v1"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"kind": </span><span style=color:#b44>"TokenReview"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"status": </span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"authenticated": </span><span style=color:#a2f;font-weight:700>true</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"user": </span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Required</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"username": </span><span style=color:#b44>"janedoe@example.com"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Optional</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"uid": </span><span style=color:#b44>"42"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Optional group memberships</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"groups": </span>[<span style=color:#b44>"developers"</span>,<span style=color:#bbb> </span><span style=color:#b44>"qa"</span>],<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Optional additional information provided by the authenticator.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># This should not contain confidential data, as it can be recorded in logs</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># or API objects, and is made available to admission webhooks.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"extra": </span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"extrafield1": </span>[<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#b44>"extravalue1"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#b44>"extravalue2"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>}<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>},<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Optional list audience-aware token authenticators can return,</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># containing the audiences from the `spec.audiences` list for which the provided token was valid.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># If this is omitted, the token is considered to be valid to authenticate to the Kubernetes API server.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"audiences": </span>[<span style=color:#b44>"https://myserver.example.com"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>}<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>}<span style=color:#bbb> </span></span></span></code></pre></div></div><div id=tokenreview-response-success-1 class=tab-pane role=tabpanel aria-labelledby=tokenreview-response-success-1><p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"apiVersion": </span><span style=color:#b44>"authentication.k8s.io/v1beta1"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"kind": </span><span style=color:#b44>"TokenReview"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"status": </span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"authenticated": </span><span style=color:#a2f;font-weight:700>true</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"user": </span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Required</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"username": </span><span style=color:#b44>"janedoe@example.com"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Optional</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"uid": </span><span style=color:#b44>"42"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Optional group memberships</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"groups": </span>[<span style=color:#b44>"developers"</span>,<span style=color:#bbb> </span><span style=color:#b44>"qa"</span>],<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Optional additional information provided by the authenticator.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># This should not contain confidential data, as it can be recorded in logs</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># or API objects, and is made available to admission webhooks.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"extra": </span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"extrafield1": </span>[<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#b44>"extravalue1"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#b44>"extravalue2"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>}<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>},<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Optional list audience-aware token authenticators can return,</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># containing the audiences from the `spec.audiences` list for which the provided token was valid.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># If this is omitted, the token is considered to be valid to authenticate to the Kubernetes API server.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"audiences": </span>[<span style=color:#b44>"https://myserver.example.com"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>}<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>}<span style=color:#bbb> </span></span></span></code></pre></div></div></div><p>An unsuccessful request would return:</p><ul class="nav nav-tabs" id=tokenreview-response-error role=tablist><li class=nav-item><a data-toggle=tab class="nav-link active" href=#tokenreview-response-error-0 role=tab aria-controls=tokenreview-response-error-0 aria-selected=true>authentication.k8s.io/v1</a></li><li class=nav-item><a data-toggle=tab class=nav-link href=#tokenreview-response-error-1 role=tab aria-controls=tokenreview-response-error-1>authentication.k8s.io/v1beta1</a></li></ul><div class=tab-content id=tokenreview-response-error><div id=tokenreview-response-error-0 class="tab-pane show active" role=tabpanel aria-labelledby=tokenreview-response-error-0><p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"apiVersion": </span><span style=color:#b44>"authentication.k8s.io/v1"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"kind": </span><span style=color:#b44>"TokenReview"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"status": </span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"authenticated": </span><span style=color:#a2f;font-weight:700>false</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Optionally include details about why authentication failed.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># If no error is provided, the API will return a generic Unauthorized message.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># The error field is ignored when authenticated=true.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"error": </span><span style=color:#b44>"Credentials are expired"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>}<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>}<span style=color:#bbb> </span></span></span></code></pre></div></div><div id=tokenreview-response-error-1 class=tab-pane role=tabpanel aria-labelledby=tokenreview-response-error-1><p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"apiVersion": </span><span style=color:#b44>"authentication.k8s.io/v1beta1"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"kind": </span><span style=color:#b44>"TokenReview"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"status": </span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"authenticated": </span><span style=color:#a2f;font-weight:700>false</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Optionally include details about why authentication failed.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># If no error is provided, the API will return a generic Unauthorized message.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># The error field is ignored when authenticated=true.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"error": </span><span style=color:#b44>"Credentials are expired"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>}<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>}<span style=color:#bbb> </span></span></span></code></pre></div></div></div><h3 id=authenticating-proxy>Authenticating Proxy</h3><p>The API server can be configured to identify users from request header values, such as <code>X-Remote-User</code>. It is designed for use in combination with an authenticating proxy, which sets the request header value.</p><ul><li><code>--requestheader-username-headers</code> Required, case-insensitive. Header names to check, in order, for the user identity. The first header containing a value is used as the username.</li><li><code>--requestheader-group-headers</code> 1.6+. Optional, case-insensitive. "X-Remote-Group" is suggested. Header names to check, in order, for the user's groups. All values in all specified headers are used as group names.</li><li><code>--requestheader-extra-headers-prefix</code> 1.6+. Optional, case-insensitive. "X-Remote-Extra-" is suggested. Header prefixes to look for to determine extra information about the user (typically used by the configured authorization plugin). Any headers beginning with any of the specified prefixes have the prefix removed. The remainder of the header name is lowercased and <a href=https://tools.ietf.org/html/rfc3986#section-2.1>percent-decoded</a> and becomes the extra key, and the header value is the extra value.</li></ul><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>Prior to 1.11.3 (and 1.10.7, 1.9.11), the extra key could only contain characters which were <a href=https://tools.ietf.org/html/rfc7230#section-3.2.6>legal in HTTP header labels</a>.</div><p>For example, with this configuration:</p><pre tabindex=0><code>--requestheader-username-headers=X-Remote-User --requestheader-group-headers=X-Remote-Group --requestheader-extra-headers-prefix=X-Remote-Extra- </code></pre><p>this request:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-http data-lang=http><span style=display:flex><span><span style=color:#00a000>GET</span> <span style=color:#00f;font-weight:700>/</span> <span style=color:#a2f;font-weight:700>HTTP</span><span style=color:#666>/</span><span style=color:#666>1.1</span> </span></span><span style=display:flex><span>X-Remote-User<span style=color:#666>:</span> fido </span></span><span style=display:flex><span>X-Remote-Group<span style=color:#666>:</span> dogs </span></span><span style=display:flex><span>X-Remote-Group<span style=color:#666>:</span> dachshunds </span></span><span style=display:flex><span>X-Remote-Extra-Acme.com%2Fproject<span style=color:#666>:</span> some-project </span></span><span style=display:flex><span>X-Remote-Extra-Scopes<span style=color:#666>:</span> openid </span></span><span style=display:flex><span>X-Remote-Extra-Scopes<span style=color:#666>:</span> profile </span></span></code></pre></div><p>would result in this user info:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>fido<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>groups</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- dogs<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- dachshunds<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>extra</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>acme.com/project</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- some-project<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>scopes</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- openid<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- profile<span style=color:#bbb> </span></span></span></code></pre></div><p>In order to prevent header spoofing, the authenticating proxy is required to present a valid client certificate to the API server for validation against the specified CA before the request headers are checked. WARNING: do <strong>not</strong> reuse a CA that is used in a different context unless you understand the risks and the mechanisms to protect the CA's usage.</p><ul><li><code>--requestheader-client-ca-file</code> Required. PEM-encoded certificate bundle. A valid client certificate must be presented and validated against the certificate authorities in the specified file before the request headers are checked for user names.</li><li><code>--requestheader-allowed-names</code> Optional. List of Common Name values (CNs). If set, a valid client certificate with a CN in the specified list must be presented before the request headers are checked for user names. If empty, any CN is allowed.</li></ul><h2 id=anonymous-requests>Anonymous requests</h2><p>When enabled, requests that are not rejected by other configured authentication methods are treated as anonymous requests, and given a username of <code>system:anonymous</code> and a group of <code>system:unauthenticated</code>.</p><p>For example, on a server with token authentication configured, and anonymous access enabled, a request providing an invalid bearer token would receive a <code>401 Unauthorized</code> error. A request providing no bearer token would be treated as an anonymous request.</p><p>In 1.5.1-1.5.x, anonymous access is disabled by default, and can be enabled by passing the <code>--anonymous-auth=true</code> option to the API server.</p><p>In 1.6+, anonymous access is enabled by default if an authorization mode other than <code>AlwaysAllow</code> is used, and can be disabled by passing the <code>--anonymous-auth=false</code> option to the API server. Starting in 1.6, the ABAC and RBAC authorizers require explicit authorization of the <code>system:anonymous</code> user or the <code>system:unauthenticated</code> group, so legacy policy rules that grant access to the <code>*</code> user or <code>*</code> group do not include anonymous users.</p><h3 id=anonymous-authenticator-configuration>Anonymous Authenticator Configuration</h3><div class="feature-state-notice feature-alpha" title="Feature Gate: AnonymousAuthConfigurableEndpoints"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.31 [alpha]</code> (enabled by default: false)</div><p>The <code>AuthenticationConfiguration</code> can be used to configure the anonymous authenticator. To enable configuring anonymous auth via the config file you need enable the <code>AnonymousAuthConfigurableEndpoints</code> feature gate. When this feature gate is enabled you cannot set the <code>--anonymous-auth</code> flag.</p><p>The main advantage of configuring anonymous authenticator using the authentication configuration file is that in addition to enabling and disabling anonymous authentication you can also configure which endpoints support anonymous authentication.</p><p>A sample authentication configuration file is below:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:#00f;font-weight:700>---</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># CAUTION: this is an example configuration.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># Do not use this for your own cluster!</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>apiserver.config.k8s.io/v1beta1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>AuthenticationConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>anonymous</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>enabled</span>:<span style=color:#bbb> </span><span style=color:#a2f;font-weight:700>true</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>conditions</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>path</span>:<span style=color:#bbb> </span>/livez<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>path</span>:<span style=color:#bbb> </span>/readyz<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>path</span>:<span style=color:#bbb> </span>/healthz<span style=color:#bbb> </span></span></span></code></pre></div><p>In the configuration above only the <code>/livez</code>, <code>/readyz</code> and <code>/healthz</code> endpoints are reachable by anonymous requests. Any other endpoints will not be reachable even if it is allowed by RBAC configuration.</p><h2 id=user-impersonation>User impersonation</h2><p>A user can act as another user through impersonation headers. These let requests manually override the user info a request authenticates as. For example, an admin could use this feature to debug an authorization policy by temporarily impersonating another user and seeing if a request was denied.</p><p>Impersonation requests first authenticate as the requesting user, then switch to the impersonated user info.</p><ul><li>A user makes an API call with their credentials <em>and</em> impersonation headers.</li><li>API server authenticates the user.</li><li>API server ensures the authenticated users have impersonation privileges.</li><li>Request user info is replaced with impersonation values.</li><li>Request is evaluated, authorization acts on impersonated user info.</li></ul><p>The following HTTP headers can be used to performing an impersonation request:</p><ul><li><code>Impersonate-User</code>: The username to act as.</li><li><code>Impersonate-Group</code>: A group name to act as. Can be provided multiple times to set multiple groups. Optional. Requires "Impersonate-User".</li><li><code>Impersonate-Extra-( extra name )</code>: A dynamic header used to associate extra fields with the user. Optional. Requires "Impersonate-User". In order to be preserved consistently, <code>( extra name )</code> must be lower-case, and any characters which aren't <a href=https://tools.ietf.org/html/rfc7230#section-3.2.6>legal in HTTP header labels</a> MUST be utf8 and <a href=https://tools.ietf.org/html/rfc3986#section-2.1>percent-encoded</a>.</li><li><code>Impersonate-Uid</code>: A unique identifier that represents the user being impersonated. Optional. Requires "Impersonate-User". Kubernetes does not impose any format requirements on this string.</li></ul><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>Prior to 1.11.3 (and 1.10.7, 1.9.11), <code>( extra name )</code> could only contain characters which were <a href=https://tools.ietf.org/html/rfc7230#section-3.2.6>legal in HTTP header labels</a>.</div><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4><code>Impersonate-Uid</code> is only available in versions 1.22.0 and higher.</div><p>An example of the impersonation headers used when impersonating a user with groups:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-http data-lang=http><span style=display:flex><span><span>Impersonate-User: jane.doe@example.com </span></span></span><span style=display:flex><span><span>Impersonate-Group: developers </span></span></span><span style=display:flex><span><span>Impersonate-Group: admins </span></span></span></code></pre></div><p>An example of the impersonation headers used when impersonating a user with a UID and extra fields:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-http data-lang=http><span style=display:flex><span><span>Impersonate-User: jane.doe@example.com </span></span></span><span style=display:flex><span><span>Impersonate-Extra-dn: cn=jane,ou=engineers,dc=example,dc=com </span></span></span><span style=display:flex><span><span>Impersonate-Extra-acme.com%2Fproject: some-project </span></span></span><span style=display:flex><span><span>Impersonate-Extra-scopes: view </span></span></span><span style=display:flex><span><span>Impersonate-Extra-scopes: development </span></span></span><span style=display:flex><span><span>Impersonate-Uid: 06f6ce97-e2c5-4ab8-7ba5-7654dd08d52b </span></span></span></code></pre></div><p>When using <code>kubectl</code> set the <code>--as</code> flag to configure the <code>Impersonate-User</code> header, set the <code>--as-group</code> flag to configure the <code>Impersonate-Group</code> header.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>kubectl drain mynode </span></span></code></pre></div><pre tabindex=0><code class=language-none data-lang=none>Error from server (Forbidden): User "clark" cannot get nodes at the cluster scope. (get nodes mynode) </code></pre><p>Set the <code>--as</code> and <code>--as-group</code> flag:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>kubectl drain mynode --as<span style=color:#666>=</span>superman --as-group<span style=color:#666>=</span>system:masters </span></span></code></pre></div><pre tabindex=0><code class=language-none data-lang=none>node/mynode cordoned node/mynode drained </code></pre><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4><code>kubectl</code> cannot impersonate extra fields or UIDs.</div><p>To impersonate a user, group, user identifier (UID) or extra fields, the impersonating user must have the ability to perform the "impersonate" verb on the kind of attribute being impersonated ("user", "group", "uid", etc.). For clusters that enable the RBAC authorization plugin, the following ClusterRole encompasses the rules needed to set user and group impersonation headers:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ClusterRole<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>impersonator<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>""</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"users"</span>,<span style=color:#bbb> </span><span style=color:#b44>"groups"</span>,<span style=color:#bbb> </span><span style=color:#b44>"serviceaccounts"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"impersonate"</span>]<span style=color:#bbb> </span></span></span></code></pre></div><p>For impersonation, extra fields and impersonated UIDs are both under the "authentication.k8s.io" <code>apiGroup</code>. Extra fields are evaluated as sub-resources of the resource "userextras". To allow a user to use impersonation headers for the extra field "scopes" and for UIDs, a user should be granted the following role:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ClusterRole<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>scopes-and-uid-impersonator<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># Can set "Impersonate-Extra-scopes" header and the "Impersonate-Uid" header.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"authentication.k8s.io"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"userextras/scopes"</span>,<span style=color:#bbb> </span><span style=color:#b44>"uids"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"impersonate"</span>]<span style=color:#bbb> </span></span></span></code></pre></div><p>The values of impersonation headers can also be restricted by limiting the set of <code>resourceNames</code> a resource can take.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ClusterRole<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>limited-impersonator<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># Can impersonate the user "jane.doe@example.com"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>""</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"users"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"impersonate"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resourceNames</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"jane.doe@example.com"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># Can impersonate the groups "developers" and "admins"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>""</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"groups"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"impersonate"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resourceNames</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"developers"</span>,<span style=color:#b44>"admins"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># Can impersonate the extras field "scopes" with the values "view" and "development"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"authentication.k8s.io"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"userextras/scopes"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"impersonate"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resourceNames</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"view"</span>,<span style=color:#bbb> </span><span style=color:#b44>"development"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># Can impersonate the uid "06f6ce97-e2c5-4ab8-7ba5-7654dd08d52b"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"authentication.k8s.io"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"uids"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"impersonate"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resourceNames</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"06f6ce97-e2c5-4ab8-7ba5-7654dd08d52b"</span>]<span style=color:#bbb> </span></span></span></code></pre></div><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>Impersonating a user or group allows you to perform any action as if you were that user or group; for that reason, impersonation is not namespace scoped. If you want to allow impersonation using Kubernetes RBAC, this requires using a <code>ClusterRole</code> and a <code>ClusterRoleBinding</code>, not a <code>Role</code> and <code>RoleBinding</code>.</div><h2 id=client-go-credential-plugins>client-go credential plugins</h2><div class="feature-state-notice feature-stable"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.22 [stable]</code></div><p><code>k8s.io/client-go</code> and tools using it such as <code>kubectl</code> and <code>kubelet</code> are able to execute an external command to receive user credentials.</p><p>This feature is intended for client side integrations with authentication protocols not natively supported by <code>k8s.io/client-go</code> (LDAP, Kerberos, OAuth2, SAML, etc.). The plugin implements the protocol specific logic, then returns opaque credentials to use. Almost all credential plugin use cases require a server side component with support for the <a href=#webhook-token-authentication>webhook token authenticator</a> to interpret the credential format produced by the client plugin.</p><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>Earlier versions of <code>kubectl</code> included built-in support for authenticating to AKS and GKE, but this is no longer present.</div><h3 id=example-use-case>Example use case</h3><p>In a hypothetical use case, an organization would run an external service that exchanges LDAP credentials for user specific, signed tokens. The service would also be capable of responding to <a href=#webhook-token-authentication>webhook token authenticator</a> requests to validate the tokens. Users would be required to install a credential plugin on their workstation.</p><p>To authenticate against the API:</p><ul><li>The user issues a <code>kubectl</code> command.</li><li>Credential plugin prompts the user for LDAP credentials, exchanges credentials with external service for a token.</li><li>Credential plugin returns token to client-go, which uses it as a bearer token against the API server.</li><li>API server uses the <a href=#webhook-token-authentication>webhook token authenticator</a> to submit a <code>TokenReview</code> to the external service.</li><li>External service verifies the signature on the token and returns the user's username and groups.</li></ul><h3 id=configuration>Configuration</h3><p>Credential plugins are configured through <a href=/docs/tasks/access-application-cluster/configure-access-multiple-clusters/>kubectl config files</a> as part of the user fields.</p><ul class="nav nav-tabs" id=exec-plugin-kubeconfig-example-1 role=tablist><li class=nav-item><a data-toggle=tab class="nav-link active" href=#exec-plugin-kubeconfig-example-1-0 role=tab aria-controls=exec-plugin-kubeconfig-example-1-0 aria-selected=true>client.authentication.k8s.io/v1</a></li><li class=nav-item><a data-toggle=tab class=nav-link href=#exec-plugin-kubeconfig-example-1-1 role=tab aria-controls=exec-plugin-kubeconfig-example-1-1>client.authentication.k8s.io/v1beta1</a></li></ul><div class=tab-content id=exec-plugin-kubeconfig-example-1><div id=exec-plugin-kubeconfig-example-1-0 class="tab-pane show active" role=tabpanel aria-labelledby=exec-plugin-kubeconfig-example-1-0><p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Config<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>users</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-user<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>user</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>exec</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Command to execute. Required.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>command</span>:<span style=color:#bbb> </span><span style=color:#b44>"example-client-go-exec-plugin"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># API version to use when decoding the ExecCredentials resource. Required.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># The API version returned by the plugin MUST match the version listed here.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># To integrate with tools that support multiple versions (such as client.authentication.k8s.io/v1beta1),</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># set an environment variable, pass an argument to the tool that indicates which version the exec plugin expects,</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># or read the version from the ExecCredential object in the KUBERNETES_EXEC_INFO environment variable.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span><span style=color:#b44>"client.authentication.k8s.io/v1"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Environment variables to set when executing the plugin. Optional.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>env</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>"FOO"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>value</span>:<span style=color:#bbb> </span><span style=color:#b44>"bar"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Arguments to pass when executing the plugin. Optional.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>args</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:#b44>"arg1"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:#b44>"arg2"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Text shown to the user when the executable doesn't seem to be present. Optional.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>installHint</span>:<span style=color:#bbb> </span>|<span style=color:#b44;font-style:italic> </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> example-client-go-exec-plugin is required to authenticate </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> to the current cluster. It can be installed: </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> On macOS: brew install example-client-go-exec-plugin </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> On Ubuntu: apt-get install example-client-go-exec-plugin </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> On Fedora: dnf install example-client-go-exec-plugin </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> ...</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Whether or not to provide cluster information, which could potentially contain</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># very large CA data, to this exec plugin as a part of the KUBERNETES_EXEC_INFO</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># environment variable.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>provideClusterInfo</span>:<span style=color:#bbb> </span><span style=color:#a2f;font-weight:700>true</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># The contract between the exec plugin and the standard input I/O stream. If the</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># contract cannot be satisfied, this plugin will not be run and an error will be</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># returned. Valid values are "Never" (this exec plugin never uses standard input),</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># "IfAvailable" (this exec plugin wants to use standard input if it is available),</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># or "Always" (this exec plugin requires standard input to function). Required.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>interactiveMode</span>:<span style=color:#bbb> </span>Never<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>clusters</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-cluster<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>cluster</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>server</span>:<span style=color:#bbb> </span><span style=color:#b44>"https://172.17.4.100:6443"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>certificate-authority</span>:<span style=color:#bbb> </span><span style=color:#b44>"/etc/kubernetes/ca.pem"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>extensions</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>client.authentication.k8s.io/exec<span style=color:#bbb> </span><span style=color:#080;font-style:italic># reserved extension name for per cluster exec config</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>extension</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>arbitrary</span>:<span style=color:#bbb> </span>config<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>this</span>:<span style=color:#bbb> </span>can be provided via the KUBERNETES_EXEC_INFO environment variable upon setting provideClusterInfo<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>you</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"can"</span>,<span style=color:#bbb> </span><span style=color:#b44>"put"</span>,<span style=color:#bbb> </span><span style=color:#b44>"anything"</span>,<span style=color:#bbb> </span><span style=color:#b44>"here"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>contexts</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-cluster<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>context</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>cluster</span>:<span style=color:#bbb> </span>my-cluster<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>user</span>:<span style=color:#bbb> </span>my-user<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>current-context</span>:<span style=color:#bbb> </span>my-cluster<span style=color:#bbb> </span></span></span></code></pre></div></div><div id=exec-plugin-kubeconfig-example-1-1 class=tab-pane role=tabpanel aria-labelledby=exec-plugin-kubeconfig-example-1-1><p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Config<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>users</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-user<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>user</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>exec</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Command to execute. Required.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>command</span>:<span style=color:#bbb> </span><span style=color:#b44>"example-client-go-exec-plugin"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># API version to use when decoding the ExecCredentials resource. Required.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># The API version returned by the plugin MUST match the version listed here.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># To integrate with tools that support multiple versions (such as client.authentication.k8s.io/v1),</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># set an environment variable, pass an argument to the tool that indicates which version the exec plugin expects,</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># or read the version from the ExecCredential object in the KUBERNETES_EXEC_INFO environment variable.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span><span style=color:#b44>"client.authentication.k8s.io/v1beta1"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Environment variables to set when executing the plugin. Optional.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>env</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>"FOO"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>value</span>:<span style=color:#bbb> </span><span style=color:#b44>"bar"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Arguments to pass when executing the plugin. Optional.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>args</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:#b44>"arg1"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:#b44>"arg2"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Text shown to the user when the executable doesn't seem to be present. Optional.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>installHint</span>:<span style=color:#bbb> </span>|<span style=color:#b44;font-style:italic> </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> example-client-go-exec-plugin is required to authenticate </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> to the current cluster. It can be installed: </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> On macOS: brew install example-client-go-exec-plugin </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> On Ubuntu: apt-get install example-client-go-exec-plugin </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> On Fedora: dnf install example-client-go-exec-plugin </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> ...</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Whether or not to provide cluster information, which could potentially contain</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># very large CA data, to this exec plugin as a part of the KUBERNETES_EXEC_INFO</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># environment variable.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>provideClusterInfo</span>:<span style=color:#bbb> </span><span style=color:#a2f;font-weight:700>true</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># The contract between the exec plugin and the standard input I/O stream. If the</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># contract cannot be satisfied, this plugin will not be run and an error will be</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># returned. Valid values are "Never" (this exec plugin never uses standard input),</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># "IfAvailable" (this exec plugin wants to use standard input if it is available),</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># or "Always" (this exec plugin requires standard input to function). Optional.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Defaults to "IfAvailable".</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>interactiveMode</span>:<span style=color:#bbb> </span>Never<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>clusters</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-cluster<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>cluster</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>server</span>:<span style=color:#bbb> </span><span style=color:#b44>"https://172.17.4.100:6443"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>certificate-authority</span>:<span style=color:#bbb> </span><span style=color:#b44>"/etc/kubernetes/ca.pem"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>extensions</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>client.authentication.k8s.io/exec<span style=color:#bbb> </span><span style=color:#080;font-style:italic># reserved extension name for per cluster exec config</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>extension</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>arbitrary</span>:<span style=color:#bbb> </span>config<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>this</span>:<span style=color:#bbb> </span>can be provided via the KUBERNETES_EXEC_INFO environment variable upon setting provideClusterInfo<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>you</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"can"</span>,<span style=color:#bbb> </span><span style=color:#b44>"put"</span>,<span style=color:#bbb> </span><span style=color:#b44>"anything"</span>,<span style=color:#bbb> </span><span style=color:#b44>"here"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>contexts</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-cluster<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>context</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>cluster</span>:<span style=color:#bbb> </span>my-cluster<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>user</span>:<span style=color:#bbb> </span>my-user<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>current-context</span>:<span style=color:#bbb> </span>my-cluster<span style=color:#bbb> </span></span></span></code></pre></div></div></div><p>Relative command paths are interpreted as relative to the directory of the config file. If KUBECONFIG is set to <code>/home/jane/kubeconfig</code> and the exec command is <code>./bin/example-client-go-exec-plugin</code>, the binary <code>/home/jane/bin/example-client-go-exec-plugin</code> is executed.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-user<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>user</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>exec</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Path relative to the directory of the kubeconfig</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>command</span>:<span style=color:#bbb> </span><span style=color:#b44>"./bin/example-client-go-exec-plugin"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span><span style=color:#b44>"client.authentication.k8s.io/v1"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>interactiveMode</span>:<span style=color:#bbb> </span>Never<span style=color:#bbb> </span></span></span></code></pre></div><h3 id=input-and-output-formats>Input and output formats</h3><p>The executed command prints an <code>ExecCredential</code> object to <code>stdout</code>. <code>k8s.io/client-go</code> authenticates against the Kubernetes API using the returned credentials in the <code>status</code>. The executed command is passed an <code>ExecCredential</code> object as input via the <code>KUBERNETES_EXEC_INFO</code> environment variable. This input contains helpful information like the expected API version of the returned <code>ExecCredential</code> object and whether or not the plugin can use <code>stdin</code> to interact with the user.</p><p>When run from an interactive session (i.e., a terminal), <code>stdin</code> can be exposed directly to the plugin. Plugins should use the <code>spec.interactive</code> field of the input <code>ExecCredential</code> object from the <code>KUBERNETES_EXEC_INFO</code> environment variable in order to determine if <code>stdin</code> has been provided. A plugin's <code>stdin</code> requirements (i.e., whether <code>stdin</code> is optional, strictly required, or never used in order for the plugin to run successfully) is declared via the <code>user.exec.interactiveMode</code> field in the <a href=/docs/concepts/configuration/organize-cluster-access-kubeconfig/>kubeconfig</a> (see table below for valid values). The <code>user.exec.interactiveMode</code> field is optional in <code>client.authentication.k8s.io/v1beta1</code> and required in <code>client.authentication.k8s.io/v1</code>.</p><table><caption style=display:none>interactiveMode values</caption><thead><tr><th><code>interactiveMode</code> Value</th><th>Meaning</th></tr></thead><tbody><tr><td><code>Never</code></td><td>This exec plugin never needs to use standard input, and therefore the exec plugin will be run regardless of whether standard input is available for user input.</td></tr><tr><td><code>IfAvailable</code></td><td>This exec plugin would like to use standard input if it is available, but can still operate if standard input is not available. Therefore, the exec plugin will be run regardless of whether stdin is available for user input. If standard input is available for user input, then it will be provided to this exec plugin.</td></tr><tr><td><code>Always</code></td><td>This exec plugin requires standard input in order to run, and therefore the exec plugin will only be run if standard input is available for user input. If standard input is not available for user input, then the exec plugin will not be run and an error will be returned by the exec plugin runner.</td></tr></tbody></table><p>To use bearer token credentials, the plugin returns a token in the status of the <a href=/docs/reference/config-api/client-authentication.v1beta1/#client-authentication-k8s-io-v1beta1-ExecCredential><code>ExecCredential</code></a></p><ul class="nav nav-tabs" id=exec-plugin-execcredential-example-1 role=tablist><li class=nav-item><a data-toggle=tab class="nav-link active" href=#exec-plugin-execcredential-example-1-0 role=tab aria-controls=exec-plugin-execcredential-example-1-0 aria-selected=true>client.authentication.k8s.io/v1</a></li><li class=nav-item><a data-toggle=tab class=nav-link href=#exec-plugin-execcredential-example-1-1 role=tab aria-controls=exec-plugin-execcredential-example-1-1>client.authentication.k8s.io/v1beta1</a></li></ul><div class=tab-content id=exec-plugin-execcredential-example-1><div id=exec-plugin-execcredential-example-1-0 class="tab-pane show active" role=tabpanel aria-labelledby=exec-plugin-execcredential-example-1-0><p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"client.authentication.k8s.io/v1"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"ExecCredential"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"status"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"token"</span>: <span style=color:#b44>"my-bearer-token"</span> </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div></div><div id=exec-plugin-execcredential-example-1-1 class=tab-pane role=tabpanel aria-labelledby=exec-plugin-execcredential-example-1-1><p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"client.authentication.k8s.io/v1beta1"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"ExecCredential"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"status"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"token"</span>: <span style=color:#b44>"my-bearer-token"</span> </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div></div></div><p>Alternatively, a PEM-encoded client certificate and key can be returned to use TLS client auth. If the plugin returns a different certificate and key on a subsequent call, <code>k8s.io/client-go</code> will close existing connections with the server to force a new TLS handshake.</p><p>If specified, <code>clientKeyData</code> and <code>clientCertificateData</code> must both must be present.</p><p><code>clientCertificateData</code> may contain additional intermediate certificates to send to the server.</p><ul class="nav nav-tabs" id=exec-plugin-execcredential-example-2 role=tablist><li class=nav-item><a data-toggle=tab class="nav-link active" href=#exec-plugin-execcredential-example-2-0 role=tab aria-controls=exec-plugin-execcredential-example-2-0 aria-selected=true>client.authentication.k8s.io/v1</a></li><li class=nav-item><a data-toggle=tab class=nav-link href=#exec-plugin-execcredential-example-2-1 role=tab aria-controls=exec-plugin-execcredential-example-2-1>client.authentication.k8s.io/v1beta1</a></li></ul><div class=tab-content id=exec-plugin-execcredential-example-2><div id=exec-plugin-execcredential-example-2-0 class="tab-pane show active" role=tabpanel aria-labelledby=exec-plugin-execcredential-example-2-0><p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"client.authentication.k8s.io/v1"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"ExecCredential"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"status"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"clientCertificateData"</span>: <span style=color:#b44>"-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"clientKeyData"</span>: <span style=color:#b44>"-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"</span> </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div></div><div id=exec-plugin-execcredential-example-2-1 class=tab-pane role=tabpanel aria-labelledby=exec-plugin-execcredential-example-2-1><p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"client.authentication.k8s.io/v1beta1"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"ExecCredential"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"status"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"clientCertificateData"</span>: <span style=color:#b44>"-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"clientKeyData"</span>: <span style=color:#b44>"-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"</span> </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div></div></div><p>Optionally, the response can include the expiry of the credential formatted as a <a href=https://datatracker.ietf.org/doc/html/rfc3339>RFC 3339</a> timestamp.</p><p>Presence or absence of an expiry has the following impact:</p><ul><li>If an expiry is included, the bearer token and TLS credentials are cached until the expiry time is reached, or if the server responds with a 401 HTTP status code, or when the process exits.</li><li>If an expiry is omitted, the bearer token and TLS credentials are cached until the server responds with a 401 HTTP status code or until the process exits.</li></ul><ul class="nav nav-tabs" id=exec-plugin-execcredential-example-3 role=tablist><li class=nav-item><a data-toggle=tab class="nav-link active" href=#exec-plugin-execcredential-example-3-0 role=tab aria-controls=exec-plugin-execcredential-example-3-0 aria-selected=true>client.authentication.k8s.io/v1</a></li><li class=nav-item><a data-toggle=tab class=nav-link href=#exec-plugin-execcredential-example-3-1 role=tab aria-controls=exec-plugin-execcredential-example-3-1>client.authentication.k8s.io/v1beta1</a></li></ul><div class=tab-content id=exec-plugin-execcredential-example-3><div id=exec-plugin-execcredential-example-3-0 class="tab-pane show active" role=tabpanel aria-labelledby=exec-plugin-execcredential-example-3-0><p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"client.authentication.k8s.io/v1"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"ExecCredential"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"status"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"token"</span>: <span style=color:#b44>"my-bearer-token"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"expirationTimestamp"</span>: <span style=color:#b44>"2018-03-05T17:30:20-08:00"</span> </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div></div><div id=exec-plugin-execcredential-example-3-1 class=tab-pane role=tabpanel aria-labelledby=exec-plugin-execcredential-example-3-1><p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"client.authentication.k8s.io/v1beta1"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"ExecCredential"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"status"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"token"</span>: <span style=color:#b44>"my-bearer-token"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"expirationTimestamp"</span>: <span style=color:#b44>"2018-03-05T17:30:20-08:00"</span> </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div></div></div><p>To enable the exec plugin to obtain cluster-specific information, set <code>provideClusterInfo</code> on the <code>user.exec</code> field in the <a href=/docs/concepts/configuration/organize-cluster-access-kubeconfig/>kubeconfig</a>. The plugin will then be supplied this cluster-specific information in the <code>KUBERNETES_EXEC_INFO</code> environment variable. Information from this environment variable can be used to perform cluster-specific credential acquisition logic. The following <code>ExecCredential</code> manifest describes a cluster information sample.</p><ul class="nav nav-tabs" id=exec-plugin-execcredential-example-4 role=tablist><li class=nav-item><a data-toggle=tab class="nav-link active" href=#exec-plugin-execcredential-example-4-0 role=tab aria-controls=exec-plugin-execcredential-example-4-0 aria-selected=true>client.authentication.k8s.io/v1</a></li><li class=nav-item><a data-toggle=tab class=nav-link href=#exec-plugin-execcredential-example-4-1 role=tab aria-controls=exec-plugin-execcredential-example-4-1>client.authentication.k8s.io/v1beta1</a></li></ul><div class=tab-content id=exec-plugin-execcredential-example-4><div id=exec-plugin-execcredential-example-4-0 class="tab-pane show active" role=tabpanel aria-labelledby=exec-plugin-execcredential-example-4-0><p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"client.authentication.k8s.io/v1"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"ExecCredential"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"spec"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"cluster"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"server"</span>: <span style=color:#b44>"https://172.17.4.100:6443"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"certificate-authority-data"</span>: <span style=color:#b44>"LS0t..."</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"config"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"arbitrary"</span>: <span style=color:#b44>"config"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"this"</span>: <span style=color:#b44>"can be provided via the KUBERNETES_EXEC_INFO environment variable upon setting provideClusterInfo"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"you"</span>: [<span style=color:#b44>"can"</span>, <span style=color:#b44>"put"</span>, <span style=color:#b44>"anything"</span>, <span style=color:#b44>"here"</span>] </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span> }, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"interactive"</span>: <span style=color:#a2f;font-weight:700>true</span> </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div></div><div id=exec-plugin-execcredential-example-4-1 class=tab-pane role=tabpanel aria-labelledby=exec-plugin-execcredential-example-4-1><p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"client.authentication.k8s.io/v1beta1"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"ExecCredential"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"spec"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"cluster"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"server"</span>: <span style=color:#b44>"https://172.17.4.100:6443"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"certificate-authority-data"</span>: <span style=color:#b44>"LS0t..."</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"config"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"arbitrary"</span>: <span style=color:#b44>"config"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"this"</span>: <span style=color:#b44>"can be provided via the KUBERNETES_EXEC_INFO environment variable upon setting provideClusterInfo"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"you"</span>: [<span style=color:#b44>"can"</span>, <span style=color:#b44>"put"</span>, <span style=color:#b44>"anything"</span>, <span style=color:#b44>"here"</span>] </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span> }, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"interactive"</span>: <span style=color:#a2f;font-weight:700>true</span> </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div></div></div><h2 id=self-subject-review>API access to authentication information for a client</h2><div class="feature-state-notice feature-stable"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.28 [stable]</code></div><p>If your cluster has the API enabled, you can use the <code>SelfSubjectReview</code> API to find out how your Kubernetes cluster maps your authentication information to identify you as a client. This works whether you are authenticating as a user (typically representing a real person) or as a ServiceAccount.</p><p><code>SelfSubjectReview</code> objects do not have any configurable fields. On receiving a request, the Kubernetes API server fills the status with the user attributes and returns it to the user.</p><p>Request example (the body would be a <code>SelfSubjectReview</code>):</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-http data-lang=http><span style=display:flex><span><span>POST /apis/authentication.k8s.io/v1/selfsubjectreviews </span></span></span></code></pre></div><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"authentication.k8s.io/v1"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"SelfSubjectReview"</span> </span></span><span style=display:flex><span>} </span></span></code></pre></div><p>Response example:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"authentication.k8s.io/v1"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"SelfSubjectReview"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"status"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"userInfo"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"name"</span>: <span style=color:#b44>"jane.doe"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"uid"</span>: <span style=color:#b44>"b6c7cfd4-f166-11ec-8ea0-0242ac120002"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"groups"</span>: [ </span></span><span style=display:flex><span> <span style=color:#b44>"viewers"</span>, </span></span><span style=display:flex><span> <span style=color:#b44>"editors"</span>, </span></span><span style=display:flex><span> <span style=color:#b44>"system:authenticated"</span> </span></span><span style=display:flex><span> ], </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"extra"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"provider_id"</span>: [<span style=color:#b44>"token.company.example"</span>] </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div><p>For convenience, the <code>kubectl auth whoami</code> command is present. Executing this command will produce the following output (yet different user attributes will be shown):</p><ul><li><p>Simple output example</p><pre tabindex=0><code>ATTRIBUTE VALUE Username jane.doe Groups [system:authenticated] </code></pre></li><li><p>Complex example including extra attributes</p><pre tabindex=0><code>ATTRIBUTE VALUE Username jane.doe UID b79dbf30-0c6a-11ed-861d-0242ac120002 Groups [students teachers system:authenticated] Extra: skills [reading learning] Extra: subjects [math sports] </code></pre></li></ul><p>By providing the output flag, it is also possible to print the JSON or YAML representation of the result:</p><ul class="nav nav-tabs" id=self-subject-attributes-review-example-1 role=tablist><li class=nav-item><a data-toggle=tab class="nav-link active" href=#self-subject-attributes-review-example-1-0 role=tab aria-controls=self-subject-attributes-review-example-1-0 aria-selected=true>JSON</a></li><li class=nav-item><a data-toggle=tab class=nav-link href=#self-subject-attributes-review-example-1-1 role=tab aria-controls=self-subject-attributes-review-example-1-1>YAML</a></li></ul><div class=tab-content id=self-subject-attributes-review-example-1><div id=self-subject-attributes-review-example-1-0 class="tab-pane show active" role=tabpanel aria-labelledby=self-subject-attributes-review-example-1-0><p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"authentication.k8s.io/v1"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"SelfSubjectReview"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"status"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"userInfo"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"username"</span>: <span style=color:#b44>"jane.doe"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"uid"</span>: <span style=color:#b44>"b79dbf30-0c6a-11ed-861d-0242ac120002"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"groups"</span>: [ </span></span><span style=display:flex><span> <span style=color:#b44>"students"</span>, </span></span><span style=display:flex><span> <span style=color:#b44>"teachers"</span>, </span></span><span style=display:flex><span> <span style=color:#b44>"system:authenticated"</span> </span></span><span style=display:flex><span> ], </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"extra"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"skills"</span>: [ </span></span><span style=display:flex><span> <span style=color:#b44>"reading"</span>, </span></span><span style=display:flex><span> <span style=color:#b44>"learning"</span> </span></span><span style=display:flex><span> ], </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"subjects"</span>: [ </span></span><span style=display:flex><span> <span style=color:#b44>"math"</span>, </span></span><span style=display:flex><span> <span style=color:#b44>"sports"</span> </span></span><span style=display:flex><span> ] </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div></div><div id=self-subject-attributes-review-example-1-1 class=tab-pane role=tabpanel aria-labelledby=self-subject-attributes-review-example-1-1><p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>authentication.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>SelfSubjectReview<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>status</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>userInfo</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>username</span>:<span style=color:#bbb> </span>jane.doe<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>uid</span>:<span style=color:#bbb> </span>b79dbf30-0c6a-11ed-861d-0242ac120002<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>groups</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- students<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- teachers<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- system:authenticated<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>extra</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>skills</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- reading<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- learning<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>subjects</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- math<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- sports<span style=color:#bbb> </span></span></span></code></pre></div></div></div><p>This feature is extremely useful when a complicated authentication flow is used in a Kubernetes cluster, for example, if you use <a href=/docs/reference/access-authn-authz/authentication/#webhook-token-authentication>webhook token authentication</a> or <a href=/docs/reference/access-authn-authz/authentication/#authenticating-proxy>authenticating proxy</a>.</p><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>The Kubernetes API server fills the <code>userInfo</code> after all authentication mechanisms are applied, including <a href=/docs/reference/access-authn-authz/authentication/#user-impersonation>impersonation</a>. If you, or an authentication proxy, make a SelfSubjectReview using impersonation, you see the user details and properties for the user that was impersonated.</div><p>By default, all authenticated users can create <code>SelfSubjectReview</code> objects when the <code>APISelfSubjectReview</code> feature is enabled. It is allowed by the <code>system:basic-user</code> cluster role.</p><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4><p>You can only make <code>SelfSubjectReview</code> requests if:</p><ul><li>the <code>APISelfSubjectReview</code> <a href=/docs/reference/command-line-tools-reference/feature-gates/>feature gate</a> is enabled for your cluster (not needed for Kubernetes 1.31, but older Kubernetes versions might not offer this feature gate, or might default it to be off)</li><li>(if you are running a version of Kubernetes older than v1.28) the API server for your cluster has the <code>authentication.k8s.io/v1alpha1</code> or <code>authentication.k8s.io/v1beta1</code> <a class=glossary-tooltip title='A set of related paths in the Kubernetes API.' data-toggle=tooltip data-placement=top href=/docs/concepts/overview/kubernetes-api/#api-groups-and-versioning target=_blank aria-label='API group'>API group</a> enabled.</li></ul></div><h2 id=what-s-next>What's next</h2><ul><li>Read the <a href=/docs/reference/config-api/client-authentication.v1beta1/>client authentication reference (v1beta1)</a></li><li>Read the <a href=/docs/reference/config-api/client-authentication.v1/>client authentication reference (v1)</a></li></ul></div><div class=td-content style=page-break-before:always><h1 id=pg-de45b6ca7419a0e308044425b2ac52bb>2 - Authenticating with Bootstrap Tokens</h1><div class="feature-state-notice feature-stable"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.18 [stable]</code></div><p>Bootstrap tokens are a simple bearer token that is meant to be used when creating new clusters or joining new nodes to an existing cluster. It was built to support <a href=/docs/reference/setup-tools/kubeadm/>kubeadm</a>, but can be used in other contexts for users that wish to start clusters without <code>kubeadm</code>. It is also built to work, via RBAC policy, with the <a href=/docs/reference/access-authn-authz/kubelet-tls-bootstrapping/>kubelet TLS Bootstrapping</a> system.</p><h2 id=bootstrap-tokens-overview>Bootstrap Tokens Overview</h2><p>Bootstrap Tokens are defined with a specific type (<code>bootstrap.kubernetes.io/token</code>) of secrets that lives in the <code>kube-system</code> namespace. These Secrets are then read by the Bootstrap Authenticator in the API Server. Expired tokens are removed with the TokenCleaner controller in the Controller Manager. The tokens are also used to create a signature for a specific ConfigMap used in a "discovery" process through a BootstrapSigner controller.</p><h2 id=token-format>Token Format</h2><p>Bootstrap Tokens take the form of <code>abcdef.0123456789abcdef</code>. More formally, they must match the regular expression <code>[a-z0-9]{6}\.[a-z0-9]{16}</code>.</p><p>The first part of the token is the "Token ID" and is considered public information. It is used when referring to a token without leaking the secret part used for authentication. The second part is the "Token Secret" and should only be shared with trusted parties.</p><h2 id=enabling-bootstrap-token-authentication>Enabling Bootstrap Token Authentication</h2><p>The Bootstrap Token authenticator can be enabled using the following flag on the API server:</p><pre tabindex=0><code>--enable-bootstrap-token-auth </code></pre><p>When enabled, bootstrapping tokens can be used as bearer token credentials to authenticate requests against the API server.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-http data-lang=http><span style=display:flex><span><span>Authorization: Bearer 07401b.f395accd246ae52d </span></span></span></code></pre></div><p>Tokens authenticate as the username <code>system:bootstrap:<token id></code> and are members of the group <code>system:bootstrappers</code>. Additional groups may be specified in the token's Secret.</p><p>Expired tokens can be deleted automatically by enabling the <code>tokencleaner</code> controller on the controller manager.</p><pre tabindex=0><code>--controllers=*,tokencleaner </code></pre><h2 id=bootstrap-token-secret-format>Bootstrap Token Secret Format</h2><p>Each valid token is backed by a secret in the <code>kube-system</code> namespace. You can find the full design doc <a href=https://git.k8s.io/design-proposals-archive/cluster-lifecycle/bootstrap-discovery.md>here</a>.</p><p>Here is what the secret looks like.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Secret<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Name MUST be of form "bootstrap-token-<token id>"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>bootstrap-token-07401b<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespace</span>:<span style=color:#bbb> </span>kube-system<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># Type MUST be 'bootstrap.kubernetes.io/token'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>type</span>:<span style=color:#bbb> </span>bootstrap.kubernetes.io/token<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>stringData</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Human readable description. Optional.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>description</span>:<span style=color:#bbb> </span><span style=color:#b44>"The default bootstrap token generated by 'kubeadm init'."</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Token ID and secret. Required.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>token-id</span>:<span style=color:#bbb> </span>07401b<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>token-secret</span>:<span style=color:#bbb> </span>f395accd246ae52d<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Expiration. Optional.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expiration</span>:<span style=color:#bbb> </span>2017-03-10T03:22:11Z<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Allowed usages.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>usage-bootstrap-authentication</span>:<span style=color:#bbb> </span><span style=color:#b44>"true"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>usage-bootstrap-signing</span>:<span style=color:#bbb> </span><span style=color:#b44>"true"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Extra groups to authenticate the token as. Must start with "system:bootstrappers:"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>auth-extra-groups</span>:<span style=color:#bbb> </span>system:bootstrappers:worker,system:bootstrappers:ingress<span style=color:#bbb> </span></span></span></code></pre></div><p>The type of the secret must be <code>bootstrap.kubernetes.io/token</code> and the name must be <code>bootstrap-token-<token id></code>. It must also exist in the <code>kube-system</code> namespace.</p><p>The <code>usage-bootstrap-*</code> members indicate what this secret is intended to be used for. A value must be set to <code>true</code> to be enabled.</p><ul><li><code>usage-bootstrap-authentication</code> indicates that the token can be used to authenticate to the API server as a bearer token.</li><li><code>usage-bootstrap-signing</code> indicates that the token may be used to sign the <code>cluster-info</code> ConfigMap as described below.</li></ul><p>The <code>expiration</code> field controls the expiry of the token. Expired tokens are rejected when used for authentication and ignored during ConfigMap signing. The expiry value is encoded as an absolute UTC time using <a href=https://datatracker.ietf.org/doc/html/rfc3339>RFC3339</a>. Enable the <code>tokencleaner</code> controller to automatically delete expired tokens.</p><h2 id=token-management-with-kubeadm>Token Management with kubeadm</h2><p>You can use the <code>kubeadm</code> tool to manage tokens on a running cluster. See the <a href=/docs/reference/setup-tools/kubeadm/kubeadm-token/>kubeadm token docs</a> for details.</p><h2 id=configmap-signing>ConfigMap Signing</h2><p>In addition to authentication, the tokens can be used to sign a ConfigMap. This is used early in a cluster bootstrap process before the client trusts the API server. The signed ConfigMap can be authenticated by the shared token.</p><p>Enable ConfigMap signing by enabling the <code>bootstrapsigner</code> controller on the Controller Manager.</p><pre tabindex=0><code>--controllers=*,bootstrapsigner </code></pre><p>The ConfigMap that is signed is <code>cluster-info</code> in the <code>kube-public</code> namespace. The typical flow is that a client reads this ConfigMap while unauthenticated and ignoring TLS errors. It then validates the payload of the ConfigMap by looking at a signature embedded in the ConfigMap.</p><p>The ConfigMap may look like this:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ConfigMap<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>cluster-info<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespace</span>:<span style=color:#bbb> </span>kube-public<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>data</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>jws-kubeconfig-07401b</span>:<span style=color:#bbb> </span>eyJhbGciOiJIUzI1NiIsImtpZCI6IjA3NDAxYiJ9..tYEfbo6zDNo40MQE07aZcQX2m3EB2rO3NuXtxVMYm9U<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kubeconfig</span>:<span style=color:#bbb> </span>|<span style=color:#b44;font-style:italic> </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> apiVersion: v1 </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> clusters: </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> - cluster: </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> certificate-authority-data: <really long certificate data> </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> server: https://10.138.0.2:6443 </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> name: "" </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> contexts: [] </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> current-context: "" </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> kind: Config </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> preferences: {} </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> users: []</span><span style=color:#bbb> </span></span></span></code></pre></div><p>The <code>kubeconfig</code> member of the ConfigMap is a config file with only the cluster information filled out. The key thing being communicated here is the <code>certificate-authority-data</code>. This may be expanded in the future.</p><p>The signature is a JWS signature using the "detached" mode. To validate the signature, the user should encode the <code>kubeconfig</code> payload according to JWS rules (base64 encoded while discarding any trailing <code>=</code>). That encoded payload is then used to form a whole JWS by inserting it between the 2 dots. You can verify the JWS using the <code>HS256</code> scheme (HMAC-SHA256) with the full token (e.g. <code>07401b.f395accd246ae52d</code>) as the shared secret. Users <em>must</em> verify that HS256 is used.</p><div class="alert alert-danger" role=alert><h4 class=alert-heading>Warning:</h4>Any party with a bootstrapping token can create a valid signature for that token. When using ConfigMap signing it's discouraged to share the same token with many clients, since a compromised client can potentially man-in-the middle another client relying on the signature to bootstrap TLS trust.</div><p>Consult the <a href=/docs/reference/setup-tools/kubeadm/implementation-details/>kubeadm implementation details</a> section for more information.</p></div><div class=td-content style=page-break-before:always><h1 id=pg-342be69d36f174f762c36f4fe11fcb20>3 - Authorization</h1><div class=lead>Details of Kubernetes authorization mechanisms and supported authorization modes.</div><p>Kubernetes authorization takes place following <a href=/docs/reference/access-authn-authz/authentication/>authentication</a>. Usually, a client making a request must be authenticated (logged in) before its request can be allowed; however, Kubernetes also allows anonymous requests in some circumstances.</p><p>For an overview of how authorization fits into the wider context of API access control, read <a href=/docs/concepts/security/controlling-access/>Controlling Access to the Kubernetes API</a>.</p><h2 id=determine-whether-a-request-is-allowed-or-denied>Authorization verdicts</h2><p>Kubernetes authorization of API requests takes place within the API server. The API server evaluates all of the request attributes against all policies, potentially also consulting external services, and then allows or denies the request.</p><p>All parts of an API request must be allowed by some authorization mechanism in order to proceed. In other words: access is denied by default.</p><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4><p>Access controls and policies that depend on specific fields of specific kinds of objects are handled by <a class=glossary-tooltip title='A piece of code that intercepts requests to the Kubernetes API server prior to persistence of the object.' data-toggle=tooltip data-placement=top href=/docs/reference/access-authn-authz/admission-controllers/ target=_blank aria-label='admission controllers'>admission controllers</a>.</p><p>Kubernetes admission control happens after authorization has completed (and, therefore, only when the authorization decision was to allow the request).</p></div><p>When multiple <a href=#authorization-modules>authorization modules</a> are configured, each is checked in sequence. If any authorizer <em>approves</em> or <em>denies</em> a request, that decision is immediately returned and no other authorizer is consulted. If all modules have <em>no opinion</em> on the request, then the request is denied. An overall deny verdict means that the API server rejects the request and responds with an HTTP 403 (Forbidden) status.</p><h2 id=request-attributes-used-in-authorization>Request attributes used in authorization</h2><p>Kubernetes reviews only the following API request attributes:</p><ul><li><strong>user</strong> - The <code>user</code> string provided during authentication.</li><li><strong>group</strong> - The list of group names to which the authenticated user belongs.</li><li><strong>extra</strong> - A map of arbitrary string keys to string values, provided by the authentication layer.</li><li><strong>API</strong> - Indicates whether the request is for an API resource.</li><li><strong>Request path</strong> - Path to miscellaneous non-resource endpoints like <code>/api</code> or <code>/healthz</code>.</li><li><strong>API request verb</strong> - API verbs like <code>get</code>, <code>list</code>, <code>create</code>, <code>update</code>, <code>patch</code>, <code>watch</code>, <code>delete</code>, and <code>deletecollection</code> are used for resource requests. To determine the request verb for a resource API endpoint, see <a href=/docs/reference/access-authn-authz/authorization/#determine-the-request-verb>request verbs and authorization</a>.</li><li><strong>HTTP request verb</strong> - Lowercased HTTP methods like <code>get</code>, <code>post</code>, <code>put</code>, and <code>delete</code> are used for non-resource requests.</li><li><strong>Resource</strong> - The ID or name of the resource that is being accessed (for resource requests only) -- For resource requests using <code>get</code>, <code>update</code>, <code>patch</code>, and <code>delete</code> verbs, you must provide the resource name.</li><li><strong>Subresource</strong> - The subresource that is being accessed (for resource requests only).</li><li><strong>Namespace</strong> - The namespace of the object that is being accessed (for namespaced resource requests only).</li><li><strong>API group</strong> - The <a class=glossary-tooltip title='A set of related paths in the Kubernetes API.' data-toggle=tooltip data-placement=top href=/docs/concepts/overview/kubernetes-api/#api-groups-and-versioning target=_blank aria-label='API Group'>API Group</a> being accessed (for resource requests only). An empty string designates the <em>core</em> <a href=/docs/reference/using-api/#api-groups>API group</a>.</li></ul><h3 id=determine-the-request-verb>Request verbs and authorization</h3><h4 id=request-verb-non-resource>Non-resource requests</h4><p>Requests to endpoints other than <code>/api/v1/...</code> or <code>/apis/<group>/<version>/...</code> are considered <em>non-resource requests</em>, and use the lower-cased HTTP method of the request as the verb. For example, making a <code>GET</code> request using HTTP to endpoints such as <code>/api</code> or <code>/healthz</code> would use <strong>get</strong> as the verb.</p><h4 id=request-verb-resource>Resource requests</h4><p>To determine the request verb for a resource API endpoint, Kubernetes maps the HTTP verb used and considers whether or not the request acts on an individual resource or on a collection of resources:</p><table><thead><tr><th>HTTP verb</th><th>request verb</th></tr></thead><tbody><tr><td><code>POST</code></td><td><strong>create</strong></td></tr><tr><td><code>GET</code>, <code>HEAD</code></td><td><strong>get</strong> (for individual resources), <strong>list</strong> (for collections, including full object content), <strong>watch</strong> (for watching an individual resource or collection of resources)</td></tr><tr><td><code>PUT</code></td><td><strong>update</strong></td></tr><tr><td><code>PATCH</code></td><td><strong>patch</strong></td></tr><tr><td><code>DELETE</code></td><td><strong>delete</strong> (for individual resources), <strong>deletecollection</strong> (for collections)</td></tr></tbody></table><div class="alert alert-caution" role=alert><h4 class=alert-heading>Caution:</h4>+The <strong>get</strong>, <strong>list</strong> and <strong>watch</strong> verbs can all return the full details of a resource. In terms of access to the returned data they are equivalent. For example, <strong>list</strong> on <code>secrets</code> will reveal the <strong>data</strong> attributes of any returned resources.</div><p>Kubernetes sometimes checks authorization for additional permissions using specialized verbs. For example:</p><ul><li>Special cases of <a href=/docs/reference/access-authn-authz/authentication/>authentication</a><ul><li><strong>impersonate</strong> verb on <code>users</code>, <code>groups</code>, and <code>serviceaccounts</code> in the core API group, and the <code>userextras</code> in the <code>authentication.k8s.io</code> API group.</li></ul></li><li><a href=/docs/reference/access-authn-authz/certificate-signing-requests/#authorization>Authorization of CertificateSigningRequests</a><ul><li><strong>approve</strong> verb for CertificateSigningRequests, and <strong>update</strong> for revisions to existing approvals</li></ul></li><li><a href=/docs/reference/access-authn-authz/rbac/#privilege-escalation-prevention-and-bootstrapping>RBAC</a><ul><li><strong>bind</strong> and <strong>escalate</strong> verbs on <code>roles</code> and <code>clusterroles</code> resources in the <code>rbac.authorization.k8s.io</code> API group.</li></ul></li></ul><h2 id=authorization-context>Authorization context</h2><p>Kubernetes expects attributes that are common to REST API requests. This means that Kubernetes authorization works with existing organization-wide or cloud-provider-wide access control systems which may handle other APIs besides the Kubernetes API.</p><h2 id=authorization-modules>Authorization modes</h2><p>The Kubernetes API server may authorize a request using one of several authorization modes:</p><dl><dt><code>AlwaysAllow</code></dt><dd>This mode allows all requests, which brings <a href=#warning-always-allow>security risks</a>. Use this authorization mode only if you do not require authorization for your API requests (for example, for testing).</dd><dt><code>AlwaysDeny</code></dt><dd>This mode blocks all requests. Use this authorization mode only for testing.</dd><dt><code>ABAC</code> (<a href=/docs/reference/access-authn-authz/abac/>attribute-based access control</a>)</dt><dd>Kubernetes ABAC mode defines an access control paradigm whereby access rights are granted to users through the use of policies which combine attributes together. The policies can use any type of attributes (user attributes, resource attributes, object, environment attributes, etc).</dd><dt><code>RBAC</code> (<a href=/docs/reference/access-authn-authz/rbac/>role-based access control</a>)</dt><dd>Kubernetes RBAC is a method of regulating access to computer or network resources based on the roles of individual users within an enterprise. In this context, access is the ability of an individual user to perform a specific task, such as view, create, or modify a file.<br>In this mode, Kubernetes uses the <code>rbac.authorization.k8s.io</code> API group to drive authorization decisions, allowing you to dynamically configure permission policies through the Kubernetes API.</dd><dt><code>Node</code></dt><dd>A special-purpose authorization mode that grants permissions to kubelets based on the pods they are scheduled to run. To learn more about the Node authorization mode, see <a href=/docs/reference/access-authn-authz/node/>Node Authorization</a>.</dd><dt><code>Webhook</code></dt><dd>Kubernetes <a href=/docs/reference/access-authn-authz/webhook/>webhook mode</a> for authorization makes a synchronous HTTP callout, blocking the request until the remote HTTP service responds to the query.You can write your own software to handle the callout, or use solutions from the ecosystem.</dd></dl><a id=warning-always-allow><div class="alert alert-danger" role=alert><h4 class=alert-heading>Warning:</h4><p>Enabling the <code>AlwaysAllow</code> mode bypasses authorization; do not use this on a cluster where you do not trust <strong>all</strong> potential API clients, including the workloads that you run.</p><p>Authorization mechanisms typically return either a <em>deny</em> or <em>no opinion</em> result; see <a href=#determine-whether-a-request-is-allowed-or-denied>authorization verdicts</a> for more on this. Activating the <code>AlwaysAllow</code> means that if all other authorizers return “no opinion”, the request is allowed. For example, <code>--authorization-mode=AlwaysAllow,RBAC</code> has the same effect as <code>--authorization-mode=AlwaysAllow</code> because Kubernetes RBAC does not provide negative (deny) access rules.</p><p>You should not use the <code>AlwaysAllow</code> mode on a Kubernetes cluster where the API server is reachable from the public internet.</p></div><h3 id=the-system-masters-group>The system:masters group</h3><p>The <code>system:masters</code> group is a built-in Kubernetes group that grants unrestricted access to the API server. Any user assigned to this group has full cluster administrator privileges, bypassing any authorization restrictions imposed by the RBAC or Webhook mechanisms. <a href=/docs/concepts/security/rbac-good-practices/#least-privilege>Avoid adding users</a> to this group. If you do need to grant a user cluster-admin rights, you can create a <a href=/docs/reference/access-authn-authz/rbac/#user-facing-roles>ClusterRoleBinding</a> to the built-in <code>cluster-admin</code> ClusterRole.</p><h3 id=choice-of-authz-config>Authorization mode configuration</h3><p>You can configure the Kubernetes API server's authorizer chain using either <a href=#using-flags-for-your-authorization-module>command line arguments</a> only or, as a beta feature, using a <a href=#using-configuration-file-for-authorization>configuration file</a>.</p><p>You have to pick one of the two configuration approaches; setting both <code>--authorization-config</code> path and configuring an authorization webhook using the <code>--authorization-mode</code> and <code>--authorization-webhook-*</code> command line arguments is not allowed. If you try this, the API server reports an error message during startup, then exits immediately.</p><h3 id=using-flags-for-your-authorization-module>Command line authorization mode configuration</h3><div class="feature-state-notice feature-stable"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.8 [stable]</code></div><p>You can use the following modes:</p><ul><li><code>--authorization-mode=ABAC</code> (Attribute-based access control mode)</li><li><code>--authorization-mode=RBAC</code> (Role-based access control mode)</li><li><code>--authorization-mode=Node</code> (Node authorizer)</li><li><code>--authorization-mode=Webhook</code> (Webhook authorization mode)</li><li><code>--authorization-mode=AlwaysAllow</code> (always allows requests; carries <a href=#warning-always-allow>security risks</a>)</li><li><code>--authorization-mode=AlwaysDeny</code> (always denies requests)</li></ul><p>You can choose more than one authorization mode; for example: <code>--authorization-mode=Node,Webhook</code></p><p>Kubernetes checks authorization modules based on the order that you specify them on the API server's command line, so an earlier module has higher priority to allow or deny a request.</p><p>You cannot combine the <code>--authorization-mode</code> command line argument with the <code>--authorization-config</code> command line argument used for <a href=#using-configuration-file-for-authorization-mode>configuring authorization using a local file</a>.</p><p>For more information on command line arguments to the API server, read the <a href=/docs/reference/command-line-tools-reference/kube-apiserver/><code>kube-apiserver</code> reference</a>.</p><a id=configuring-the-api-server-using-an-authorization-config-file><h3 id=using-configuration-file-for-authorization>Configuring the API Server using an authorization config file</h3><div class="feature-state-notice feature-beta" title="Feature Gate: StructuredAuthorizationConfiguration"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.30 [beta]</code> (enabled by default: true)</div><p>As a beta feature, Kubernetes lets you configure authorization chains that can include multiple webhooks. The authorization items in that chain can have well-defined parameters that validate requests in a particular order, offering you fine-grained control, such as explicit Deny on failures.</p><p>The configuration file approach even allows you to specify <a href=/docs/reference/using-api/cel/>CEL</a> rules to pre-filter requests before they are dispatched to webhooks, helping you to prevent unnecessary invocations. The API server also automatically reloads the authorizer chain when the configuration file is modified.</p><p>You specify the path to the authorization configuration using the <code>--authorization-config</code> command line argument.</p><p>If you want to use command line arguments instead of a configuration file, that's also a valid and supported approach. Some authorization capabilities (for example: multiple webhooks, webhook failure policy, and pre-filter rules) are only available if you use an authorization configuration file.</p><h4 id=authz-config-example>Example configuration</h4><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:#00f;font-weight:700>---</span><span style=color:#bbb> </span></span></span><span style=display:flex;background-color:#dfdfdf><span><span style=color:#bbb></span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex;background-color:#dfdfdf><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># DO NOT USE THE CONFIG AS IS. THIS IS AN EXAMPLE.</span><span style=color:#bbb> </span></span></span><span style=display:flex;background-color:#dfdfdf><span><span style=color:#bbb></span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>apiserver.config.k8s.io/v1beta1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>AuthorizationConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>authorizers</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>type</span>:<span style=color:#bbb> </span>Webhook<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Name used to describe the authorizer</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># This is explicitly used in monitoring machinery for metrics</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Note:</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># - Validation for this field is similar to how K8s labels are validated today.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Required, with no default</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>webhook<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>webhook</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># The duration to cache 'authorized' responses from the webhook</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># authorizer.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Same as setting `--authorization-webhook-cache-authorized-ttl` flag</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Default: 5m0s</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>authorizedTTL</span>:<span style=color:#bbb> </span>30s<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># The duration to cache 'unauthorized' responses from the webhook</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># authorizer.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Same as setting `--authorization-webhook-cache-unauthorized-ttl` flag</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Default: 30s</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>unauthorizedTTL</span>:<span style=color:#bbb> </span>30s<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Timeout for the webhook request</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Maximum allowed is 30s.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Required, with no default.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>timeout</span>:<span style=color:#bbb> </span>3s<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># The API version of the authorization.k8s.io SubjectAccessReview to</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># send to and expect from the webhook.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Same as setting `--authorization-webhook-version` flag</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Required, with no default</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Valid values: v1beta1, v1</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>subjectAccessReviewVersion</span>:<span style=color:#bbb> </span>v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># MatchConditionSubjectAccessReviewVersion specifies the SubjectAccessReview</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># version the CEL expressions are evaluated against</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Valid values: v1</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Required, no default value</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>matchConditionSubjectAccessReviewVersion</span>:<span style=color:#bbb> </span>v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Controls the authorization decision when a webhook request fails to</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># complete or returns a malformed response or errors evaluating</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># matchConditions.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Valid values:</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># - NoOpinion: continue to subsequent authorizers to see if one of</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># them allows the request</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># - Deny: reject the request without consulting subsequent authorizers</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Required, with no default.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>failurePolicy</span>:<span style=color:#bbb> </span>Deny<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>connectionInfo</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Controls how the webhook should communicate with the server.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Valid values:</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># - KubeConfigFile: use the file specified in kubeConfigFile to locate the</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># server.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># - InClusterConfig: use the in-cluster configuration to call the</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># SubjectAccessReview API hosted by kube-apiserver. This mode is not</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># allowed for kube-apiserver.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>type</span>:<span style=color:#bbb> </span>KubeConfigFile<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Path to KubeConfigFile for connection info</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Required, if connectionInfo.Type is KubeConfigFile</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kubeConfigFile</span>:<span style=color:#bbb> </span>/kube-system-authz-webhook.yaml<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># matchConditions is a list of conditions that must be met for a request to be sent to this</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># webhook. An empty list of matchConditions matches all requests.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># There are a maximum of 64 match conditions allowed.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># The exact matching logic is (in order):</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># 1. If at least one matchCondition evaluates to FALSE, then the webhook is skipped.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># 2. If ALL matchConditions evaluate to TRUE, then the webhook is called.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># 3. If at least one matchCondition evaluates to an error (but none are FALSE):</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># - If failurePolicy=Deny, then the webhook rejects the request</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># - If failurePolicy=NoOpinion, then the error is ignored and the webhook is skipped</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>matchConditions</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># expression represents the expression which will be evaluated by CEL. Must evaluate to bool.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># CEL expressions have access to the contents of the SubjectAccessReview in v1 version.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># If version specified by subjectAccessReviewVersion in the request variable is v1beta1,</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># the contents would be converted to the v1 version before evaluating the CEL expression.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># only send resource requests to the webhook</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span>has(request.resourceAttributes)<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># only intercept requests to kube-system</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span>request.resourceAttributes.namespace == 'kube-system'<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># don't intercept requests from kube-system service accounts</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>"!('system:serviceaccounts:kube-system' in request.groups)"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>type</span>:<span style=color:#bbb> </span>Node<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>node<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>type</span>:<span style=color:#bbb> </span>RBAC<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>rbac<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>type</span>:<span style=color:#bbb> </span>Webhook<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>in-cluster-authorizer<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>webhook</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>authorizedTTL</span>:<span style=color:#bbb> </span>5m<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>unauthorizedTTL</span>:<span style=color:#bbb> </span>30s<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>timeout</span>:<span style=color:#bbb> </span>3s<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>subjectAccessReviewVersion</span>:<span style=color:#bbb> </span>v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>failurePolicy</span>:<span style=color:#bbb> </span>NoOpinion<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>connectionInfo</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>type</span>:<span style=color:#bbb> </span>InClusterConfig</span></span></code></pre></div><p>When configuring the authorizer chain using a configuration file, make sure all the control plane nodes have the same file contents. Take a note of the API server configuration when upgrading / downgrading your clusters. For example, if upgrading from Kubernetes 1.30 to Kubernetes 1.31, you would need to make sure the config file is in a format that Kubernetes 1.31 can understand, before you upgrade the cluster. If you downgrade to 1.30, you would need to set the configuration appropriately.</p><h4 id=authorization-configuration-and-reloads>Authorization configuration and reloads</h4><p>Kubernetes reloads the authorization configuration file when the API server observes a change to the file, and also on a 60 second schedule if no change events were observed.</p><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4><p>You must ensure that all non-webhook authorizer types remain unchanged in the file on reload.</p><p>A reload <strong>must not</strong> add or remove Node or RBAC authorizers (they can be reordered, but cannot be added or removed).</p></div><h2 id=privilege-escalation-via-pod-creation>Privilege escalation via workload creation or edits</h2><p>Users who can create/edit pods in a namespace, either directly or through an object that enables indirect <a href=/docs/concepts/architecture/controller/>workload management</a>, may be able to escalate their privileges in that namespace. The potential routes to privilege escalation include Kubernetes <a href=/docs/concepts/extend-kubernetes/#api-extensions>API extensions</a> and their associated <a class=glossary-tooltip title='A control loop that watches the shared state of the cluster through the apiserver and makes changes attempting to move the current state towards the desired state.' data-toggle=tooltip data-placement=top href=/docs/concepts/architecture/controller/ target=_blank aria-label=controllers>controllers</a>.</p><div class="alert alert-caution" role=alert><h4 class=alert-heading>Caution:</h4>As a cluster administrator, use caution when granting access to create or edit workloads. Some details of how these can be misused are documented in <a href=/docs/reference/access-authn-authz/authorization/#escalation-paths>escalation paths</a>.</div><h3 id=escalation-paths>Escalation paths</h3><p>There are different ways that an attacker or untrustworthy user could gain additional privilege within a namespace, if you allow them to run arbitrary Pods in that namespace:</p><ul><li>Mounting arbitrary Secrets in that namespace<ul><li>Can be used to access confidential information meant for other workloads</li><li>Can be used to obtain a more privileged ServiceAccount's service account token</li></ul></li><li>Using arbitrary ServiceAccounts in that namespace<ul><li>Can perform Kubernetes API actions as another workload (impersonation)</li><li>Can perform any privileged actions that ServiceAccount has</li></ul></li><li>Mounting or using ConfigMaps meant for other workloads in that namespace<ul><li>Can be used to obtain information meant for other workloads, such as database host names.</li></ul></li><li>Mounting volumes meant for other workloads in that namespace<ul><li>Can be used to obtain information meant for other workloads, and change it.</li></ul></li></ul><div class="alert alert-caution" role=alert><h4 class=alert-heading>Caution:</h4>As a system administrator, you should be cautious when deploying CustomResourceDefinitions that let users make changes to the above areas. These may open privilege escalations paths. Consider the consequences of this kind of change when deciding on your authorization controls.</div><h2 id=checking-api-access>Checking API access</h2><p><code>kubectl</code> provides the <code>auth can-i</code> subcommand for quickly querying the API authorization layer. The command uses the <code>SelfSubjectAccessReview</code> API to determine if the current user can perform a given action, and works regardless of the authorization mode used.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>kubectl auth can-i create deployments --namespace dev </span></span></code></pre></div><p>The output is similar to this:</p><pre tabindex=0><code>yes </code></pre><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl auth can-i create deployments --namespace prod </span></span></code></pre></div><p>The output is similar to this:</p><pre tabindex=0><code>no </code></pre><p>Administrators can combine this with <a href=/docs/reference/access-authn-authz/authentication/#user-impersonation>user impersonation</a> to determine what action other users can perform.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>kubectl auth can-i list secrets --namespace dev --as dave </span></span></code></pre></div><p>The output is similar to this:</p><pre tabindex=0><code>no </code></pre><p>Similarly, to check whether a ServiceAccount named <code>dev-sa</code> in Namespace <code>dev</code> can list Pods in the Namespace <code>target</code>:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>kubectl auth can-i list pods <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --namespace target <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --as system:serviceaccount:dev:dev-sa </span></span></code></pre></div><p>The output is similar to this:</p><pre tabindex=0><code>yes </code></pre><p>SelfSubjectAccessReview is part of the <code>authorization.k8s.io</code> API group, which exposes the API server authorization to external services. Other resources in this group include:</p><dl><dt>SubjectAccessReview</dt><dd>Access review for any user, not only the current one. Useful for delegating authorization decisions to the API server. For example, the kubelet and extension API servers use this to determine user access to their own APIs.</dd><dt>LocalSubjectAccessReview</dt><dd>Like SubjectAccessReview but restricted to a specific namespace.</dd><dt>SelfSubjectRulesReview</dt><dd>A review which returns the set of actions a user can perform within a namespace. Useful for users to quickly summarize their own access, or for UIs to hide/show actions.</dd></dl><p>These APIs can be queried by creating normal Kubernetes resources, where the response <code>status</code> field of the returned object is the result of the query. For example:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>kubectl create -f - -o yaml <span style=color:#b44><< EOF </span></span></span><span style=display:flex><span><span style=color:#b44>apiVersion: authorization.k8s.io/v1 </span></span></span><span style=display:flex><span><span style=color:#b44>kind: SelfSubjectAccessReview </span></span></span><span style=display:flex><span><span style=color:#b44>spec: </span></span></span><span style=display:flex><span><span style=color:#b44> resourceAttributes: </span></span></span><span style=display:flex><span><span style=color:#b44> group: apps </span></span></span><span style=display:flex><span><span style=color:#b44> resource: deployments </span></span></span><span style=display:flex><span><span style=color:#b44> verb: create </span></span></span><span style=display:flex><span><span style=color:#b44> namespace: dev </span></span></span><span style=display:flex><span><span style=color:#b44>EOF</span> </span></span></code></pre></div><p>The generated SelfSubjectAccessReview is similar to:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>authorization.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>SelfSubjectAccessReview<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>creationTimestamp</span>:<span style=color:#bbb> </span><span style=color:#a2f;font-weight:700>null</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>spec</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resourceAttributes</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>group</span>:<span style=color:#bbb> </span>apps<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resource</span>:<span style=color:#bbb> </span>deployments<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespace</span>:<span style=color:#bbb> </span>dev<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verb</span>:<span style=color:#bbb> </span>create<span style=color:#bbb> </span></span></span><span style=display:flex;background-color:#dfdfdf><span><span style=color:#bbb></span><span style=color:green;font-weight:700>status</span>:<span style=color:#bbb> </span></span></span><span style=display:flex;background-color:#dfdfdf><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>allowed</span>:<span style=color:#bbb> </span><span style=color:#a2f;font-weight:700>true</span><span style=color:#bbb> </span></span></span><span style=display:flex;background-color:#dfdfdf><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>denied</span>:<span style=color:#bbb> </span><span style=color:#a2f;font-weight:700>false</span></span></span></code></pre></div><h2 id=what-s-next>What's next</h2><ul><li>To learn more about Authentication, see <a href=/docs/reference/access-authn-authz/authentication/>Authentication</a>.</li><li>For an overview, read <a href=/docs/concepts/security/controlling-access/>Controlling Access to the Kubernetes API</a>.</li><li>To learn more about Admission Control, see <a href=/docs/reference/access-authn-authz/admission-controllers/>Using Admission Controllers</a>.</li><li>Read more about <a href=/docs/reference/using-api/cel/>Common Expression Language in Kubernetes</a>.</li></ul></div><div class=td-content style=page-break-before:always><h1 id=pg-954776b47f2d90515f375623a0ce98e1>4 - Using RBAC Authorization</h1><p>Role-based access control (RBAC) is a method of regulating access to computer or network resources based on the roles of individual users within your organization.</p><p>RBAC authorization uses the <code>rbac.authorization.k8s.io</code> <a class=glossary-tooltip title='A set of related paths in the Kubernetes API.' data-toggle=tooltip data-placement=top href=/docs/concepts/overview/kubernetes-api/#api-groups-and-versioning target=_blank aria-label='API group'>API group</a> to drive authorization decisions, allowing you to dynamically configure policies through the Kubernetes API.</p><p>To enable RBAC, start the <a class=glossary-tooltip title='Control plane component that serves the Kubernetes API.' data-toggle=tooltip data-placement=top href=/docs/concepts/architecture/#kube-apiserver target=_blank aria-label='API server'>API server</a> with the <code>--authorization-mode</code> flag set to a comma-separated list that includes <code>RBAC</code>; for example:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kube-apiserver --authorization-mode<span style=color:#666>=</span>Example,RBAC --other-options --more-options </span></span></code></pre></div><h2 id=api-overview>API objects</h2><p>The RBAC API declares four kinds of Kubernetes object: <em>Role</em>, <em>ClusterRole</em>, <em>RoleBinding</em> and <em>ClusterRoleBinding</em>. You can describe or amend the RBAC <a class=glossary-tooltip title='An entity in the Kubernetes system, representing part of the state of your cluster.' data-toggle=tooltip data-placement=top href=/docs/concepts/overview/working-with-objects/#kubernetes-objects target=_blank aria-label=objects>objects</a> using tools such as <code>kubectl</code>, just like any other Kubernetes object.</p><div class="alert alert-caution" role=alert><h4 class=alert-heading>Caution:</h4>These objects, by design, impose access restrictions. If you are making changes to a cluster as you learn, see <a href=#privilege-escalation-prevention-and-bootstrapping>privilege escalation prevention and bootstrapping</a> to understand how those restrictions can prevent you making some changes.</div><h3 id=role-and-clusterrole>Role and ClusterRole</h3><p>An RBAC <em>Role</em> or <em>ClusterRole</em> contains rules that represent a set of permissions. Permissions are purely additive (there are no "deny" rules).</p><p>A Role always sets permissions within a particular <a class=glossary-tooltip title='An abstraction used by Kubernetes to support isolation of groups of resources within a single cluster.' data-toggle=tooltip data-placement=top href=/docs/concepts/overview/working-with-objects/namespaces target=_blank aria-label=namespace>namespace</a>; when you create a Role, you have to specify the namespace it belongs in.</p><p>ClusterRole, by contrast, is a non-namespaced resource. The resources have different names (Role and ClusterRole) because a Kubernetes object always has to be either namespaced or not namespaced; it can't be both.</p><p>ClusterRoles have several uses. You can use a ClusterRole to:</p><ol><li>define permissions on namespaced resources and be granted access within individual namespace(s)</li><li>define permissions on namespaced resources and be granted access across all namespaces</li><li>define permissions on cluster-scoped resources</li></ol><p>If you want to define a role within a namespace, use a Role; if you want to define a role cluster-wide, use a ClusterRole.</p><h4 id=role-example>Role example</h4><p>Here's an example Role in the "default" namespace that can be used to grant read access to <a class=glossary-tooltip title='A Pod represents a set of running containers in your cluster.' data-toggle=tooltip data-placement=top href=/docs/concepts/workloads/pods/ target=_blank aria-label=pods>pods</a>:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Role<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespace</span>:<span style=color:#bbb> </span>default<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>pod-reader<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>""</span>]<span style=color:#bbb> </span><span style=color:#080;font-style:italic># "" indicates the core API group</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"pods"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"get"</span>,<span style=color:#bbb> </span><span style=color:#b44>"watch"</span>,<span style=color:#bbb> </span><span style=color:#b44>"list"</span>]<span style=color:#bbb> </span></span></span></code></pre></div><h4 id=clusterrole-example>ClusterRole example</h4><p>A ClusterRole can be used to grant the same permissions as a Role. Because ClusterRoles are cluster-scoped, you can also use them to grant access to:</p><ul><li><p>cluster-scoped resources (like <a class=glossary-tooltip title='A node is a worker machine in Kubernetes.' data-toggle=tooltip data-placement=top href=/docs/concepts/architecture/nodes/ target=_blank aria-label=nodes>nodes</a>)</p></li><li><p>non-resource endpoints (like <code>/healthz</code>)</p></li><li><p>namespaced resources (like Pods), across all namespaces</p><p>For example: you can use a ClusterRole to allow a particular user to run <code>kubectl get pods --all-namespaces</code></p></li></ul><p>Here is an example of a ClusterRole that can be used to grant read access to <a class=glossary-tooltip title='Stores sensitive information, such as passwords, OAuth tokens, and ssh keys.' data-toggle=tooltip data-placement=top href=/docs/concepts/configuration/secret/ target=_blank aria-label=secrets>secrets</a> in any particular namespace, or across all namespaces (depending on how it is <a href=#rolebinding-and-clusterrolebinding>bound</a>):</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ClusterRole<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># "namespace" omitted since ClusterRoles are not namespaced</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>secret-reader<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>""</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># at the HTTP level, the name of the resource for accessing Secret</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># objects is "secrets"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"secrets"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"get"</span>,<span style=color:#bbb> </span><span style=color:#b44>"watch"</span>,<span style=color:#bbb> </span><span style=color:#b44>"list"</span>]<span style=color:#bbb> </span></span></span></code></pre></div><p>The name of a Role or a ClusterRole object must be a valid <a href=/docs/concepts/overview/working-with-objects/names/#path-segment-names>path segment name</a>.</p><h3 id=rolebinding-and-clusterrolebinding>RoleBinding and ClusterRoleBinding</h3><p>A role binding grants the permissions defined in a role to a user or set of users. It holds a list of <em>subjects</em> (users, groups, or service accounts), and a reference to the role being granted. A RoleBinding grants permissions within a specific namespace whereas a ClusterRoleBinding grants that access cluster-wide.</p><p>A RoleBinding may reference any Role in the same namespace. Alternatively, a RoleBinding can reference a ClusterRole and bind that ClusterRole to the namespace of the RoleBinding. If you want to bind a ClusterRole to all the namespaces in your cluster, you use a ClusterRoleBinding.</p><p>The name of a RoleBinding or ClusterRoleBinding object must be a valid <a href=/docs/concepts/overview/working-with-objects/names/#path-segment-names>path segment name</a>.</p><h4 id=rolebinding-example>RoleBinding examples</h4><p>Here is an example of a RoleBinding that grants the "pod-reader" Role to the user "jane" within the "default" namespace. This allows "jane" to read pods in the "default" namespace.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># This role binding allows "jane" to read pods in the "default" namespace.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># You need to already have a Role named "pod-reader" in that namespace.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>RoleBinding<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>read-pods<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespace</span>:<span style=color:#bbb> </span>default<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>subjects</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># You can specify more than one "subject"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>User<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>jane<span style=color:#bbb> </span><span style=color:#080;font-style:italic># "name" is case sensitive</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroup</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>roleRef</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># "roleRef" specifies the binding to a Role / ClusterRole</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Role<span style=color:#bbb> </span><span style=color:#080;font-style:italic>#this must be Role or ClusterRole</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>pod-reader<span style=color:#bbb> </span><span style=color:#080;font-style:italic># this must match the name of the Role or ClusterRole you wish to bind to</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroup</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io<span style=color:#bbb> </span></span></span></code></pre></div><p>A RoleBinding can also reference a ClusterRole to grant the permissions defined in that ClusterRole to resources inside the RoleBinding's namespace. This kind of reference lets you define a set of common roles across your cluster, then reuse them within multiple namespaces.</p><p>For instance, even though the following RoleBinding refers to a ClusterRole, "dave" (the subject, case sensitive) will only be able to read Secrets in the "development" namespace, because the RoleBinding's namespace (in its metadata) is "development".</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># This role binding allows "dave" to read secrets in the "development" namespace.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># You need to already have a ClusterRole named "secret-reader".</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>RoleBinding<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>read-secrets<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># The namespace of the RoleBinding determines where the permissions are granted.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># This only grants permissions within the "development" namespace.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespace</span>:<span style=color:#bbb> </span>development<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>subjects</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>User<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>dave<span style=color:#bbb> </span><span style=color:#080;font-style:italic># Name is case sensitive</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroup</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>roleRef</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ClusterRole<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>secret-reader<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroup</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io<span style=color:#bbb> </span></span></span></code></pre></div><h4 id=clusterrolebinding-example>ClusterRoleBinding example</h4><p>To grant permissions across a whole cluster, you can use a ClusterRoleBinding. The following ClusterRoleBinding allows any user in the group "manager" to read secrets in any namespace.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># This cluster role binding allows anyone in the "manager" group to read secrets in any namespace.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ClusterRoleBinding<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>read-secrets-global<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>subjects</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Group<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>manager<span style=color:#bbb> </span><span style=color:#080;font-style:italic># Name is case sensitive</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroup</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>roleRef</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ClusterRole<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>secret-reader<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroup</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io<span style=color:#bbb> </span></span></span></code></pre></div><p>After you create a binding, you cannot change the Role or ClusterRole that it refers to. If you try to change a binding's <code>roleRef</code>, you get a validation error. If you do want to change the <code>roleRef</code> for a binding, you need to remove the binding object and create a replacement.</p><p>There are two reasons for this restriction:</p><ol><li>Making <code>roleRef</code> immutable allows granting someone <code>update</code> permission on an existing binding object, so that they can manage the list of subjects, without being able to change the role that is granted to those subjects.</li><li>A binding to a different role is a fundamentally different binding. Requiring a binding to be deleted/recreated in order to change the <code>roleRef</code> ensures the full list of subjects in the binding is intended to be granted the new role (as opposed to enabling or accidentally modifying only the roleRef without verifying all of the existing subjects should be given the new role's permissions).</li></ol><p>The <code>kubectl auth reconcile</code> command-line utility creates or updates a manifest file containing RBAC objects, and handles deleting and recreating binding objects if required to change the role they refer to. See <a href=#kubectl-auth-reconcile>command usage and examples</a> for more information.</p><h3 id=referring-to-resources>Referring to resources</h3><p>In the Kubernetes API, most resources are represented and accessed using a string representation of their object name, such as <code>pods</code> for a Pod. RBAC refers to resources using exactly the same name that appears in the URL for the relevant API endpoint. Some Kubernetes APIs involve a <em>subresource</em>, such as the logs for a Pod. A request for a Pod's logs looks like:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-http data-lang=http><span style=display:flex><span><span>GET /api/v1/namespaces/{namespace}/pods/{name}/log </span></span></span></code></pre></div><p>In this case, <code>pods</code> is the namespaced resource for Pod resources, and <code>log</code> is a subresource of <code>pods</code>. To represent this in an RBAC role, use a slash (<code>/</code>) to delimit the resource and subresource. To allow a subject to read <code>pods</code> and also access the <code>log</code> subresource for each of those Pods, you write:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Role<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespace</span>:<span style=color:#bbb> </span>default<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>pod-and-pod-logs-reader<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>""</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"pods"</span>,<span style=color:#bbb> </span><span style=color:#b44>"pods/log"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"get"</span>,<span style=color:#bbb> </span><span style=color:#b44>"list"</span>]<span style=color:#bbb> </span></span></span></code></pre></div><p>You can also refer to resources by name for certain requests through the <code>resourceNames</code> list. When specified, requests can be restricted to individual instances of a resource. Here is an example that restricts its subject to only <code>get</code> or <code>update</code> a <a class=glossary-tooltip title='An API object used to store non-confidential data in key-value pairs. Can be consumed as environment variables, command-line arguments, or configuration files in a volume.' data-toggle=tooltip data-placement=top href=/docs/concepts/configuration/configmap/ target=_blank aria-label=ConfigMap>ConfigMap</a> named <code>my-configmap</code>:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Role<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespace</span>:<span style=color:#bbb> </span>default<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>configmap-updater<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>""</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># at the HTTP level, the name of the resource for accessing ConfigMap</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># objects is "configmaps"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"configmaps"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resourceNames</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"my-configmap"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"update"</span>,<span style=color:#bbb> </span><span style=color:#b44>"get"</span>]<span style=color:#bbb> </span></span></span></code></pre></div><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>You cannot restrict <code>create</code> or <code>deletecollection</code> requests by their resource name. For <code>create</code>, this limitation is because the name of the new object may not be known at authorization time. If you restrict <code>list</code> or <code>watch</code> by resourceName, clients must include a <code>metadata.name</code> field selector in their <code>list</code> or <code>watch</code> request that matches the specified resourceName in order to be authorized. For example, <code>kubectl get configmaps --field-selector=metadata.name=my-configmap</code></div><p>Rather than referring to individual <code>resources</code>, <code>apiGroups</code>, and <code>verbs</code>, you can use the wildcard <code>*</code> symbol to refer to all such objects. For <code>nonResourceURLs</code>, you can use the wildcard <code>*</code> as a suffix glob match. For <code>resourceNames</code>, an empty set means that everything is allowed. Here is an example that allows access to perform any current and future action on all current and future resources in the <code>example.com</code> API group. This is similar to the built-in <code>cluster-admin</code> role.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Role<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespace</span>:<span style=color:#bbb> </span>default<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>example.com-superuser<span style=color:#bbb> </span><span style=color:#080;font-style:italic># DO NOT USE THIS ROLE, IT IS JUST AN EXAMPLE</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"example.com"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"*"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"*"</span>]<span style=color:#bbb> </span></span></span></code></pre></div><div class="alert alert-caution" role=alert><h4 class=alert-heading>Caution:</h4>Using wildcards in resource and verb entries could result in overly permissive access being granted to sensitive resources. For instance, if a new resource type is added, or a new subresource is added, or a new custom verb is checked, the wildcard entry automatically grants access, which may be undesirable. The <a href=/docs/concepts/security/rbac-good-practices/#least-privilege>principle of least privilege</a> should be employed, using specific resources and verbs to ensure only the permissions required for the workload to function correctly are applied.</div><h3 id=aggregated-clusterroles>Aggregated ClusterRoles</h3><p>You can <em>aggregate</em> several ClusterRoles into one combined ClusterRole. A controller, running as part of the cluster control plane, watches for ClusterRole objects with an <code>aggregationRule</code> set. The <code>aggregationRule</code> defines a label <a class=glossary-tooltip title='Allows users to filter a list of resources based on labels.' data-toggle=tooltip data-placement=top href=/docs/concepts/overview/working-with-objects/labels/ target=_blank aria-label=selector>selector</a> that the controller uses to match other ClusterRole objects that should be combined into the <code>rules</code> field of this one.</p><div class="alert alert-caution" role=alert><h4 class=alert-heading>Caution:</h4>The control plane overwrites any values that you manually specify in the <code>rules</code> field of an aggregate ClusterRole. If you want to change or add rules, do so in the <code>ClusterRole</code> objects that are selected by the <code>aggregationRule</code>.</div><p>Here is an example aggregated ClusterRole:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ClusterRole<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>monitoring<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>aggregationRule</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>clusterRoleSelectors</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>matchLabels</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>rbac.example.com/aggregate-to-monitoring</span>:<span style=color:#bbb> </span><span style=color:#b44>"true"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span>[]<span style=color:#bbb> </span><span style=color:#080;font-style:italic># The control plane automatically fills in the rules</span><span style=color:#bbb> </span></span></span></code></pre></div><p>If you create a new ClusterRole that matches the label selector of an existing aggregated ClusterRole, that change triggers adding the new rules into the aggregated ClusterRole. Here is an example that adds rules to the "monitoring" ClusterRole, by creating another ClusterRole labeled <code>rbac.example.com/aggregate-to-monitoring: true</code>.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ClusterRole<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>monitoring-endpoints<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>labels</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>rbac.example.com/aggregate-to-monitoring</span>:<span style=color:#bbb> </span><span style=color:#b44>"true"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># When you create the "monitoring-endpoints" ClusterRole,</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># the rules below will be added to the "monitoring" ClusterRole.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>""</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"services"</span>,<span style=color:#bbb> </span><span style=color:#b44>"endpointslices"</span>,<span style=color:#bbb> </span><span style=color:#b44>"pods"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"get"</span>,<span style=color:#bbb> </span><span style=color:#b44>"list"</span>,<span style=color:#bbb> </span><span style=color:#b44>"watch"</span>]<span style=color:#bbb> </span></span></span></code></pre></div><p>The <a href=#default-roles-and-role-bindings>default user-facing roles</a> use ClusterRole aggregation. This lets you, as a cluster administrator, include rules for custom resources, such as those served by <a class=glossary-tooltip title='Custom code that defines a resource to add to your Kubernetes API server without building a complete custom server.' data-toggle=tooltip data-placement=top href=/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/ target=_blank aria-label=CustomResourceDefinitions>CustomResourceDefinitions</a> or aggregated API servers, to extend the default roles.</p><p>For example: the following ClusterRoles let the "admin" and "edit" default roles manage the custom resource named CronTab, whereas the "view" role can perform only read actions on CronTab resources. You can assume that CronTab objects are named <code>"crontabs"</code> in URLs as seen by the API server.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ClusterRole<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>aggregate-cron-tabs-edit<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>labels</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Add these permissions to the "admin" and "edit" default roles.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>rbac.authorization.k8s.io/aggregate-to-admin</span>:<span style=color:#bbb> </span><span style=color:#b44>"true"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>rbac.authorization.k8s.io/aggregate-to-edit</span>:<span style=color:#bbb> </span><span style=color:#b44>"true"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"stable.example.com"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"crontabs"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"get"</span>,<span style=color:#bbb> </span><span style=color:#b44>"list"</span>,<span style=color:#bbb> </span><span style=color:#b44>"watch"</span>,<span style=color:#bbb> </span><span style=color:#b44>"create"</span>,<span style=color:#bbb> </span><span style=color:#b44>"update"</span>,<span style=color:#bbb> </span><span style=color:#b44>"patch"</span>,<span style=color:#bbb> </span><span style=color:#b44>"delete"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#00f;font-weight:700>---</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ClusterRole<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>aggregate-cron-tabs-view<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>labels</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Add these permissions to the "view" default role.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>rbac.authorization.k8s.io/aggregate-to-view</span>:<span style=color:#bbb> </span><span style=color:#b44>"true"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"stable.example.com"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"crontabs"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"get"</span>,<span style=color:#bbb> </span><span style=color:#b44>"list"</span>,<span style=color:#bbb> </span><span style=color:#b44>"watch"</span>]<span style=color:#bbb> </span></span></span></code></pre></div><h4 id=role-examples>Role examples</h4><p>The following examples are excerpts from Role or ClusterRole objects, showing only the <code>rules</code> section.</p><p>Allow reading <code>"pods"</code> resources in the core <a class=glossary-tooltip title='A set of related paths in the Kubernetes API.' data-toggle=tooltip data-placement=top href=/docs/concepts/overview/kubernetes-api/#api-groups-and-versioning target=_blank aria-label='API Group'>API Group</a>:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>""</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># at the HTTP level, the name of the resource for accessing Pod</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># objects is "pods"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"pods"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"get"</span>,<span style=color:#bbb> </span><span style=color:#b44>"list"</span>,<span style=color:#bbb> </span><span style=color:#b44>"watch"</span>]<span style=color:#bbb> </span></span></span></code></pre></div><p>Allow reading/writing Deployments (at the HTTP level: objects with <code>"deployments"</code> in the resource part of their URL) in the <code>"apps"</code> API groups:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"apps"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># at the HTTP level, the name of the resource for accessing Deployment</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># objects is "deployments"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"deployments"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"get"</span>,<span style=color:#bbb> </span><span style=color:#b44>"list"</span>,<span style=color:#bbb> </span><span style=color:#b44>"watch"</span>,<span style=color:#bbb> </span><span style=color:#b44>"create"</span>,<span style=color:#bbb> </span><span style=color:#b44>"update"</span>,<span style=color:#bbb> </span><span style=color:#b44>"patch"</span>,<span style=color:#bbb> </span><span style=color:#b44>"delete"</span>]<span style=color:#bbb> </span></span></span></code></pre></div><p>Allow reading Pods in the core API group, as well as reading or writing Job resources in the <code>"batch"</code> API group:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>""</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># at the HTTP level, the name of the resource for accessing Pod</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># objects is "pods"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"pods"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"get"</span>,<span style=color:#bbb> </span><span style=color:#b44>"list"</span>,<span style=color:#bbb> </span><span style=color:#b44>"watch"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"batch"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># at the HTTP level, the name of the resource for accessing Job</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># objects is "jobs"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"jobs"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"get"</span>,<span style=color:#bbb> </span><span style=color:#b44>"list"</span>,<span style=color:#bbb> </span><span style=color:#b44>"watch"</span>,<span style=color:#bbb> </span><span style=color:#b44>"create"</span>,<span style=color:#bbb> </span><span style=color:#b44>"update"</span>,<span style=color:#bbb> </span><span style=color:#b44>"patch"</span>,<span style=color:#bbb> </span><span style=color:#b44>"delete"</span>]<span style=color:#bbb> </span></span></span></code></pre></div><p>Allow reading a ConfigMap named "my-config" (must be bound with a RoleBinding to limit to a single ConfigMap in a single namespace):</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>""</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># at the HTTP level, the name of the resource for accessing ConfigMap</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># objects is "configmaps"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"configmaps"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resourceNames</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"my-config"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"get"</span>]<span style=color:#bbb> </span></span></span></code></pre></div><p>Allow reading the resource <code>"nodes"</code> in the core group (because a Node is cluster-scoped, this must be in a ClusterRole bound with a ClusterRoleBinding to be effective):</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>""</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># at the HTTP level, the name of the resource for accessing Node</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># objects is "nodes"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"nodes"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"get"</span>,<span style=color:#bbb> </span><span style=color:#b44>"list"</span>,<span style=color:#bbb> </span><span style=color:#b44>"watch"</span>]<span style=color:#bbb> </span></span></span></code></pre></div><p>Allow GET and POST requests to the non-resource endpoint <code>/healthz</code> and all subpaths (must be in a ClusterRole bound with a ClusterRoleBinding to be effective):</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>nonResourceURLs</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"/healthz"</span>,<span style=color:#bbb> </span><span style=color:#b44>"/healthz/*"</span>]<span style=color:#bbb> </span><span style=color:#080;font-style:italic># '*' in a nonResourceURL is a suffix glob match</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"get"</span>,<span style=color:#bbb> </span><span style=color:#b44>"post"</span>]<span style=color:#bbb> </span></span></span></code></pre></div><h3 id=referring-to-subjects>Referring to subjects</h3><p>A RoleBinding or ClusterRoleBinding binds a role to subjects. Subjects can be groups, users or <a class=glossary-tooltip title='Provides an identity for processes that run in a Pod.' data-toggle=tooltip data-placement=top href=/docs/tasks/configure-pod-container/configure-service-account/ target=_blank aria-label=ServiceAccounts>ServiceAccounts</a>.</p><p>Kubernetes represents usernames as strings. These can be: plain names, such as "alice"; email-style names, like "bob@example.com"; or numeric user IDs represented as a string. It is up to you as a cluster administrator to configure the <a href=/docs/reference/access-authn-authz/authentication/>authentication modules</a> so that authentication produces usernames in the format you want.</p><div class="alert alert-caution" role=alert><h4 class=alert-heading>Caution:</h4>The prefix <code>system:</code> is reserved for Kubernetes system use, so you should ensure that you don't have users or groups with names that start with <code>system:</code> by accident. Other than this special prefix, the RBAC authorization system does not require any format for usernames.</div><p>In Kubernetes, Authenticator modules provide group information. Groups, like users, are represented as strings, and that string has no format requirements, other than that the prefix <code>system:</code> is reserved.</p><p><a href=/docs/tasks/configure-pod-container/configure-service-account/>ServiceAccounts</a> have names prefixed with <code>system:serviceaccount:</code>, and belong to groups that have names prefixed with <code>system:serviceaccounts:</code>.</p><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4><ul><li><code>system:serviceaccount:</code> (singular) is the prefix for service account usernames.</li><li><code>system:serviceaccounts:</code> (plural) is the prefix for service account groups.</li></ul></div><h4 id=role-binding-examples>RoleBinding examples</h4><p>The following examples are <code>RoleBinding</code> excerpts that only show the <code>subjects</code> section.</p><p>For a user named <code>alice@example.com</code>:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>subjects</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>User<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>"alice@example.com"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroup</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io<span style=color:#bbb> </span></span></span></code></pre></div><p>For a group named <code>frontend-admins</code>:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>subjects</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Group<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>"frontend-admins"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroup</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io<span style=color:#bbb> </span></span></span></code></pre></div><p>For the default service account in the "kube-system" namespace:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>subjects</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ServiceAccount<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>default<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespace</span>:<span style=color:#bbb> </span>kube-system<span style=color:#bbb> </span></span></span></code></pre></div><p>For all service accounts in the "qa" namespace:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>subjects</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Group<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>system:serviceaccounts:qa<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroup</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io<span style=color:#bbb> </span></span></span></code></pre></div><p>For all service accounts in any namespace:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>subjects</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Group<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>system:serviceaccounts<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroup</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io<span style=color:#bbb> </span></span></span></code></pre></div><p>For all authenticated users:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>subjects</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Group<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>system:authenticated<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroup</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io<span style=color:#bbb> </span></span></span></code></pre></div><p>For all unauthenticated users:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>subjects</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Group<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>system:unauthenticated<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroup</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io<span style=color:#bbb> </span></span></span></code></pre></div><p>For all users:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>subjects</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Group<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>system:authenticated<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroup</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Group<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>system:unauthenticated<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroup</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io<span style=color:#bbb> </span></span></span></code></pre></div><h2 id=default-roles-and-role-bindings>Default roles and role bindings</h2><p>API servers create a set of default ClusterRole and ClusterRoleBinding objects. Many of these are <code>system:</code> prefixed, which indicates that the resource is directly managed by the cluster control plane. All of the default ClusterRoles and ClusterRoleBindings are labeled with <code>kubernetes.io/bootstrapping=rbac-defaults</code>.</p><div class="alert alert-caution" role=alert><h4 class=alert-heading>Caution:</h4>Take care when modifying ClusterRoles and ClusterRoleBindings with names that have a <code>system:</code> prefix. Modifications to these resources can result in non-functional clusters.</div><h3 id=auto-reconciliation>Auto-reconciliation</h3><p>At each start-up, the API server updates default cluster roles with any missing permissions, and updates default cluster role bindings with any missing subjects. This allows the cluster to repair accidental modifications, and helps to keep roles and role bindings up-to-date as permissions and subjects change in new Kubernetes releases.</p><p>To opt out of this reconciliation, set the <code>rbac.authorization.kubernetes.io/autoupdate</code> annotation on a default cluster role or default cluster RoleBinding to <code>false</code>. Be aware that missing default permissions and subjects can result in non-functional clusters.</p><p>Auto-reconciliation is enabled by default if the RBAC authorizer is active.</p><h3 id=discovery-roles>API discovery roles</h3><p>Default cluster role bindings authorize unauthenticated and authenticated users to read API information that is deemed safe to be publicly accessible (including CustomResourceDefinitions). To disable anonymous unauthenticated access, add <code>--anonymous-auth=false</code> flag to the API server configuration.</p><p>To view the configuration of these roles via <code>kubectl</code> run:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl get clusterroles system:discovery -o yaml </span></span></code></pre></div><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>If you edit that ClusterRole, your changes will be overwritten on API server restart via <a href=#auto-reconciliation>auto-reconciliation</a>. To avoid that overwriting, either do not manually edit the role, or disable auto-reconciliation.</div><table><caption>Kubernetes RBAC API discovery roles</caption><col style=width:25%><col style=width:25%><col><thead><tr><th>Default ClusterRole</th><th>Default ClusterRoleBinding</th><th>Description</th></tr></thead><tbody><tr><td><b>system:basic-user</b></td><td><b>system:authenticated</b> group</td><td>Allows a user read-only access to basic information about themselves. Prior to v1.14, this role was also bound to <tt>system:unauthenticated</tt> by default.</td></tr><tr><td><b>system:discovery</b></td><td><b>system:authenticated</b> group</td><td>Allows read-only access to API discovery endpoints needed to discover and negotiate an API level. Prior to v1.14, this role was also bound to <tt>system:unauthenticated</tt> by default.</td></tr><tr><td><b>system:public-info-viewer</b></td><td><b>system:authenticated</b> and <b>system:unauthenticated</b> groups</td><td>Allows read-only access to non-sensitive information about the cluster. Introduced in Kubernetes v1.14.</td></tr></tbody></table><h3 id=user-facing-roles>User-facing roles</h3><p>Some of the default ClusterRoles are not <code>system:</code> prefixed. These are intended to be user-facing roles. They include super-user roles (<code>cluster-admin</code>), roles intended to be granted cluster-wide using ClusterRoleBindings, and roles intended to be granted within particular namespaces using RoleBindings (<code>admin</code>, <code>edit</code>, <code>view</code>).</p><p>User-facing ClusterRoles use <a href=#aggregated-clusterroles>ClusterRole aggregation</a> to allow admins to include rules for custom resources on these ClusterRoles. To add rules to the <code>admin</code>, <code>edit</code>, or <code>view</code> roles, create a ClusterRole with one or more of the following labels:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>labels</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>rbac.authorization.k8s.io/aggregate-to-admin</span>:<span style=color:#bbb> </span><span style=color:#b44>"true"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>rbac.authorization.k8s.io/aggregate-to-edit</span>:<span style=color:#bbb> </span><span style=color:#b44>"true"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>rbac.authorization.k8s.io/aggregate-to-view</span>:<span style=color:#bbb> </span><span style=color:#b44>"true"</span><span style=color:#bbb> </span></span></span></code></pre></div><table><col style=width:25%><col style=width:25%><col><thead><tr><th>Default ClusterRole</th><th>Default ClusterRoleBinding</th><th>Description</th></tr></thead><tbody><tr><td><b>cluster-admin</b></td><td><b>system:masters</b> group</td><td>Allows super-user access to perform any action on any resource. When used in a <b>ClusterRoleBinding</b>, it gives full control over every resource in the cluster and in all namespaces. When used in a <b>RoleBinding</b>, it gives full control over every resource in the role binding's namespace, including the namespace itself.</td></tr><tr><td><b>admin</b></td><td>None</td><td>Allows admin access, intended to be granted within a namespace using a <b>RoleBinding</b>.<p>If used in a <b>RoleBinding</b>, allows read/write access to most resources in a namespace, including the ability to create roles and role bindings within the namespace. This role does not allow write access to resource quota or to the namespace itself. This role also does not allow write access to EndpointSlices (or Endpoints) in clusters created using Kubernetes v1.22+. More information is available in the <a href=#write-access-for-endpoints>"Write Access for EndpointSlices and Endpoints" section</a>.</td></p></tr><tr><td><b>edit</b></td><td>None</td><td>Allows read/write access to most objects in a namespace.<p>This role does not allow viewing or modifying roles or role bindings. However, this role allows accessing Secrets and running Pods as any ServiceAccount in the namespace, so it can be used to gain the API access levels of any ServiceAccount in the namespace. This role also does not allow write access to EndpointSlices (or Endpoints) in clusters created using Kubernetes v1.22+. More information is available in the <a href=#write-access-for-endpoints>"Write Access for EndpointSlices and Endpoints" section</a>.</td></p></tr><tr><td><b>view</b></td><td>None</td><td>Allows read-only access to see most objects in a namespace. It does not allow viewing roles or role bindings.<p>This role does not allow viewing Secrets, since reading the contents of Secrets enables access to ServiceAccount credentials in the namespace, which would allow API access as any ServiceAccount in the namespace (a form of privilege escalation).</td></p></tr></tbody></table><h3 id=core-component-roles>Core component roles</h3><table><col style=width:25%><col style=width:25%><col><thead><tr><th>Default ClusterRole</th><th>Default ClusterRoleBinding</th><th>Description</th></tr></thead><tbody><tr><td><b>system:kube-scheduler</b></td><td><b>system:kube-scheduler</b> user</td><td>Allows access to the resources required by the <a class=glossary-tooltip title='Control plane component that watches for newly created pods with no assigned node, and selects a node for them to run on.' data-toggle=tooltip data-placement=top href=/docs/reference/command-line-tools-reference/kube-scheduler/ target=_blank aria-label=scheduler>scheduler</a> component.</td></tr><tr><td><b>system:volume-scheduler</b></td><td><b>system:kube-scheduler</b> user</td><td>Allows access to the volume resources required by the kube-scheduler component.</td></tr><tr><td><b>system:kube-controller-manager</b></td><td><b>system:kube-controller-manager</b> user</td><td>Allows access to the resources required by the <a class=glossary-tooltip title='Control Plane component that runs controller processes.' data-toggle=tooltip data-placement=top href=/docs/reference/command-line-tools-reference/kube-controller-manager/ target=_blank aria-label='controller manager'>controller manager</a> component. The permissions required by individual controllers are detailed in the <a href=#controller-roles>controller roles</a>.</td></tr><tr><td><b>system:node</b></td><td>None</td><td>Allows access to resources required by the kubelet, <b>including read access to all secrets, and write access to all pod status objects</b>.<p>You should use the <a href=/docs/reference/access-authn-authz/node/>Node authorizer</a> and <a href=/docs/reference/access-authn-authz/admission-controllers/#noderestriction>NodeRestriction admission plugin</a> instead of the <tt>system:node</tt> role, and allow granting API access to kubelets based on the Pods scheduled to run on them.</p><p>The <tt>system:node</tt> role only exists for compatibility with Kubernetes clusters upgraded from versions prior to v1.8.</p></td></tr><tr><td><b>system:node-proxier</b></td><td><b>system:kube-proxy</b> user</td><td>Allows access to the resources required by the <a class=glossary-tooltip title='kube-proxy is a network proxy that runs on each node in the cluster.' data-toggle=tooltip data-placement=top href=/docs/reference/command-line-tools-reference/kube-proxy/ target=_blank aria-label=kube-proxy>kube-proxy</a> component.</td></tr></tbody></table><h3 id=other-component-roles>Other component roles</h3><table><col style=width:25%><col style=width:25%><col><thead><tr><th>Default ClusterRole</th><th>Default ClusterRoleBinding</th><th>Description</th></tr></thead><tbody><tr><td><b>system:auth-delegator</b></td><td>None</td><td>Allows delegated authentication and authorization checks. This is commonly used by add-on API servers for unified authentication and authorization.</td></tr><tr><td><b>system:heapster</b></td><td>None</td><td>Role for the <a href=https://github.com/kubernetes/heapster>Heapster</a> component (deprecated).</td></tr><tr><td><b>system:kube-aggregator</b></td><td>None</td><td>Role for the <a href=https://github.com/kubernetes/kube-aggregator>kube-aggregator</a> component.</td></tr><tr><td><b>system:kube-dns</b></td><td><b>kube-dns</b> service account in the <b>kube-system</b> namespace</td><td>Role for the <a href=/docs/concepts/services-networking/dns-pod-service/>kube-dns</a> component.</td></tr><tr><td><b>system:kubelet-api-admin</b></td><td>None</td><td>Allows full access to the kubelet API.</td></tr><tr><td><b>system:node-bootstrapper</b></td><td>None</td><td>Allows access to the resources required to perform <a href=/docs/reference/access-authn-authz/kubelet-tls-bootstrapping/>kubelet TLS bootstrapping</a>.</td></tr><tr><td><b>system:node-problem-detector</b></td><td>None</td><td>Role for the <a href=https://github.com/kubernetes/node-problem-detector>node-problem-detector</a> component.</td></tr><tr><td><b>system:persistent-volume-provisioner</b></td><td>None</td><td>Allows access to the resources required by most <a href=/docs/concepts/storage/persistent-volumes/#dynamic>dynamic volume provisioners</a>.</td></tr><tr><td><b>system:monitoring</b></td><td><b>system:monitoring</b> group</td><td>Allows read access to control-plane monitoring endpoints (i.e. <a class=glossary-tooltip title='Control plane component that serves the Kubernetes API.' data-toggle=tooltip data-placement=top href=/docs/concepts/architecture/#kube-apiserver target=_blank aria-label=kube-apiserver>kube-apiserver</a> liveness and readiness endpoints (<tt>/healthz</tt>, <tt>/livez</tt>, <tt>/readyz</tt>), the individual health-check endpoints (<tt>/healthz/*</tt>, <tt>/livez/*</tt>, <tt>/readyz/*</tt>), and <tt>/metrics</tt>). Note that individual health check endpoints and the metric endpoint may expose sensitive information.</td></tr></tbody></table><h3 id=controller-roles>Roles for built-in controllers</h3><p>The Kubernetes <a class=glossary-tooltip title='Control Plane component that runs controller processes.' data-toggle=tooltip data-placement=top href=/docs/reference/command-line-tools-reference/kube-controller-manager/ target=_blank aria-label='controller manager'>controller manager</a> runs <a class=glossary-tooltip title='A control loop that watches the shared state of the cluster through the apiserver and makes changes attempting to move the current state towards the desired state.' data-toggle=tooltip data-placement=top href=/docs/concepts/architecture/controller/ target=_blank aria-label=controllers>controllers</a> that are built in to the Kubernetes control plane. When invoked with <code>--use-service-account-credentials</code>, kube-controller-manager starts each controller using a separate service account. Corresponding roles exist for each built-in controller, prefixed with <code>system:controller:</code>. If the controller manager is not started with <code>--use-service-account-credentials</code>, it runs all control loops using its own credential, which must be granted all the relevant roles. These roles include:</p><ul><li><code>system:controller:attachdetach-controller</code></li><li><code>system:controller:certificate-controller</code></li><li><code>system:controller:clusterrole-aggregation-controller</code></li><li><code>system:controller:cronjob-controller</code></li><li><code>system:controller:daemon-set-controller</code></li><li><code>system:controller:deployment-controller</code></li><li><code>system:controller:disruption-controller</code></li><li><code>system:controller:endpoint-controller</code></li><li><code>system:controller:expand-controller</code></li><li><code>system:controller:generic-garbage-collector</code></li><li><code>system:controller:horizontal-pod-autoscaler</code></li><li><code>system:controller:job-controller</code></li><li><code>system:controller:namespace-controller</code></li><li><code>system:controller:node-controller</code></li><li><code>system:controller:persistent-volume-binder</code></li><li><code>system:controller:pod-garbage-collector</code></li><li><code>system:controller:pv-protection-controller</code></li><li><code>system:controller:pvc-protection-controller</code></li><li><code>system:controller:replicaset-controller</code></li><li><code>system:controller:replication-controller</code></li><li><code>system:controller:resourcequota-controller</code></li><li><code>system:controller:root-ca-cert-publisher</code></li><li><code>system:controller:route-controller</code></li><li><code>system:controller:service-account-controller</code></li><li><code>system:controller:service-controller</code></li><li><code>system:controller:statefulset-controller</code></li><li><code>system:controller:ttl-controller</code></li></ul><h2 id=privilege-escalation-prevention-and-bootstrapping>Privilege escalation prevention and bootstrapping</h2><p>The RBAC API prevents users from escalating privileges by editing roles or role bindings. Because this is enforced at the API level, it applies even when the RBAC authorizer is not in use.</p><h3 id=restrictions-on-role-creation-or-update>Restrictions on role creation or update</h3><p>You can only create/update a role if at least one of the following things is true:</p><ol><li>You already have all the permissions contained in the role, at the same scope as the object being modified (cluster-wide for a ClusterRole, within the same namespace or cluster-wide for a Role).</li><li>You are granted explicit permission to perform the <code>escalate</code> verb on the <code>roles</code> or <code>clusterroles</code> resource in the <code>rbac.authorization.k8s.io</code> API group.</li></ol><p>For example, if <code>user-1</code> does not have the ability to list Secrets cluster-wide, they cannot create a ClusterRole containing that permission. To allow a user to create/update roles:</p><ol><li>Grant them a role that allows them to create/update Role or ClusterRole objects, as desired.</li><li>Grant them permission to include specific permissions in the roles they create/update:<ul><li>implicitly, by giving them those permissions (if they attempt to create or modify a Role or ClusterRole with permissions they themselves have not been granted, the API request will be forbidden)</li><li>or explicitly allow specifying any permission in a <code>Role</code> or <code>ClusterRole</code> by giving them permission to perform the <code>escalate</code> verb on <code>roles</code> or <code>clusterroles</code> resources in the <code>rbac.authorization.k8s.io</code> API group</li></ul></li></ol><h3 id=restrictions-on-role-binding-creation-or-update>Restrictions on role binding creation or update</h3><p>You can only create/update a role binding if you already have all the permissions contained in the referenced role (at the same scope as the role binding) <em>or</em> if you have been authorized to perform the <code>bind</code> verb on the referenced role. For example, if <code>user-1</code> does not have the ability to list Secrets cluster-wide, they cannot create a ClusterRoleBinding to a role that grants that permission. To allow a user to create/update role bindings:</p><ol><li>Grant them a role that allows them to create/update RoleBinding or ClusterRoleBinding objects, as desired.</li><li>Grant them permissions needed to bind a particular role:<ul><li>implicitly, by giving them the permissions contained in the role.</li><li>explicitly, by giving them permission to perform the <code>bind</code> verb on the particular Role (or ClusterRole).</li></ul></li></ol><p>For example, this ClusterRole and RoleBinding would allow <code>user-1</code> to grant other users the <code>admin</code>, <code>edit</code>, and <code>view</code> roles in the namespace <code>user-1-namespace</code>:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ClusterRole<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>role-grantor<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"rbac.authorization.k8s.io"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"rolebindings"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"create"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"rbac.authorization.k8s.io"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"clusterroles"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"bind"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># omit resourceNames to allow binding any ClusterRole</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resourceNames</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"admin"</span>,<span style=color:#b44>"edit"</span>,<span style=color:#b44>"view"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#00f;font-weight:700>---</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>RoleBinding<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>role-grantor-binding<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespace</span>:<span style=color:#bbb> </span>user-1-namespace<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>roleRef</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroup</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ClusterRole<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>role-grantor<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>subjects</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroup</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>User<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>user-1<span style=color:#bbb> </span></span></span></code></pre></div><p>When bootstrapping the first roles and role bindings, it is necessary for the initial user to grant permissions they do not yet have. To bootstrap initial roles and role bindings:</p><ul><li>Use a credential with the "system:masters" group, which is bound to the "cluster-admin" super-user role by the default bindings.</li></ul><h2 id=command-line-utilities>Command-line utilities</h2><h3 id=kubectl-create-role><code>kubectl create role</code></h3><p>Creates a Role object defining permissions within a single namespace. Examples:</p><ul><li><p>Create a Role named "pod-reader" that allows users to perform <code>get</code>, <code>watch</code> and <code>list</code> on pods:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create role pod-reader --verb<span style=color:#666>=</span>get --verb<span style=color:#666>=</span>list --verb<span style=color:#666>=</span>watch --resource<span style=color:#666>=</span>pods </span></span></code></pre></div></li><li><p>Create a Role named "pod-reader" with resourceNames specified:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create role pod-reader --verb<span style=color:#666>=</span>get --resource<span style=color:#666>=</span>pods --resource-name<span style=color:#666>=</span>readablepod --resource-name<span style=color:#666>=</span>anotherpod </span></span></code></pre></div></li><li><p>Create a Role named "foo" with apiGroups specified:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create role foo --verb<span style=color:#666>=</span>get,list,watch --resource<span style=color:#666>=</span>replicasets.apps </span></span></code></pre></div></li><li><p>Create a Role named "foo" with subresource permissions:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create role foo --verb<span style=color:#666>=</span>get,list,watch --resource<span style=color:#666>=</span>pods,pods/status </span></span></code></pre></div></li><li><p>Create a Role named "my-component-lease-holder" with permissions to get/update a resource with a specific name:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create role my-component-lease-holder --verb<span style=color:#666>=</span>get,list,watch,update --resource<span style=color:#666>=</span>lease --resource-name<span style=color:#666>=</span>my-component </span></span></code></pre></div></li></ul><h3 id=kubectl-create-clusterrole><code>kubectl create clusterrole</code></h3><p>Creates a ClusterRole. Examples:</p><ul><li><p>Create a ClusterRole named "pod-reader" that allows user to perform <code>get</code>, <code>watch</code> and <code>list</code> on pods:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create clusterrole pod-reader --verb<span style=color:#666>=</span>get,list,watch --resource<span style=color:#666>=</span>pods </span></span></code></pre></div></li><li><p>Create a ClusterRole named "pod-reader" with resourceNames specified:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create clusterrole pod-reader --verb<span style=color:#666>=</span>get --resource<span style=color:#666>=</span>pods --resource-name<span style=color:#666>=</span>readablepod --resource-name<span style=color:#666>=</span>anotherpod </span></span></code></pre></div></li><li><p>Create a ClusterRole named "foo" with apiGroups specified:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create clusterrole foo --verb<span style=color:#666>=</span>get,list,watch --resource<span style=color:#666>=</span>replicasets.apps </span></span></code></pre></div></li><li><p>Create a ClusterRole named "foo" with subresource permissions:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create clusterrole foo --verb<span style=color:#666>=</span>get,list,watch --resource<span style=color:#666>=</span>pods,pods/status </span></span></code></pre></div></li><li><p>Create a ClusterRole named "foo" with nonResourceURL specified:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create clusterrole <span style=color:#b44>"foo"</span> --verb<span style=color:#666>=</span>get --non-resource-url<span style=color:#666>=</span>/logs/* </span></span></code></pre></div></li><li><p>Create a ClusterRole named "monitoring" with an aggregationRule specified:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create clusterrole monitoring --aggregation-rule<span style=color:#666>=</span><span style=color:#b44>"rbac.example.com/aggregate-to-monitoring=true"</span> </span></span></code></pre></div></li></ul><h3 id=kubectl-create-rolebinding><code>kubectl create rolebinding</code></h3><p>Grants a Role or ClusterRole within a specific namespace. Examples:</p><ul><li><p>Within the namespace "acme", grant the permissions in the "admin" ClusterRole to a user named "bob":</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create rolebinding bob-admin-binding --clusterrole<span style=color:#666>=</span>admin --user<span style=color:#666>=</span>bob --namespace<span style=color:#666>=</span>acme </span></span></code></pre></div></li><li><p>Within the namespace "acme", grant the permissions in the "view" ClusterRole to the service account in the namespace "acme" named "myapp":</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create rolebinding myapp-view-binding --clusterrole<span style=color:#666>=</span>view --serviceaccount<span style=color:#666>=</span>acme:myapp --namespace<span style=color:#666>=</span>acme </span></span></code></pre></div></li><li><p>Within the namespace "acme", grant the permissions in the "view" ClusterRole to a service account in the namespace "myappnamespace" named "myapp":</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create rolebinding myappnamespace-myapp-view-binding --clusterrole<span style=color:#666>=</span>view --serviceaccount<span style=color:#666>=</span>myappnamespace:myapp --namespace<span style=color:#666>=</span>acme </span></span></code></pre></div></li></ul><h3 id=kubectl-create-clusterrolebinding><code>kubectl create clusterrolebinding</code></h3><p>Grants a ClusterRole across the entire cluster (all namespaces). Examples:</p><ul><li><p>Across the entire cluster, grant the permissions in the "cluster-admin" ClusterRole to a user named "root":</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create clusterrolebinding root-cluster-admin-binding --clusterrole<span style=color:#666>=</span>cluster-admin --user<span style=color:#666>=</span>root </span></span></code></pre></div></li><li><p>Across the entire cluster, grant the permissions in the "system:node-proxier" ClusterRole to a user named "system:kube-proxy":</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create clusterrolebinding kube-proxy-binding --clusterrole<span style=color:#666>=</span>system:node-proxier --user<span style=color:#666>=</span>system:kube-proxy </span></span></code></pre></div></li><li><p>Across the entire cluster, grant the permissions in the "view" ClusterRole to a service account named "myapp" in the namespace "acme":</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create clusterrolebinding myapp-view-binding --clusterrole<span style=color:#666>=</span>view --serviceaccount<span style=color:#666>=</span>acme:myapp </span></span></code></pre></div></li></ul><h3 id=kubectl-auth-reconcile><code>kubectl auth reconcile</code></h3><p>Creates or updates <code>rbac.authorization.k8s.io/v1</code> API objects from a manifest file.</p><p>Missing objects are created, and the containing namespace is created for namespaced objects, if required.</p><p>Existing roles are updated to include the permissions in the input objects, and remove extra permissions if <code>--remove-extra-permissions</code> is specified.</p><p>Existing bindings are updated to include the subjects in the input objects, and remove extra subjects if <code>--remove-extra-subjects</code> is specified.</p><p>Examples:</p><ul><li><p>Test applying a manifest file of RBAC objects, displaying changes that would be made:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl auth reconcile -f my-rbac-rules.yaml --dry-run<span style=color:#666>=</span>client </span></span></code></pre></div></li><li><p>Apply a manifest file of RBAC objects, preserving any extra permissions (in roles) and any extra subjects (in bindings):</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl auth reconcile -f my-rbac-rules.yaml </span></span></code></pre></div></li><li><p>Apply a manifest file of RBAC objects, removing any extra permissions (in roles) and any extra subjects (in bindings):</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl auth reconcile -f my-rbac-rules.yaml --remove-extra-subjects --remove-extra-permissions </span></span></code></pre></div></li></ul><h2 id=service-account-permissions>ServiceAccount permissions</h2><p>Default RBAC policies grant scoped permissions to control-plane components, nodes, and controllers, but grant <em>no permissions</em> to service accounts outside the <code>kube-system</code> namespace (beyond the permissions given by <a href=#discovery-roles>API discovery roles</a>).</p><p>This allows you to grant particular roles to particular ServiceAccounts as needed. Fine-grained role bindings provide greater security, but require more effort to administrate. Broader grants can give unnecessary (and potentially escalating) API access to ServiceAccounts, but are easier to administrate.</p><p>In order from most secure to least secure, the approaches are:</p><ol><li><p>Grant a role to an application-specific service account (best practice)</p><p>This requires the application to specify a <code>serviceAccountName</code> in its pod spec, and for the service account to be created (via the API, application manifest, <code>kubectl create serviceaccount</code>, etc.).</p><p>For example, grant read-only permission within "my-namespace" to the "my-sa" service account:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create rolebinding my-sa-view <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --clusterrole<span style=color:#666>=</span>view <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --serviceaccount<span style=color:#666>=</span>my-namespace:my-sa <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --namespace<span style=color:#666>=</span>my-namespace </span></span></code></pre></div></li><li><p>Grant a role to the "default" service account in a namespace</p><p>If an application does not specify a <code>serviceAccountName</code>, it uses the "default" service account.</p><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>Permissions given to the "default" service account are available to any pod in the namespace that does not specify a <code>serviceAccountName</code>.</div><p>For example, grant read-only permission within "my-namespace" to the "default" service account:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create rolebinding default-view <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --clusterrole<span style=color:#666>=</span>view <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --serviceaccount<span style=color:#666>=</span>my-namespace:default <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --namespace<span style=color:#666>=</span>my-namespace </span></span></code></pre></div><p>Many <a href=/docs/concepts/cluster-administration/addons/>add-ons</a> run as the "default" service account in the <code>kube-system</code> namespace. To allow those add-ons to run with super-user access, grant cluster-admin permissions to the "default" service account in the <code>kube-system</code> namespace.</p><div class="alert alert-caution" role=alert><h4 class=alert-heading>Caution:</h4>Enabling this means the <code>kube-system</code> namespace contains Secrets that grant super-user access to your cluster's API.</div><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create clusterrolebinding add-on-cluster-admin <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --clusterrole<span style=color:#666>=</span>cluster-admin <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --serviceaccount<span style=color:#666>=</span>kube-system:default </span></span></code></pre></div></li><li><p>Grant a role to all service accounts in a namespace</p><p>If you want all applications in a namespace to have a role, no matter what service account they use, you can grant a role to the service account group for that namespace.</p><p>For example, grant read-only permission within "my-namespace" to all service accounts in that namespace:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create rolebinding serviceaccounts-view <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --clusterrole<span style=color:#666>=</span>view <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --group<span style=color:#666>=</span>system:serviceaccounts:my-namespace <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --namespace<span style=color:#666>=</span>my-namespace </span></span></code></pre></div></li><li><p>Grant a limited role to all service accounts cluster-wide (discouraged)</p><p>If you don't want to manage permissions per-namespace, you can grant a cluster-wide role to all service accounts.</p><p>For example, grant read-only permission across all namespaces to all service accounts in the cluster:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create clusterrolebinding serviceaccounts-view <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --clusterrole<span style=color:#666>=</span>view <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --group<span style=color:#666>=</span>system:serviceaccounts </span></span></code></pre></div></li><li><p>Grant super-user access to all service accounts cluster-wide (strongly discouraged)</p><p>If you don't care about partitioning permissions at all, you can grant super-user access to all service accounts.</p><div class="alert alert-danger" role=alert><h4 class=alert-heading>Warning:</h4>This allows any application full access to your cluster, and also grants any user with read access to Secrets (or the ability to create any pod) full access to your cluster.</div><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create clusterrolebinding serviceaccounts-cluster-admin <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --clusterrole<span style=color:#666>=</span>cluster-admin <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --group<span style=color:#666>=</span>system:serviceaccounts </span></span></code></pre></div></li></ol><h2 id=write-access-for-endpoints>Write access for EndpointSlices and Endpoints</h2><p>Kubernetes clusters created before Kubernetes v1.22 include write access to EndpointSlices (and Endpoints) in the aggregated "edit" and "admin" roles. As a mitigation for <a href=https://github.com/kubernetes/kubernetes/issues/103675>CVE-2021-25740</a>, this access is not part of the aggregated roles in clusters that you create using Kubernetes v1.22 or later.</p><p>Existing clusters that have been upgraded to Kubernetes v1.22 will not be subject to this change. The <a href=https://github.com/kubernetes/kubernetes/issues/103675>CVE announcement</a> includes guidance for restricting this access in existing clusters.</p><p>If you want new clusters to retain this level of access in the aggregated roles, you can create the following ClusterRole:</p><div class="highlight code-sample"><div class=copy-code-icon><a href=https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/access/endpoints-aggregated.yaml download=access/endpoints-aggregated.yaml><code>access/endpoints-aggregated.yaml</code> </a><img src=/images/copycode.svg class=icon-copycode onclick='copyCode("access-endpoints-aggregated-yaml")' title="Copy access/endpoints-aggregated.yaml to clipboard"></img></div><div class=includecode id=access-endpoints-aggregated-yaml><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ClusterRole<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>annotations</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kubernetes.io/description</span>:<span style=color:#bbb> </span>|-<span style=color:#b44;font-style:italic> </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> Add endpoints write permissions to the edit and admin roles. This was </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> removed by default in 1.22 because of CVE-2021-25740. See </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> https://issue.k8s.io/103675. This can allow writers to direct LoadBalancer </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> or Ingress implementations to expose backend IPs that would not otherwise </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> be accessible, and can circumvent network policies or security controls </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> intended to prevent/isolate access to those backends. </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> EndpointSlices were never included in the edit or admin roles, so there </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> is nothing to restore for the EndpointSlice API.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>labels</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>rbac.authorization.k8s.io/aggregate-to-edit</span>:<span style=color:#bbb> </span><span style=color:#b44>"true"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>custom:aggregate-to-edit:endpoints<span style=color:#bbb> </span><span style=color:#080;font-style:italic># you can change this if you wish</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>""</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"endpoints"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"create"</span>,<span style=color:#bbb> </span><span style=color:#b44>"delete"</span>,<span style=color:#bbb> </span><span style=color:#b44>"deletecollection"</span>,<span style=color:#bbb> </span><span style=color:#b44>"patch"</span>,<span style=color:#bbb> </span><span style=color:#b44>"update"</span>]<span style=color:#bbb> </span></span></span></code></pre></div></div></div><h2 id=upgrading-from-abac>Upgrading from ABAC</h2><p>Clusters that originally ran older Kubernetes versions often used permissive ABAC policies, including granting full API access to all service accounts.</p><p>Default RBAC policies grant scoped permissions to control-plane components, nodes, and controllers, but grant <em>no permissions</em> to service accounts outside the <code>kube-system</code> namespace (beyond the permissions given by <a href=#discovery-roles>API discovery roles</a>).</p><p>While far more secure, this can be disruptive to existing workloads expecting to automatically receive API permissions. Here are two approaches for managing this transition:</p><h3 id=parallel-authorizers>Parallel authorizers</h3><p>Run both the RBAC and ABAC authorizers, and specify a policy file that contains the <a href=/docs/reference/access-authn-authz/abac/#policy-file-format>legacy ABAC policy</a>:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>--authorization-mode<span style=color:#666>=</span>...,RBAC,ABAC --authorization-policy-file<span style=color:#666>=</span>mypolicy.json </span></span></code></pre></div><p>To explain that first command line option in detail: if earlier authorizers, such as Node, deny a request, then the RBAC authorizer attempts to authorize the API request. If RBAC also denies that API request, the ABAC authorizer is then run. This means that any request allowed by <em>either</em> the RBAC or ABAC policies is allowed.</p><p>When the kube-apiserver is run with a log level of 5 or higher for the RBAC component (<code>--vmodule=rbac*=5</code> or <code>--v=5</code>), you can see RBAC denials in the API server log (prefixed with <code>RBAC</code>). You can use that information to determine which roles need to be granted to which users, groups, or service accounts.</p><p>Once you have <a href=#service-account-permissions>granted roles to service accounts</a> and workloads are running with no RBAC denial messages in the server logs, you can remove the ABAC authorizer.</p><h3 id=permissive-rbac-permissions>Permissive RBAC permissions</h3><p>You can replicate a permissive ABAC policy using RBAC role bindings.</p><div class="alert alert-danger" role=alert><h4 class=alert-heading>Warning:</h4><p>The following policy allows <strong>ALL</strong> service accounts to act as cluster administrators. Any application running in a container receives service account credentials automatically, and could perform any action against the API, including viewing secrets and modifying permissions. This is not a recommended policy.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create clusterrolebinding permissive-binding <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --clusterrole<span style=color:#666>=</span>cluster-admin <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --user<span style=color:#666>=</span>admin <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --user<span style=color:#666>=</span>kubelet <span style=color:#b62;font-weight:700>\ </span></span></span><span style=display:flex><span><span style=color:#b62;font-weight:700></span> --group<span style=color:#666>=</span>system:serviceaccounts </span></span></code></pre></div></div><p>After you have transitioned to use RBAC, you should adjust the access controls for your cluster to ensure that these meet your information security needs.</p></div><div class=td-content style=page-break-before:always><h1 id=pg-9cbb97d4d9f08d67931a1baae4e6519c>5 - Using Node Authorization</h1><p>Node authorization is a special-purpose authorization mode that specifically authorizes API requests made by kubelets.</p><h2 id=overview>Overview</h2><p>The Node authorizer allows a kubelet to perform API operations. This includes:</p><p>Read operations:</p><ul><li>services</li><li>endpoints</li><li>nodes</li><li>pods</li><li>secrets, configmaps, persistent volume claims and persistent volumes related to pods bound to the kubelet's node</li></ul><div class="feature-state-notice feature-alpha" title="Feature Gate: AuthorizeNodeWithSelectors"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.31 [alpha]</code> (enabled by default: false)</div><p>When the <code>AuthorizeNodeWithSelectors</code> feature is enabled (along with the pre-requisite <code>AuthorizeWithSelectors</code> feature), kubelets are only allowed to read their own Node objects, and are only allowed to read pods bound to their node.</p><p>Write operations:</p><ul><li>nodes and node status (enable the <code>NodeRestriction</code> admission plugin to limit a kubelet to modify its own node)</li><li>pods and pod status (enable the <code>NodeRestriction</code> admission plugin to limit a kubelet to modify pods bound to itself)</li><li>events</li></ul><p>Auth-related operations:</p><ul><li>read/write access to the <a href=/docs/reference/access-authn-authz/certificate-signing-requests/>CertificateSigningRequests API</a> for TLS bootstrapping</li><li>the ability to create TokenReviews and SubjectAccessReviews for delegated authentication/authorization checks</li></ul><p>In future releases, the node authorizer may add or remove permissions to ensure kubelets have the minimal set of permissions required to operate correctly.</p><p>In order to be authorized by the Node authorizer, kubelets must use a credential that identifies them as being in the <code>system:nodes</code> group, with a username of <code>system:node:<nodeName></code>. This group and user name format match the identity created for each kubelet as part of <a href=/docs/reference/access-authn-authz/kubelet-tls-bootstrapping/>kubelet TLS bootstrapping</a>.</p><p>The value of <code><nodeName></code> <strong>must</strong> match precisely the name of the node as registered by the kubelet. By default, this is the host name as provided by <code>hostname</code>, or overridden via the <a href=/docs/reference/command-line-tools-reference/kubelet/>kubelet option</a> <code>--hostname-override</code>. However, when using the <code>--cloud-provider</code> kubelet option, the specific hostname may be determined by the cloud provider, ignoring the local <code>hostname</code> and the <code>--hostname-override</code> option. For specifics about how the kubelet determines the hostname, see the <a href=/docs/reference/command-line-tools-reference/kubelet/>kubelet options reference</a>.</p><p>To enable the Node authorizer, start the apiserver with <code>--authorization-mode=Node</code>.</p><p>To limit the API objects kubelets are able to write, enable the <a href=/docs/reference/access-authn-authz/admission-controllers/#noderestriction>NodeRestriction</a> admission plugin by starting the apiserver with <code>--enable-admission-plugins=...,NodeRestriction,...</code></p><h2 id=migration-considerations>Migration considerations</h2><h3 id=kubelets-outside-the-system-nodes-group>Kubelets outside the <code>system:nodes</code> group</h3><p>Kubelets outside the <code>system:nodes</code> group would not be authorized by the <code>Node</code> authorization mode, and would need to continue to be authorized via whatever mechanism currently authorizes them. The node admission plugin would not restrict requests from these kubelets.</p><h3 id=kubelets-with-undifferentiated-usernames>Kubelets with undifferentiated usernames</h3><p>In some deployments, kubelets have credentials that place them in the <code>system:nodes</code> group, but do not identify the particular node they are associated with, because they do not have a username in the <code>system:node:...</code> format. These kubelets would not be authorized by the <code>Node</code> authorization mode, and would need to continue to be authorized via whatever mechanism currently authorizes them.</p><p>The <code>NodeRestriction</code> admission plugin would ignore requests from these kubelets, since the default node identifier implementation would not consider that a node identity.</p></div><div class=td-content style=page-break-before:always><h1 id=pg-215c25173044b8f97e9b0494b0c7e53f>6 - Webhook Mode</h1><p>A WebHook is an HTTP callback: an HTTP POST that occurs when something happens; a simple event-notification via HTTP POST. A web application implementing WebHooks will POST a message to a URL when certain things happen.</p><p>When specified, mode <code>Webhook</code> causes Kubernetes to query an outside REST service when determining user privileges.</p><h2 id=configuration-file-format>Configuration File Format</h2><p>Mode <code>Webhook</code> requires a file for HTTP configuration, specify by the <code>--authorization-webhook-config-file=SOME_FILENAME</code> flag.</p><p>The configuration file uses the <a href=/docs/tasks/access-application-cluster/configure-access-multiple-clusters/>kubeconfig</a> file format. Within the file "users" refers to the API Server webhook and "clusters" refers to the remote service.</p><p>A configuration example which uses HTTPS client auth:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:#080;font-style:italic># Kubernetes API version</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># kind of the API object</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Config<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># clusters refers to the remote service.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>clusters</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>name-of-remote-authz-service<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>cluster</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># CA for verifying the remote service.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>certificate-authority</span>:<span style=color:#bbb> </span>/path/to/ca.pem<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># URL of remote service to query. Must use 'https'. May not include parameters.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>server</span>:<span style=color:#bbb> </span>https://authz.example.com/authorize<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># users refers to the API Server's webhook configuration.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>users</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>name-of-api-server<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>user</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>client-certificate</span>:<span style=color:#bbb> </span>/path/to/cert.pem<span style=color:#bbb> </span><span style=color:#080;font-style:italic># cert for the webhook plugin to use</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>client-key</span>:<span style=color:#bbb> </span>/path/to/key.pem <span style=color:#bbb> </span><span style=color:#080;font-style:italic># key matching the cert</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># kubeconfig files require a context. Provide one for the API Server.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>current-context</span>:<span style=color:#bbb> </span>webhook<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>contexts</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>context</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>cluster</span>:<span style=color:#bbb> </span>name-of-remote-authz-service<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>user</span>:<span style=color:#bbb> </span>name-of-api-server<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>webhook<span style=color:#bbb> </span></span></span></code></pre></div><h2 id=request-payloads>Request Payloads</h2><p>When faced with an authorization decision, the API Server POSTs a JSON- serialized <code>authorization.k8s.io/v1beta1</code> <code>SubjectAccessReview</code> object describing the action. This object contains fields describing the user attempting to make the request, and either details about the resource being accessed or requests attributes.</p><p>Note that webhook API objects are subject to the same <a href=/docs/concepts/overview/kubernetes-api/>versioning compatibility rules</a> as other Kubernetes API objects. Implementers should be aware of looser compatibility promises for beta objects and check the "apiVersion" field of the request to ensure correct deserialization. Additionally, the API Server must enable the <code>authorization.k8s.io/v1beta1</code> API extensions group (<code>--runtime-config=authorization.k8s.io/v1beta1=true</code>).</p><p>An example request body:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"authorization.k8s.io/v1beta1"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"SubjectAccessReview"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"spec"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"resourceAttributes"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"namespace"</span>: <span style=color:#b44>"kittensandponies"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"verb"</span>: <span style=color:#b44>"get"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"group"</span>: <span style=color:#b44>"unicorn.example.org"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"resource"</span>: <span style=color:#b44>"pods"</span> </span></span><span style=display:flex><span> }, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"user"</span>: <span style=color:#b44>"jane"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"group"</span>: [ </span></span><span style=display:flex><span> <span style=color:#b44>"group1"</span>, </span></span><span style=display:flex><span> <span style=color:#b44>"group2"</span> </span></span><span style=display:flex><span> ] </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div><p>The remote service is expected to fill the <code>status</code> field of the request and respond to either allow or disallow access. The response body's <code>spec</code> field is ignored and may be omitted. A permissive response would return:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"authorization.k8s.io/v1beta1"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"SubjectAccessReview"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"status"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"allowed"</span>: <span style=color:#a2f;font-weight:700>true</span> </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div><p>For disallowing access there are two methods.</p><p>The first method is preferred in most cases, and indicates the authorization webhook does not allow, or has "no opinion" about the request, but if other authorizers are configured, they are given a chance to allow the request. If there are no other authorizers, or none of them allow the request, the request is forbidden. The webhook would return:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"authorization.k8s.io/v1beta1"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"SubjectAccessReview"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"status"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"allowed"</span>: <span style=color:#a2f;font-weight:700>false</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"reason"</span>: <span style=color:#b44>"user does not have read access to the namespace"</span> </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div><p>The second method denies immediately, short-circuiting evaluation by other configured authorizers. This should only be used by webhooks that have detailed knowledge of the full authorizer configuration of the cluster. The webhook would return:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"authorization.k8s.io/v1beta1"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"SubjectAccessReview"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"status"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"allowed"</span>: <span style=color:#a2f;font-weight:700>false</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"denied"</span>: <span style=color:#a2f;font-weight:700>true</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"reason"</span>: <span style=color:#b44>"user does not have read access to the namespace"</span> </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div><p>Access to non-resource paths are sent as:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"authorization.k8s.io/v1beta1"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"SubjectAccessReview"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"spec"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"nonResourceAttributes"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"path"</span>: <span style=color:#b44>"/debug"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"verb"</span>: <span style=color:#b44>"get"</span> </span></span><span style=display:flex><span> }, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"user"</span>: <span style=color:#b44>"jane"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"group"</span>: [ </span></span><span style=display:flex><span> <span style=color:#b44>"group1"</span>, </span></span><span style=display:flex><span> <span style=color:#b44>"group2"</span> </span></span><span style=display:flex><span> ] </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div><div class="feature-state-notice feature-alpha" title="Feature Gate: AuthorizeWithSelectors"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.31 [alpha]</code> (enabled by default: false)</div><p>With the <code>AuthorizeWithSelectors</code> feature enabled, field and label selectors in the request are passed to the authorization webhook. The webhook can make authorization decisions informed by the scoped field and label selectors, if it wishes.</p><p>The <a href=/docs/reference/kubernetes-api/authorization-resources/subject-access-review-v1/>SubjectAccessReview API documentation</a> gives guidelines for how these fields should be interpreted and handled by authorization webhooks, specifically using the parsed requirements rather than the raw selector strings, and how to handle unrecognized operators safely.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"authorization.k8s.io/v1beta1"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"SubjectAccessReview"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"spec"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"resourceAttributes"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"verb"</span>: <span style=color:#b44>"list"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"group"</span>: <span style=color:#b44>""</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"resource"</span>: <span style=color:#b44>"pods"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"fieldSelector"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"requirements"</span>: [ </span></span><span style=display:flex><span> {<span style=color:green;font-weight:700>"key"</span>:<span style=color:#b44>"spec.nodeName"</span>, <span style=color:green;font-weight:700>"operator"</span>:<span style=color:#b44>"In"</span>, <span style=color:green;font-weight:700>"values"</span>:[<span style=color:#b44>"mynode"</span>]} </span></span><span style=display:flex><span> ] </span></span><span style=display:flex><span> }, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"labelSelector"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"requirements"</span>: [ </span></span><span style=display:flex><span> {<span style=color:green;font-weight:700>"key"</span>:<span style=color:#b44>"example.com/mykey"</span>, <span style=color:green;font-weight:700>"operator"</span>:<span style=color:#b44>"In"</span>, <span style=color:green;font-weight:700>"values"</span>:[<span style=color:#b44>"myvalue"</span>]} </span></span><span style=display:flex><span> ] </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span> }, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"user"</span>: <span style=color:#b44>"jane"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"group"</span>: [ </span></span><span style=display:flex><span> <span style=color:#b44>"group1"</span>, </span></span><span style=display:flex><span> <span style=color:#b44>"group2"</span> </span></span><span style=display:flex><span> ] </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div><p>Non-resource paths include: <code>/api</code>, <code>/apis</code>, <code>/metrics</code>, <code>/logs</code>, <code>/debug</code>, <code>/healthz</code>, <code>/livez</code>, <code>/openapi/v2</code>, <code>/readyz</code>, and <code>/version.</code> Clients require access to <code>/api</code>, <code>/api/*</code>, <code>/apis</code>, <code>/apis/*</code>, and <code>/version</code> to discover what resources and versions are present on the server. Access to other non-resource paths can be disallowed without restricting access to the REST api.</p><p>For further information, refer to the <a href=/docs/reference/kubernetes-api/authorization-resources/subject-access-review-v1/>SubjectAccessReview API documentation</a> and <a href=https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apiserver/plugin/pkg/authorizer/webhook/webhook.go>webhook.go implementation</a>.</p></div><div class=td-content style=page-break-before:always><h1 id=pg-a5bdc757c01991e5e6ab1a82b90639ea>7 - Using ABAC Authorization</h1><p>Attribute-based access control (ABAC) defines an access control paradigm whereby access rights are granted to users through the use of policies which combine attributes together.</p><h2 id=policy-file-format>Policy File Format</h2><p>To enable <code>ABAC</code> mode, specify <code>--authorization-policy-file=SOME_FILENAME</code> and <code>--authorization-mode=ABAC</code> on startup.</p><p>The file format is <a href=https://jsonlines.org/>one JSON object per line</a>. There should be no enclosing list or map, only one map per line.</p><p>Each line is a "policy object", where each such object is a map with the following properties:</p><ul><li>Versioning properties:<ul><li><code>apiVersion</code>, type string; valid values are "abac.authorization.kubernetes.io/v1beta1". Allows versioning and conversion of the policy format.</li><li><code>kind</code>, type string: valid values are "Policy". Allows versioning and conversion of the policy format.</li></ul></li><li><code>spec</code> property set to a map with the following properties:<ul><li>Subject-matching properties:<ul><li><code>user</code>, type string; the user-string from <code>--token-auth-file</code>. If you specify <code>user</code>, it must match the username of the authenticated user.</li><li><code>group</code>, type string; if you specify <code>group</code>, it must match one of the groups of the authenticated user. <code>system:authenticated</code> matches all authenticated requests. <code>system:unauthenticated</code> matches all unauthenticated requests.</li></ul></li><li>Resource-matching properties:<ul><li><code>apiGroup</code>, type string; an API group.<ul><li>Ex: <code>apps</code>, <code>networking.k8s.io</code></li><li>Wildcard: <code>*</code> matches all API groups.</li></ul></li><li><code>namespace</code>, type string; a namespace.<ul><li>Ex: <code>kube-system</code></li><li>Wildcard: <code>*</code> matches all resource requests.</li></ul></li><li><code>resource</code>, type string; a resource type<ul><li>Ex: <code>pods</code>, <code>deployments</code></li><li>Wildcard: <code>*</code> matches all resource requests.</li></ul></li></ul></li><li>Non-resource-matching properties:<ul><li><code>nonResourcePath</code>, type string; non-resource request paths.<ul><li>Ex: <code>/version</code> or <code>/apis</code></li><li>Wildcard:<ul><li><code>*</code> matches all non-resource requests.</li><li><code>/foo/*</code> matches all subpaths of <code>/foo/</code>.</li></ul></li></ul></li></ul></li><li><code>readonly</code>, type boolean, when true, means that the Resource-matching policy only applies to get, list, and watch operations, Non-resource-matching policy only applies to get operation.</li></ul></li></ul><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4><p>An unset property is the same as a property set to the zero value for its type (e.g. empty string, 0, false). However, unset should be preferred for readability.</p><p>In the future, policies may be expressed in a JSON format, and managed via a REST interface.</p></div><h2 id=authorization-algorithm>Authorization Algorithm</h2><p>A request has attributes which correspond to the properties of a policy object.</p><p>When a request is received, the attributes are determined. Unknown attributes are set to the zero value of its type (e.g. empty string, 0, false).</p><p>A property set to <code>"*"</code> will match any value of the corresponding attribute.</p><p>The tuple of attributes is checked for a match against every policy in the policy file. If at least one line matches the request attributes, then the request is authorized (but may fail later validation).</p><p>To permit any authenticated user to do something, write a policy with the group property set to <code>"system:authenticated"</code>.</p><p>To permit any unauthenticated user to do something, write a policy with the group property set to <code>"system:unauthenticated"</code>.</p><p>To permit a user to do anything, write a policy with the apiGroup, namespace, resource, and nonResourcePath properties set to <code>"*"</code>.</p><h2 id=kubectl>Kubectl</h2><p>Kubectl uses the <code>/api</code> and <code>/apis</code> endpoints of apiserver to discover served resource types, and validates objects sent to the API by create/update operations using schema information located at <code>/openapi/v2</code>.</p><p>When using ABAC authorization, those special resources have to be explicitly exposed via the <code>nonResourcePath</code> property in a policy (see <a href=#examples>examples</a> below):</p><ul><li><code>/api</code>, <code>/api/*</code>, <code>/apis</code>, and <code>/apis/*</code> for API version negotiation.</li><li><code>/version</code> for retrieving the server version via <code>kubectl version</code>.</li><li><code>/swaggerapi/*</code> for create/update operations.</li></ul><p>To inspect the HTTP calls involved in a specific kubectl operation you can turn up the verbosity:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl --v<span style=color:#666>=</span><span style=color:#666>8</span> version </span></span></code></pre></div><h2 id=examples>Examples</h2><ol><li><p>Alice can do anything to all resources:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{<span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"abac.authorization.kubernetes.io/v1beta1"</span>, <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"Policy"</span>, <span style=color:green;font-weight:700>"spec"</span>: {<span style=color:green;font-weight:700>"user"</span>: <span style=color:#b44>"alice"</span>, <span style=color:green;font-weight:700>"namespace"</span>: <span style=color:#b44>"*"</span>, <span style=color:green;font-weight:700>"resource"</span>: <span style=color:#b44>"*"</span>, <span style=color:green;font-weight:700>"apiGroup"</span>: <span style=color:#b44>"*"</span>}} </span></span></code></pre></div></li><li><p>The kubelet can read any pods:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{<span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"abac.authorization.kubernetes.io/v1beta1"</span>, <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"Policy"</span>, <span style=color:green;font-weight:700>"spec"</span>: {<span style=color:green;font-weight:700>"user"</span>: <span style=color:#b44>"kubelet"</span>, <span style=color:green;font-weight:700>"namespace"</span>: <span style=color:#b44>"*"</span>, <span style=color:green;font-weight:700>"resource"</span>: <span style=color:#b44>"pods"</span>, <span style=color:green;font-weight:700>"readonly"</span>: <span style=color:#a2f;font-weight:700>true</span>}} </span></span></code></pre></div></li><li><p>The kubelet can read and write events:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{<span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"abac.authorization.kubernetes.io/v1beta1"</span>, <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"Policy"</span>, <span style=color:green;font-weight:700>"spec"</span>: {<span style=color:green;font-weight:700>"user"</span>: <span style=color:#b44>"kubelet"</span>, <span style=color:green;font-weight:700>"namespace"</span>: <span style=color:#b44>"*"</span>, <span style=color:green;font-weight:700>"resource"</span>: <span style=color:#b44>"events"</span>}} </span></span></code></pre></div></li><li><p>Bob can just read pods in namespace "projectCaribou":</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{<span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"abac.authorization.kubernetes.io/v1beta1"</span>, <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"Policy"</span>, <span style=color:green;font-weight:700>"spec"</span>: {<span style=color:green;font-weight:700>"user"</span>: <span style=color:#b44>"bob"</span>, <span style=color:green;font-weight:700>"namespace"</span>: <span style=color:#b44>"projectCaribou"</span>, <span style=color:green;font-weight:700>"resource"</span>: <span style=color:#b44>"pods"</span>, <span style=color:green;font-weight:700>"readonly"</span>: <span style=color:#a2f;font-weight:700>true</span>}} </span></span></code></pre></div></li><li><p>Anyone can make read-only requests to all non-resource paths:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{<span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"abac.authorization.kubernetes.io/v1beta1"</span>, <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"Policy"</span>, <span style=color:green;font-weight:700>"spec"</span>: {<span style=color:green;font-weight:700>"group"</span>: <span style=color:#b44>"system:authenticated"</span>, <span style=color:green;font-weight:700>"readonly"</span>: <span style=color:#a2f;font-weight:700>true</span>, <span style=color:green;font-weight:700>"nonResourcePath"</span>: <span style=color:#b44>"*"</span>}} </span></span><span style=display:flex><span> {<span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"abac.authorization.kubernetes.io/v1beta1"</span>, <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"Policy"</span>, <span style=color:green;font-weight:700>"spec"</span>: {<span style=color:green;font-weight:700>"group"</span>: <span style=color:#b44>"system:unauthenticated"</span>, <span style=color:green;font-weight:700>"readonly"</span>: <span style=color:#a2f;font-weight:700>true</span>, <span style=color:green;font-weight:700>"nonResourcePath"</span>: <span style=color:#b44>"*"</span>}} </span></span></code></pre></div></li></ol><p><a href=https://releases.k8s.io/v1.31.0/pkg/auth/authorizer/abac/example_policy_file.jsonl>Complete file example</a></p><h2 id=a-quick-note-on-service-accounts>A quick note on service accounts</h2><p>Every service account has a corresponding ABAC username, and that service account's username is generated according to the naming convention:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>system:serviceaccount:<namespace>:<serviceaccountname> </span></span></code></pre></div><p>Creating a new namespace leads to the creation of a new service account in the following format:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>system:serviceaccount:<namespace>:default </span></span></code></pre></div><p>For example, if you wanted to grant the default service account (in the <code>kube-system</code> namespace) full privilege to the API using ABAC, you would add this line to your policy file:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{<span style=color:green;font-weight:700>"apiVersion"</span>:<span style=color:#b44>"abac.authorization.kubernetes.io/v1beta1"</span>,<span style=color:green;font-weight:700>"kind"</span>:<span style=color:#b44>"Policy"</span>,<span style=color:green;font-weight:700>"spec"</span>:{<span style=color:green;font-weight:700>"user"</span>:<span style=color:#b44>"system:serviceaccount:kube-system:default"</span>,<span style=color:green;font-weight:700>"namespace"</span>:<span style=color:#b44>"*"</span>,<span style=color:green;font-weight:700>"resource"</span>:<span style=color:#b44>"*"</span>,<span style=color:green;font-weight:700>"apiGroup"</span>:<span style=color:#b44>"*"</span>}} </span></span></code></pre></div><p>The apiserver will need to be restarted to pick up the new policy lines.</p></div><div class=td-content style=page-break-before:always><h1 id=pg-518807b9b00bda46d7c7e6e0b17c18f8>8 - Admission Controllers Reference</h1><p>This page provides an overview of Admission Controllers.</p><h2 id=what-are-they>What are they?</h2><p>An <em>admission controller</em> is a piece of code that intercepts requests to the Kubernetes API server prior to persistence of the object, but after the request is authenticated and authorized.</p><p>Admission controllers may be <em>validating</em>, <em>mutating</em>, or both. Mutating controllers may modify objects related to the requests they admit; validating controllers may not.</p><p>Admission controllers limit requests to create, delete, modify objects. Admission controllers can also block custom verbs, such as a request connect to a Pod via an API server proxy. Admission controllers do <em>not</em> (and cannot) block requests to read (<strong>get</strong>, <strong>watch</strong> or <strong>list</strong>) objects.</p><p>The admission controllers in Kubernetes 1.31 consist of the <a href=#what-does-each-admission-controller-do>list</a> below, are compiled into the <code>kube-apiserver</code> binary, and may only be configured by the cluster administrator. In that list, there are two special controllers: MutatingAdmissionWebhook and ValidatingAdmissionWebhook. These execute the mutating and validating (respectively) <a href=/docs/reference/access-authn-authz/extensible-admission-controllers/#admission-webhooks>admission control webhooks</a> which are configured in the API.</p><h2 id=admission-control-phases>Admission control phases</h2><p>The admission control process proceeds in two phases. In the first phase, mutating admission controllers are run. In the second phase, validating admission controllers are run. Note again that some of the controllers are both.</p><p>If any of the controllers in either phase reject the request, the entire request is rejected immediately and an error is returned to the end-user.</p><p>Finally, in addition to sometimes mutating the object in question, admission controllers may sometimes have side effects, that is, mutate related resources as part of request processing. Incrementing quota usage is the canonical example of why this is necessary. Any such side-effect needs a corresponding reclamation or reconciliation process, as a given admission controller does not know for sure that a given request will pass all of the other admission controllers.</p><h2 id=why-do-i-need-them>Why do I need them?</h2><p>Several important features of Kubernetes require an admission controller to be enabled in order to properly support the feature. As a result, a Kubernetes API server that is not properly configured with the right set of admission controllers is an incomplete server and will not support all the features you expect.</p><h2 id=how-do-i-turn-on-an-admission-controller>How do I turn on an admission controller?</h2><p>The Kubernetes API server flag <code>enable-admission-plugins</code> takes a comma-delimited list of admission control plugins to invoke prior to modifying objects in the cluster. For example, the following command line enables the <code>NamespaceLifecycle</code> and the <code>LimitRanger</code> admission control plugins:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kube-apiserver --enable-admission-plugins<span style=color:#666>=</span>NamespaceLifecycle,LimitRanger ... </span></span></code></pre></div><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>Depending on the way your Kubernetes cluster is deployed and how the API server is started, you may need to apply the settings in different ways. For example, you may have to modify the systemd unit file if the API server is deployed as a systemd service, you may modify the manifest file for the API server if Kubernetes is deployed in a self-hosted way.</div><h2 id=how-do-i-turn-off-an-admission-controller>How do I turn off an admission controller?</h2><p>The Kubernetes API server flag <code>disable-admission-plugins</code> takes a comma-delimited list of admission control plugins to be disabled, even if they are in the list of plugins enabled by default.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kube-apiserver --disable-admission-plugins<span style=color:#666>=</span>PodNodeSelector,AlwaysDeny ... </span></span></code></pre></div><h2 id=which-plugins-are-enabled-by-default>Which plugins are enabled by default?</h2><p>To see which admission plugins are enabled:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kube-apiserver -h | grep enable-admission-plugins </span></span></code></pre></div><p>In Kubernetes 1.31, the default ones are:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, DefaultStorageClass, DefaultTolerationSeconds, LimitRanger, MutatingAdmissionWebhook, NamespaceLifecycle, PersistentVolumeClaimResize, PodSecurity, Priority, ResourceQuota, RuntimeClass, ServiceAccount, StorageObjectInUseProtection, TaintNodesByCondition, ValidatingAdmissionPolicy, ValidatingAdmissionWebhook </span></span></code></pre></div><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>The <a href=#validatingadmissionpolicy><code>ValidatingAdmissionPolicy</code></a> admission plugin is enabled by default, but is only active if you enable the <code>ValidatingAdmissionPolicy</code> <a href=/docs/reference/command-line-tools-reference/feature-gates/>feature gate</a> <strong>and</strong> the <code>admissionregistration.k8s.io/v1alpha1</code> API.</div><h2 id=what-does-each-admission-controller-do>What does each admission controller do?</h2><h3 id=alwaysadmit>AlwaysAdmit</h3><div class="feature-state-notice feature-deprecated"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.13 [deprecated]</code></div><p><strong>Type</strong>: Validating.</p><p>This admission controller allows all pods into the cluster. It is <strong>deprecated</strong> because its behavior is the same as if there were no admission controller at all.</p><h3 id=alwaysdeny>AlwaysDeny</h3><div class="feature-state-notice feature-deprecated"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.13 [deprecated]</code></div><p><strong>Type</strong>: Validating.</p><p>Rejects all requests. AlwaysDeny is <strong>deprecated</strong> as it has no real meaning.</p><h3 id=alwayspullimages>AlwaysPullImages</h3><p><strong>Type</strong>: Mutating and Validating.</p><p>This admission controller modifies every new Pod to force the image pull policy to <code>Always</code>. This is useful in a multitenant cluster so that users can be assured that their private images can only be used by those who have the credentials to pull them. Without this admission controller, once an image has been pulled to a node, any pod from any user can use it by knowing the image's name (assuming the Pod is scheduled onto the right node), without any authorization check against the image. When this admission controller is enabled, images are always pulled prior to starting containers, which means valid credentials are required.</p><h3 id=certificateapproval>CertificateApproval</h3><p><strong>Type</strong>: Validating.</p><p>This admission controller observes requests to approve CertificateSigningRequest resources and performs additional authorization checks to ensure the approving user has permission to <strong>approve</strong> certificate requests with the <code>spec.signerName</code> requested on the CertificateSigningRequest resource.</p><p>See <a href=/docs/reference/access-authn-authz/certificate-signing-requests/>Certificate Signing Requests</a> for more information on the permissions required to perform different actions on CertificateSigningRequest resources.</p><h3 id=certificatesigning>CertificateSigning</h3><p><strong>Type</strong>: Validating.</p><p>This admission controller observes updates to the <code>status.certificate</code> field of CertificateSigningRequest resources and performs an additional authorization checks to ensure the signing user has permission to <strong>sign</strong> certificate requests with the <code>spec.signerName</code> requested on the CertificateSigningRequest resource.</p><p>See <a href=/docs/reference/access-authn-authz/certificate-signing-requests/>Certificate Signing Requests</a> for more information on the permissions required to perform different actions on CertificateSigningRequest resources.</p><h3 id=certificatesubjectrestriction>CertificateSubjectRestriction</h3><p><strong>Type</strong>: Validating.</p><p>This admission controller observes creation of CertificateSigningRequest resources that have a <code>spec.signerName</code> of <code>kubernetes.io/kube-apiserver-client</code>. It rejects any request that specifies a 'group' (or 'organization attribute') of <code>system:masters</code>.</p><h3 id=defaultingressclass>DefaultIngressClass</h3><p><strong>Type</strong>: Mutating.</p><p>This admission controller observes creation of <code>Ingress</code> objects that do not request any specific ingress class and automatically adds a default ingress class to them. This way, users that do not request any special ingress class do not need to care about them at all and they will get the default one.</p><p>This admission controller does not do anything when no default ingress class is configured. When more than one ingress class is marked as default, it rejects any creation of <code>Ingress</code> with an error and an administrator must revisit their <code>IngressClass</code> objects and mark only one as default (with the annotation "ingressclass.kubernetes.io/is-default-class"). This admission controller ignores any <code>Ingress</code> updates; it acts only on creation.</p><p>See the <a href=/docs/concepts/services-networking/ingress/>Ingress</a> documentation for more about ingress classes and how to mark one as default.</p><h3 id=defaultstorageclass>DefaultStorageClass</h3><p><strong>Type</strong>: Mutating.</p><p>This admission controller observes creation of <code>PersistentVolumeClaim</code> objects that do not request any specific storage class and automatically adds a default storage class to them. This way, users that do not request any special storage class do not need to care about them at all and they will get the default one.</p><p>This admission controller does not do anything when no default storage class is configured. When more than one storage class is marked as default, it rejects any creation of <code>PersistentVolumeClaim</code> with an error and an administrator must revisit their <code>StorageClass</code> objects and mark only one as default. This admission controller ignores any <code>PersistentVolumeClaim</code> updates; it acts only on creation.</p><p>See <a href=/docs/concepts/storage/persistent-volumes/>persistent volume</a> documentation about persistent volume claims and storage classes and how to mark a storage class as default.</p><h3 id=defaulttolerationseconds>DefaultTolerationSeconds</h3><p><strong>Type</strong>: Mutating.</p><p>This admission controller sets the default forgiveness toleration for pods to tolerate the taints <code>notready:NoExecute</code> and <code>unreachable:NoExecute</code> based on the k8s-apiserver input parameters <code>default-not-ready-toleration-seconds</code> and <code>default-unreachable-toleration-seconds</code> if the pods don't already have toleration for taints <code>node.kubernetes.io/not-ready:NoExecute</code> or <code>node.kubernetes.io/unreachable:NoExecute</code>. The default value for <code>default-not-ready-toleration-seconds</code> and <code>default-unreachable-toleration-seconds</code> is 5 minutes.</p><h3 id=denyserviceexternalips>DenyServiceExternalIPs</h3><p><strong>Type</strong>: Validating.</p><p>This admission controller rejects all net-new usage of the <code>Service</code> field <code>externalIPs</code>. This feature is very powerful (allows network traffic interception) and not well controlled by policy. When enabled, users of the cluster may not create new Services which use <code>externalIPs</code> and may not add new values to <code>externalIPs</code> on existing <code>Service</code> objects. Existing uses of <code>externalIPs</code> are not affected, and users may remove values from <code>externalIPs</code> on existing <code>Service</code> objects.</p><p>Most users do not need this feature at all, and cluster admins should consider disabling it. Clusters that do need to use this feature should consider using some custom policy to manage usage of it.</p><p>This admission controller is disabled by default.</p><h3 id=eventratelimit>EventRateLimit</h3><div class="feature-state-notice feature-alpha"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.13 [alpha]</code></div><p><strong>Type</strong>: Validating.</p><p>This admission controller mitigates the problem where the API server gets flooded by requests to store new Events. The cluster admin can specify event rate limits by:</p><ul><li>Enabling the <code>EventRateLimit</code> admission controller;</li><li>Referencing an <code>EventRateLimit</code> configuration file from the file provided to the API server's command line flag <code>--admission-control-config-file</code>:</li></ul><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>apiserver.config.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>AdmissionConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>plugins</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>EventRateLimit<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>path</span>:<span style=color:#bbb> </span>eventconfig.yaml<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#00f;font-weight:700>...</span><span style=color:#bbb> </span></span></span></code></pre></div><p>There are four types of limits that can be specified in the configuration:</p><ul><li><code>Server</code>: All Event requests (creation or modifications) received by the API server share a single bucket.</li><li><code>Namespace</code>: Each namespace has a dedicated bucket.</li><li><code>User</code>: Each user is allocated a bucket.</li><li><code>SourceAndObject</code>: A bucket is assigned by each combination of source and involved object of the event.</li></ul><p>Below is a sample <code>eventconfig.yaml</code> for such a configuration:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>eventratelimit.admission.k8s.io/v1alpha1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Configuration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>limits</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>type</span>:<span style=color:#bbb> </span>Namespace<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>qps</span>:<span style=color:#bbb> </span><span style=color:#666>50</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>burst</span>:<span style=color:#bbb> </span><span style=color:#666>100</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>cacheSize</span>:<span style=color:#bbb> </span><span style=color:#666>2000</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>type</span>:<span style=color:#bbb> </span>User<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>qps</span>:<span style=color:#bbb> </span><span style=color:#666>10</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>burst</span>:<span style=color:#bbb> </span><span style=color:#666>50</span><span style=color:#bbb> </span></span></span></code></pre></div><p>See the <a href=/docs/reference/config-api/apiserver-eventratelimit.v1alpha1/>EventRateLimit Config API (v1alpha1)</a> for more details.</p><p>This admission controller is disabled by default.</p><h3 id=extendedresourcetoleration>ExtendedResourceToleration</h3><p><strong>Type</strong>: Mutating.</p><p>This plug-in facilitates creation of dedicated nodes with extended resources. If operators want to create dedicated nodes with extended resources (like GPUs, FPGAs etc.), they are expected to <a href=/docs/concepts/scheduling-eviction/taint-and-toleration/#example-use-cases>taint the node</a> with the extended resource name as the key. This admission controller, if enabled, automatically adds tolerations for such taints to pods requesting extended resources, so users don't have to manually add these tolerations.</p><p>This admission controller is disabled by default.</p><h3 id=imagepolicywebhook>ImagePolicyWebhook</h3><p><strong>Type</strong>: Validating.</p><p>The ImagePolicyWebhook admission controller allows a backend webhook to make admission decisions.</p><p>This admission controller is disabled by default.</p><h4 id=imagereview-config-file-format>Configuration file format</h4><p>ImagePolicyWebhook uses a configuration file to set options for the behavior of the backend. This file may be json or yaml and has the following format:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>imagePolicy</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kubeConfigFile</span>:<span style=color:#bbb> </span>/path/to/kubeconfig/for/backend<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># time in s to cache approval</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>allowTTL</span>:<span style=color:#bbb> </span><span style=color:#666>50</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># time in s to cache denial</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>denyTTL</span>:<span style=color:#bbb> </span><span style=color:#666>50</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># time in ms to wait between retries</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>retryBackoff</span>:<span style=color:#bbb> </span><span style=color:#666>500</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># determines behavior if the webhook backend fails</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>defaultAllow</span>:<span style=color:#bbb> </span><span style=color:#a2f;font-weight:700>true</span><span style=color:#bbb> </span></span></span></code></pre></div><p>Reference the ImagePolicyWebhook configuration file from the file provided to the API server's command line flag <code>--admission-control-config-file</code>:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>apiserver.config.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>AdmissionConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>plugins</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>ImagePolicyWebhook<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>path</span>:<span style=color:#bbb> </span>imagepolicyconfig.yaml<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#00f;font-weight:700>...</span><span style=color:#bbb> </span></span></span></code></pre></div><p>Alternatively, you can embed the configuration directly in the file:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>apiserver.config.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>AdmissionConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>plugins</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>ImagePolicyWebhook<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>configuration</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>imagePolicy</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kubeConfigFile</span>:<span style=color:#bbb> </span><path-to-kubeconfig-file><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>allowTTL</span>:<span style=color:#bbb> </span><span style=color:#666>50</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>denyTTL</span>:<span style=color:#bbb> </span><span style=color:#666>50</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>retryBackoff</span>:<span style=color:#bbb> </span><span style=color:#666>500</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>defaultAllow</span>:<span style=color:#bbb> </span><span style=color:#a2f;font-weight:700>true</span><span style=color:#bbb> </span></span></span></code></pre></div><p>The ImagePolicyWebhook config file must reference a <a href=/docs/tasks/access-application-cluster/configure-access-multiple-clusters/>kubeconfig</a> formatted file which sets up the connection to the backend. It is required that the backend communicate over TLS.</p><p>The kubeconfig file's <code>cluster</code> field must point to the remote service, and the <code>user</code> field must contain the returned authorizer.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:#080;font-style:italic># clusters refers to the remote service.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>clusters</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>name-of-remote-imagepolicy-service<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>cluster</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>certificate-authority</span>:<span style=color:#bbb> </span>/path/to/ca.pem <span style=color:#bbb> </span><span style=color:#080;font-style:italic># CA for verifying the remote service.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>server</span>:<span style=color:#bbb> </span>https://images.example.com/policy<span style=color:#bbb> </span><span style=color:#080;font-style:italic># URL of remote service to query. Must use 'https'.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># users refers to the API server's webhook configuration.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>users</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>name-of-api-server<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>user</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>client-certificate</span>:<span style=color:#bbb> </span>/path/to/cert.pem<span style=color:#bbb> </span><span style=color:#080;font-style:italic># cert for the webhook admission controller to use</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>client-key</span>:<span style=color:#bbb> </span>/path/to/key.pem <span style=color:#bbb> </span><span style=color:#080;font-style:italic># key matching the cert</span><span style=color:#bbb> </span></span></span></code></pre></div><p>For additional HTTP configuration, refer to the <a href=/docs/tasks/access-application-cluster/configure-access-multiple-clusters/>kubeconfig</a> documentation.</p><h4 id=request-payloads>Request payloads</h4><p>When faced with an admission decision, the API Server POSTs a JSON serialized <code>imagepolicy.k8s.io/v1alpha1</code> <code>ImageReview</code> object describing the action. This object contains fields describing the containers being admitted, as well as any pod annotations that match <code>*.image-policy.k8s.io/*</code>.</p><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>The webhook API objects are subject to the same versioning compatibility rules as other Kubernetes API objects. Implementers should be aware of looser compatibility promises for alpha objects and check the <code>apiVersion</code> field of the request to ensure correct deserialization. Additionally, the API Server must enable the <code>imagepolicy.k8s.io/v1alpha1</code> API extensions group (<code>--runtime-config=imagepolicy.k8s.io/v1alpha1=true</code>).</div><p>An example request body:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"imagepolicy.k8s.io/v1alpha1"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"ImageReview"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"spec"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"containers"</span>: [ </span></span><span style=display:flex><span> { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"image"</span>: <span style=color:#b44>"myrepo/myimage:v1"</span> </span></span><span style=display:flex><span> }, </span></span><span style=display:flex><span> { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"image"</span>: <span style=color:#b44>"myrepo/myimage@sha256:beb6bd6a68f114c1dc2ea4b28db81bdf91de202a9014972bec5e4d9171d90ed"</span> </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span> ], </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"annotations"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"mycluster.image-policy.k8s.io/ticket-1234"</span>: <span style=color:#b44>"break-glass"</span> </span></span><span style=display:flex><span> }, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"namespace"</span>: <span style=color:#b44>"mynamespace"</span> </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div><p>The remote service is expected to fill the <code>status</code> field of the request and respond to either allow or disallow access. The response body's <code>spec</code> field is ignored, and may be omitted. A permissive response would return:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"imagepolicy.k8s.io/v1alpha1"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"ImageReview"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"status"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"allowed"</span>: <span style=color:#a2f;font-weight:700>true</span> </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div><p>To disallow access, the service would return:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"imagepolicy.k8s.io/v1alpha1"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"ImageReview"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"status"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"allowed"</span>: <span style=color:#a2f;font-weight:700>false</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"reason"</span>: <span style=color:#b44>"image currently blacklisted"</span> </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div><p>For further documentation refer to the <a href=/docs/reference/config-api/imagepolicy.v1alpha1/><code>imagepolicy.v1alpha1</code> API</a>.</p><h4 id=extending-with-annotations>Extending with Annotations</h4><p>All annotations on a Pod that match <code>*.image-policy.k8s.io/*</code> are sent to the webhook. Sending annotations allows users who are aware of the image policy backend to send extra information to it, and for different backends implementations to accept different information.</p><p>Examples of information you might put here are:</p><ul><li>request to "break glass" to override a policy, in case of emergency.</li><li>a ticket number from a ticket system that documents the break-glass request</li><li>provide a hint to the policy server as to the imageID of the image being provided, to save it a lookup</li></ul><p>In any case, the annotations are provided by the user and are not validated by Kubernetes in any way.</p><h3 id=limitpodhardantiaffinitytopology>LimitPodHardAntiAffinityTopology</h3><p><strong>Type</strong>: Validating.</p><p>This admission controller denies any pod that defines <code>AntiAffinity</code> topology key other than <code>kubernetes.io/hostname</code> in <code>requiredDuringSchedulingRequiredDuringExecution</code>.</p><p>This admission controller is disabled by default.</p><h3 id=limitranger>LimitRanger</h3><p><strong>Type</strong>: Mutating and Validating.</p><p>This admission controller will observe the incoming request and ensure that it does not violate any of the constraints enumerated in the <code>LimitRange</code> object in a <code>Namespace</code>. If you are using <code>LimitRange</code> objects in your Kubernetes deployment, you MUST use this admission controller to enforce those constraints. LimitRanger can also be used to apply default resource requests to Pods that don't specify any; currently, the default LimitRanger applies a 0.1 CPU requirement to all Pods in the <code>default</code> namespace.</p><p>See the <a href=/docs/reference/kubernetes-api/policy-resources/limit-range-v1/>LimitRange API reference</a> and the <a href=/docs/tasks/administer-cluster/manage-resources/memory-default-namespace/>example of LimitRange</a> for more details.</p><h3 id=mutatingadmissionwebhook>MutatingAdmissionWebhook</h3><p><strong>Type</strong>: Mutating.</p><p>This admission controller calls any mutating webhooks which match the request. Matching webhooks are called in serial; each one may modify the object if it desires.</p><p>This admission controller (as implied by the name) only runs in the mutating phase.</p><p>If a webhook called by this has side effects (for example, decrementing quota) it <em>must</em> have a reconciliation system, as it is not guaranteed that subsequent webhooks or validating admission controllers will permit the request to finish.</p><p>If you disable the MutatingAdmissionWebhook, you must also disable the <code>MutatingWebhookConfiguration</code> object in the <code>admissionregistration.k8s.io/v1</code> group/version via the <code>--runtime-config</code> flag, both are on by default.</p><h4 id=use-caution-when-authoring-and-installing-mutating-webhooks>Use caution when authoring and installing mutating webhooks</h4><ul><li>Users may be confused when the objects they try to create are different from what they get back.</li><li>Built in control loops may break when the objects they try to create are different when read back.<ul><li>Setting originally unset fields is less likely to cause problems than overwriting fields set in the original request. Avoid doing the latter.</li></ul></li><li>Future changes to control loops for built-in resources or third-party resources may break webhooks that work well today. Even when the webhook installation API is finalized, not all possible webhook behaviors will be guaranteed to be supported indefinitely.</li></ul><h3 id=namespaceautoprovision>NamespaceAutoProvision</h3><p><strong>Type</strong>: Mutating.</p><p>This admission controller examines all incoming requests on namespaced resources and checks if the referenced namespace does exist. It creates a namespace if it cannot be found. This admission controller is useful in deployments that do not want to restrict creation of a namespace prior to its usage.</p><h3 id=namespaceexists>NamespaceExists</h3><p><strong>Type</strong>: Validating.</p><p>This admission controller checks all requests on namespaced resources other than <code>Namespace</code> itself. If the namespace referenced from a request doesn't exist, the request is rejected.</p><h3 id=namespacelifecycle>NamespaceLifecycle</h3><p><strong>Type</strong>: Validating.</p><p>This admission controller enforces that a <code>Namespace</code> that is undergoing termination cannot have new objects created in it, and ensures that requests in a non-existent <code>Namespace</code> are rejected. This admission controller also prevents deletion of three system reserved namespaces <code>default</code>, <code>kube-system</code>, <code>kube-public</code>.</p><p>A <code>Namespace</code> deletion kicks off a sequence of operations that remove all objects (pods, services, etc.) in that namespace. In order to enforce integrity of that process, we strongly recommend running this admission controller.</p><h3 id=noderestriction>NodeRestriction</h3><p><strong>Type</strong>: Validating.</p><p>This admission controller limits the <code>Node</code> and <code>Pod</code> objects a kubelet can modify. In order to be limited by this admission controller, kubelets must use credentials in the <code>system:nodes</code> group, with a username in the form <code>system:node:<nodeName></code>. Such kubelets will only be allowed to modify their own <code>Node</code> API object, and only modify <code>Pod</code> API objects that are bound to their node. kubelets are not allowed to update or remove taints from their <code>Node</code> API object.</p><p>The <code>NodeRestriction</code> admission plugin prevents kubelets from deleting their <code>Node</code> API object, and enforces kubelet modification of labels under the <code>kubernetes.io/</code> or <code>k8s.io/</code> prefixes as follows:</p><ul><li><strong>Prevents</strong> kubelets from adding/removing/updating labels with a <code>node-restriction.kubernetes.io/</code> prefix. This label prefix is reserved for administrators to label their <code>Node</code> objects for workload isolation purposes, and kubelets will not be allowed to modify labels with that prefix.</li><li><strong>Allows</strong> kubelets to add/remove/update these labels and label prefixes:<ul><li><code>kubernetes.io/hostname</code></li><li><code>kubernetes.io/arch</code></li><li><code>kubernetes.io/os</code></li><li><code>beta.kubernetes.io/instance-type</code></li><li><code>node.kubernetes.io/instance-type</code></li><li><code>failure-domain.beta.kubernetes.io/region</code> (deprecated)</li><li><code>failure-domain.beta.kubernetes.io/zone</code> (deprecated)</li><li><code>topology.kubernetes.io/region</code></li><li><code>topology.kubernetes.io/zone</code></li><li><code>kubelet.kubernetes.io/</code>-prefixed labels</li><li><code>node.kubernetes.io/</code>-prefixed labels</li></ul></li></ul><p>Use of any other labels under the <code>kubernetes.io</code> or <code>k8s.io</code> prefixes by kubelets is reserved, and may be disallowed or allowed by the <code>NodeRestriction</code> admission plugin in the future.</p><p>Future versions may add additional restrictions to ensure kubelets have the minimal set of permissions required to operate correctly.</p><h3 id=ownerreferencespermissionenforcement>OwnerReferencesPermissionEnforcement</h3><p><strong>Type</strong>: Validating.</p><p>This admission controller protects the access to the <code>metadata.ownerReferences</code> of an object so that only users with <strong>delete</strong> permission to the object can change it. This admission controller also protects the access to <code>metadata.ownerReferences[x].blockOwnerDeletion</code> of an object, so that only users with <strong>update</strong> permission to the <code>finalizers</code> subresource of the referenced <em>owner</em> can change it.</p><h3 id=persistentvolumeclaimresize>PersistentVolumeClaimResize</h3><div class="feature-state-notice feature-stable"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.24 [stable]</code></div><p><strong>Type</strong>: Validating.</p><p>This admission controller implements additional validations for checking incoming <code>PersistentVolumeClaim</code> resize requests.</p><p>Enabling the <code>PersistentVolumeClaimResize</code> admission controller is recommended. This admission controller prevents resizing of all claims by default unless a claim's <code>StorageClass</code> explicitly enables resizing by setting <code>allowVolumeExpansion</code> to <code>true</code>.</p><p>For example: all <code>PersistentVolumeClaim</code>s created from the following <code>StorageClass</code> support volume expansion:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>storage.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>StorageClass<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>gluster-vol-default<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>provisioner</span>:<span style=color:#bbb> </span>kubernetes.io/glusterfs<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>parameters</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resturl</span>:<span style=color:#bbb> </span><span style=color:#b44>"http://192.168.10.100:8080"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>restuser</span>:<span style=color:#bbb> </span><span style=color:#b44>""</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>secretNamespace</span>:<span style=color:#bbb> </span><span style=color:#b44>""</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>secretName</span>:<span style=color:#bbb> </span><span style=color:#b44>""</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>allowVolumeExpansion</span>:<span style=color:#bbb> </span><span style=color:#a2f;font-weight:700>true</span><span style=color:#bbb> </span></span></span></code></pre></div><p>For more information about persistent volume claims, see <a href=/docs/concepts/storage/persistent-volumes/#persistentvolumeclaims>PersistentVolumeClaims</a>.</p><h3 id=podnodeselector>PodNodeSelector</h3><div class="feature-state-notice feature-alpha"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.5 [alpha]</code></div><p><strong>Type</strong>: Validating.</p><p>This admission controller defaults and limits what node selectors may be used within a namespace by reading a namespace annotation and a global configuration.</p><p>This admission controller is disabled by default.</p><h4 id=configuration-file-format>Configuration file format</h4><p><code>PodNodeSelector</code> uses a configuration file to set options for the behavior of the backend. Note that the configuration file format will move to a versioned file in a future release. This file may be json or yaml and has the following format:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>podNodeSelectorPluginConfig</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>clusterDefaultNodeSelector</span>:<span style=color:#bbb> </span>name-of-node-selector<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespace1</span>:<span style=color:#bbb> </span>name-of-node-selector<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespace2</span>:<span style=color:#bbb> </span>name-of-node-selector<span style=color:#bbb> </span></span></span></code></pre></div><p>Reference the <code>PodNodeSelector</code> configuration file from the file provided to the API server's command line flag <code>--admission-control-config-file</code>:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>apiserver.config.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>AdmissionConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>plugins</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>PodNodeSelector<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>path</span>:<span style=color:#bbb> </span>podnodeselector.yaml<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#00f;font-weight:700>...</span><span style=color:#bbb> </span></span></span></code></pre></div><h4 id=configuration-annotation-format>Configuration Annotation Format</h4><p><code>PodNodeSelector</code> uses the annotation key <code>scheduler.alpha.kubernetes.io/node-selector</code> to assign node selectors to namespaces.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Namespace<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>annotations</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>scheduler.alpha.kubernetes.io/node-selector</span>:<span style=color:#bbb> </span>name-of-node-selector<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>namespace3<span style=color:#bbb> </span></span></span></code></pre></div><h4 id=internal-behavior>Internal Behavior</h4><p>This admission controller has the following behavior:</p><ol><li>If the <code>Namespace</code> has an annotation with a key <code>scheduler.alpha.kubernetes.io/node-selector</code>, use its value as the node selector.</li><li>If the namespace lacks such an annotation, use the <code>clusterDefaultNodeSelector</code> defined in the <code>PodNodeSelector</code> plugin configuration file as the node selector.</li><li>Evaluate the pod's node selector against the namespace node selector for conflicts. Conflicts result in rejection.</li><li>Evaluate the pod's node selector against the namespace-specific allowed selector defined the plugin configuration file. Conflicts result in rejection.</li></ol><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>PodNodeSelector allows forcing pods to run on specifically labeled nodes. Also see the PodTolerationRestriction admission plugin, which allows preventing pods from running on specifically tainted nodes.</div><h3 id=podsecurity>PodSecurity</h3><div class="feature-state-notice feature-stable"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.25 [stable]</code></div><p><strong>Type</strong>: Validating.</p><p>The PodSecurity admission controller checks new Pods before they are admitted, determines if it should be admitted based on the requested security context and the restrictions on permitted <a href=/docs/concepts/security/pod-security-standards/>Pod Security Standards</a> for the namespace that the Pod would be in.</p><p>See the <a href=/docs/concepts/security/pod-security-admission/>Pod Security Admission</a> documentation for more information.</p><p>PodSecurity replaced an older admission controller named PodSecurityPolicy.</p><h3 id=podtolerationrestriction>PodTolerationRestriction</h3><div class="feature-state-notice feature-alpha"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.7 [alpha]</code></div><p><strong>Type</strong>: Mutating and Validating.</p><p>The PodTolerationRestriction admission controller verifies any conflict between tolerations of a pod and the tolerations of its namespace. It rejects the pod request if there is a conflict. It then merges the tolerations annotated on the namespace into the tolerations of the pod. The resulting tolerations are checked against a list of allowed tolerations annotated to the namespace. If the check succeeds, the pod request is admitted otherwise it is rejected.</p><p>If the namespace of the pod does not have any associated default tolerations or allowed tolerations annotated, the cluster-level default tolerations or cluster-level list of allowed tolerations are used instead if they are specified.</p><p>Tolerations to a namespace are assigned via the <code>scheduler.alpha.kubernetes.io/defaultTolerations</code> annotation key. The list of allowed tolerations can be added via the <code>scheduler.alpha.kubernetes.io/tolerationsWhitelist</code> annotation key.</p><p>Example for namespace annotations:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Namespace<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>apps-that-need-nodes-exclusively<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>annotations</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>scheduler.alpha.kubernetes.io/defaultTolerations</span>:<span style=color:#bbb> </span><span style=color:#b44>'[{"operator": "Exists", "effect": "NoSchedule", "key": "dedicated-node"}]'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>scheduler.alpha.kubernetes.io/tolerationsWhitelist</span>:<span style=color:#bbb> </span><span style=color:#b44>'[{"operator": "Exists", "effect": "NoSchedule", "key": "dedicated-node"}]'</span><span style=color:#bbb> </span></span></span></code></pre></div><p>This admission controller is disabled by default.</p><h3 id=priority>Priority</h3><p><strong>Type</strong>: Mutating and Validating.</p><p>The priority admission controller uses the <code>priorityClassName</code> field and populates the integer value of the priority. If the priority class is not found, the Pod is rejected.</p><h3 id=resourcequota>ResourceQuota</h3><p><strong>Type</strong>: Validating.</p><p>This admission controller will observe the incoming request and ensure that it does not violate any of the constraints enumerated in the <code>ResourceQuota</code> object in a <code>Namespace</code>. If you are using <code>ResourceQuota</code> objects in your Kubernetes deployment, you MUST use this admission controller to enforce quota constraints.</p><p>See the <a href=/docs/reference/kubernetes-api/policy-resources/resource-quota-v1/>ResourceQuota API reference</a> and the <a href=/docs/concepts/policy/resource-quotas/>example of Resource Quota</a> for more details.</p><h3 id=runtimeclass>RuntimeClass</h3><p><strong>Type</strong>: Mutating and Validating.</p><p>If you define a RuntimeClass with <a href=/docs/concepts/scheduling-eviction/pod-overhead/>Pod overhead</a> configured, this admission controller checks incoming Pods. When enabled, this admission controller rejects any Pod create requests that have the overhead already set. For Pods that have a RuntimeClass configured and selected in their <code>.spec</code>, this admission controller sets <code>.spec.overhead</code> in the Pod based on the value defined in the corresponding RuntimeClass.</p><p>See also <a href=/docs/concepts/scheduling-eviction/pod-overhead/>Pod Overhead</a> for more information.</p><h3 id=serviceaccount>ServiceAccount</h3><p><strong>Type</strong>: Mutating and Validating.</p><p>This admission controller implements automation for <a href=/docs/tasks/configure-pod-container/configure-service-account/>serviceAccounts</a>. The Kubernetes project strongly recommends enabling this admission controller. You should enable this admission controller if you intend to make any use of Kubernetes <code>ServiceAccount</code> objects.</p><p>Regarding the annotation <code>kubernetes.io/enforce-mountable-secrets</code>: While the annotation's name suggests it only concerns the mounting of Secrets, its enforcement also extends to other ways Secrets are used in the context of a Pod. Therefore, it is crucial to ensure that all the referenced secrets are correctly specified in the ServiceAccount.</p><h3 id=storageobjectinuseprotection>StorageObjectInUseProtection</h3><p><strong>Type</strong>: Mutating.</p><p>The <code>StorageObjectInUseProtection</code> plugin adds the <code>kubernetes.io/pvc-protection</code> or <code>kubernetes.io/pv-protection</code> finalizers to newly created Persistent Volume Claims (PVCs) or Persistent Volumes (PV). In case a user deletes a PVC or PV the PVC or PV is not removed until the finalizer is removed from the PVC or PV by PVC or PV Protection Controller. Refer to the <a href=/docs/concepts/storage/persistent-volumes/#storage-object-in-use-protection>Storage Object in Use Protection</a> for more detailed information.</p><h3 id=taintnodesbycondition>TaintNodesByCondition</h3><p><strong>Type</strong>: Mutating.</p><p>This admission controller <a class=glossary-tooltip title='A core object consisting of three required properties: key, value, and effect. Taints prevent the scheduling of pods on nodes or node groups.' data-toggle=tooltip data-placement=top href=/docs/concepts/scheduling-eviction/taint-and-toleration/ target=_blank aria-label=taints>taints</a> newly created Nodes as <code>NotReady</code> and <code>NoSchedule</code>. That tainting avoids a race condition that could cause Pods to be scheduled on new Nodes before their taints were updated to accurately reflect their reported conditions.</p><h3 id=validatingadmissionpolicy>ValidatingAdmissionPolicy</h3><p><strong>Type</strong>: Validating.</p><p><a href=/docs/reference/access-authn-authz/validating-admission-policy/>This admission controller</a> implements the CEL validation for incoming matched requests. It is enabled when both feature gate <code>validatingadmissionpolicy</code> and <code>admissionregistration.k8s.io/v1alpha1</code> group/version are enabled. If any of the ValidatingAdmissionPolicy fails, the request fails.</p><h3 id=validatingadmissionwebhook>ValidatingAdmissionWebhook</h3><p><strong>Type</strong>: Validating.</p><p>This admission controller calls any validating webhooks which match the request. Matching webhooks are called in parallel; if any of them rejects the request, the request fails. This admission controller only runs in the validation phase; the webhooks it calls may not mutate the object, as opposed to the webhooks called by the <code>MutatingAdmissionWebhook</code> admission controller.</p><p>If a webhook called by this has side effects (for example, decrementing quota) it <em>must</em> have a reconciliation system, as it is not guaranteed that subsequent webhooks or other validating admission controllers will permit the request to finish.</p><p>If you disable the ValidatingAdmissionWebhook, you must also disable the <code>ValidatingWebhookConfiguration</code> object in the <code>admissionregistration.k8s.io/v1</code> group/version via the <code>--runtime-config</code> flag.</p><h2 id=is-there-a-recommended-set-of-admission-controllers-to-use>Is there a recommended set of admission controllers to use?</h2><p>Yes. The recommended admission controllers are enabled by default (shown <a href=/docs/reference/command-line-tools-reference/kube-apiserver/#options>here</a>), so you do not need to explicitly specify them. You can enable additional admission controllers beyond the default set using the <code>--enable-admission-plugins</code> flag (<strong>order doesn't matter</strong>).</p></div><div class=td-content style=page-break-before:always><h1 id=pg-d04751f776f1faa6a82bbb7f0a200950>9 - Dynamic Admission Control</h1><p>In addition to <a href=/docs/reference/access-authn-authz/admission-controllers/>compiled-in admission plugins</a>, admission plugins can be developed as extensions and run as webhooks configured at runtime. This page describes how to build, configure, use, and monitor admission webhooks.</p><h2 id=what-are-admission-webhooks>What are admission webhooks?</h2><p>Admission webhooks are HTTP callbacks that receive admission requests and do something with them. You can define two types of admission webhooks, <a href=/docs/reference/access-authn-authz/admission-controllers/#validatingadmissionwebhook>validating admission webhook</a> and <a href=/docs/reference/access-authn-authz/admission-controllers/#mutatingadmissionwebhook>mutating admission webhook</a>. Mutating admission webhooks are invoked first, and can modify objects sent to the API server to enforce custom defaults. After all object modifications are complete, and after the incoming object is validated by the API server, validating admission webhooks are invoked and can reject requests to enforce custom policies.</p><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>Admission webhooks that need to guarantee they see the final state of the object in order to enforce policy should use a validating admission webhook, since objects can be modified after being seen by mutating webhooks.</div><h2 id=experimenting-with-admission-webhooks>Experimenting with admission webhooks</h2><p>Admission webhooks are essentially part of the cluster control-plane. You should write and deploy them with great caution. Please read the <a href=/docs/reference/access-authn-authz/extensible-admission-controllers/#write-an-admission-webhook-server>user guides</a> for instructions if you intend to write/deploy production-grade admission webhooks. In the following, we describe how to quickly experiment with admission webhooks.</p><h3 id=prerequisites>Prerequisites</h3><ul><li><p>Ensure that MutatingAdmissionWebhook and ValidatingAdmissionWebhook admission controllers are enabled. <a href=/docs/reference/access-authn-authz/admission-controllers/#is-there-a-recommended-set-of-admission-controllers-to-use>Here</a> is a recommended set of admission controllers to enable in general.</p></li><li><p>Ensure that the <code>admissionregistration.k8s.io/v1</code> API is enabled.</p></li></ul><h3 id=write-an-admission-webhook-server>Write an admission webhook server</h3><p>Please refer to the implementation of the <a href=https://github.com/kubernetes/kubernetes/blob/release-1.21/test/images/agnhost/webhook/main.go>admission webhook server</a> that is validated in a Kubernetes e2e test. The webhook handles the <code>AdmissionReview</code> request sent by the API servers, and sends back its decision as an <code>AdmissionReview</code> object in the same version it received.</p><p>See the <a href=#request>webhook request</a> section for details on the data sent to webhooks.</p><p>See the <a href=#response>webhook response</a> section for the data expected from webhooks.</p><p>The example admission webhook server leaves the <code>ClientAuth</code> field <a href=https://github.com/kubernetes/kubernetes/blob/v1.22.0/test/images/agnhost/webhook/config.go#L38-L39>empty</a>, which defaults to <code>NoClientCert</code>. This means that the webhook server does not authenticate the identity of the clients, supposedly API servers. If you need mutual TLS or other ways to authenticate the clients, see how to <a href=#authenticate-apiservers>authenticate API servers</a>.</p><h3 id=deploy-the-admission-webhook-service>Deploy the admission webhook service</h3><p>The webhook server in the e2e test is deployed in the Kubernetes cluster, via the <a href=/docs/reference/generated/kubernetes-api/v1.31/#deployment-v1-apps>deployment API</a>. The test also creates a <a href=/docs/reference/generated/kubernetes-api/v1.31/#service-v1-core>service</a> as the front-end of the webhook server. See <a href=https://github.com/kubernetes/kubernetes/blob/v1.22.0/test/e2e/apimachinery/webhook.go#L748>code</a>.</p><p>You may also deploy your webhooks outside of the cluster. You will need to update your webhook configurations accordingly.</p><h3 id=configure-admission-webhooks-on-the-fly>Configure admission webhooks on the fly</h3><p>You can dynamically configure what resources are subject to what admission webhooks via <a href=/docs/reference/generated/kubernetes-api/v1.31/#validatingwebhookconfiguration-v1-admissionregistration-k8s-io>ValidatingWebhookConfiguration</a> or <a href=/docs/reference/generated/kubernetes-api/v1.31/#mutatingwebhookconfiguration-v1-admissionregistration-k8s-io>MutatingWebhookConfiguration</a>.</p><p>The following is an example <code>ValidatingWebhookConfiguration</code>, a mutating webhook configuration is similar. See the <a href=#webhook-configuration>webhook configuration</a> section for details about each config field.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ValidatingWebhookConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>"pod-policy.example.com"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>webhooks</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>"pod-policy.example.com"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>""</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersions</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"v1"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>operations</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"CREATE"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"pods"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>scope</span>:<span style=color:#bbb> </span><span style=color:#b44>"Namespaced"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>clientConfig</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>service</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespace</span>:<span style=color:#bbb> </span><span style=color:#b44>"example-namespace"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>"example-service"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>caBundle</span>:<span style=color:#bbb> </span><CA_BUNDLE><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>admissionReviewVersions</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"v1"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>sideEffects</span>:<span style=color:#bbb> </span>None<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>timeoutSeconds</span>:<span style=color:#bbb> </span><span style=color:#666>5</span><span style=color:#bbb> </span></span></span></code></pre></div><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>You must replace the <code><CA_BUNDLE></code> in the above example by a valid CA bundle which is a PEM-encoded (field value is Base64 encoded) CA bundle for validating the webhook's server certificate.</div><p>The <code>scope</code> field specifies if only cluster-scoped resources ("Cluster") or namespace-scoped resources ("Namespaced") will match this rule. "∗" means that there are no scope restrictions.</p><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>When using <code>clientConfig.service</code>, the server cert must be valid for <code><svc_name>.<svc_namespace>.svc</code>.</div><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>Default timeout for a webhook call is 10 seconds, You can set the <code>timeout</code> and it is encouraged to use a short timeout for webhooks. If the webhook call times out, the request is handled according to the webhook's failure policy.</div><p>When an API server receives a request that matches one of the <code>rules</code>, the API server sends an <code>admissionReview</code> request to webhook as specified in the <code>clientConfig</code>.</p><p>After you create the webhook configuration, the system will take a few seconds to honor the new configuration.</p><h3 id=authenticate-apiservers>Authenticate API servers</h3><p>If your admission webhooks require authentication, you can configure the API servers to use basic auth, bearer token, or a cert to authenticate itself to the webhooks. There are three steps to complete the configuration.</p><ul><li><p>When starting the API server, specify the location of the admission control configuration file via the <code>--admission-control-config-file</code> flag.</p></li><li><p>In the admission control configuration file, specify where the MutatingAdmissionWebhook controller and ValidatingAdmissionWebhook controller should read the credentials. The credentials are stored in kubeConfig files (yes, the same schema that's used by kubectl), so the field name is <code>kubeConfigFile</code>. Here is an example admission control configuration file:</p></li></ul><ul class="nav nav-tabs" id=admissionconfiguration-example1 role=tablist><li class=nav-item><a data-toggle=tab class="nav-link active" href=#admissionconfiguration-example1-0 role=tab aria-controls=admissionconfiguration-example1-0 aria-selected=true>apiserver.config.k8s.io/v1</a></li><li class=nav-item><a data-toggle=tab class=nav-link href=#admissionconfiguration-example1-1 role=tab aria-controls=admissionconfiguration-example1-1>apiserver.k8s.io/v1alpha1</a></li></ul><div class=tab-content id=admissionconfiguration-example1><div id=admissionconfiguration-example1-0 class="tab-pane show active" role=tabpanel aria-labelledby=admissionconfiguration-example1-0><p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>apiserver.config.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>AdmissionConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>plugins</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>ValidatingAdmissionWebhook<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>configuration</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>apiserver.config.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>WebhookAdmissionConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kubeConfigFile</span>:<span style=color:#bbb> </span><span style=color:#b44>"<path-to-kubeconfig-file>"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>MutatingAdmissionWebhook<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>configuration</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>apiserver.config.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>WebhookAdmissionConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kubeConfigFile</span>:<span style=color:#bbb> </span><span style=color:#b44>"<path-to-kubeconfig-file>"</span><span style=color:#bbb> </span></span></span></code></pre></div></div><div id=admissionconfiguration-example1-1 class=tab-pane role=tabpanel aria-labelledby=admissionconfiguration-example1-1><p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:#080;font-style:italic># Deprecated in v1.17 in favor of apiserver.config.k8s.io/v1</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>apiserver.k8s.io/v1alpha1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>AdmissionConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>plugins</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>ValidatingAdmissionWebhook<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>configuration</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Deprecated in v1.17 in favor of apiserver.config.k8s.io/v1, kind=WebhookAdmissionConfiguration</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>apiserver.config.k8s.io/v1alpha1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>WebhookAdmission<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kubeConfigFile</span>:<span style=color:#bbb> </span><span style=color:#b44>"<path-to-kubeconfig-file>"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>MutatingAdmissionWebhook<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>configuration</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Deprecated in v1.17 in favor of apiserver.config.k8s.io/v1, kind=WebhookAdmissionConfiguration</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>apiserver.config.k8s.io/v1alpha1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>WebhookAdmission<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kubeConfigFile</span>:<span style=color:#bbb> </span><span style=color:#b44>"<path-to-kubeconfig-file>"</span><span style=color:#bbb> </span></span></span></code></pre></div></div></div><p>For more information about <code>AdmissionConfiguration</code>, see the <a href=/docs/reference/config-api/apiserver-webhookadmission.v1/>AdmissionConfiguration (v1) reference</a>. See the <a href=#webhook-configuration>webhook configuration</a> section for details about each config field.</p><p>In the kubeConfig file, provide the credentials:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Config<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>users</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># name should be set to the DNS name of the service or the host (including port) of the URL the webhook is configured to speak to.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># If a non-443 port is used for services, it must be included in the name when configuring 1.16+ API servers.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># For a webhook configured to speak to a service on the default port (443), specify the DNS name of the service:</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># - name: webhook1.ns1.svc</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># user: ...</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># For a webhook configured to speak to a service on non-default port (e.g. 8443), specify the DNS name and port of the service in 1.16+:</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># - name: webhook1.ns1.svc:8443</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># user: ...</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># and optionally create a second stanza using only the DNS name of the service for compatibility with 1.15 API servers:</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># - name: webhook1.ns1.svc</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># user: ...</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># For webhooks configured to speak to a URL, match the host (and port) specified in the webhook's URL. Examples:</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># A webhook with `url: https://www.example.com`:</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># - name: www.example.com</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># user: ...</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># A webhook with `url: https://www.example.com:443`:</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># - name: www.example.com:443</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># user: ...</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># A webhook with `url: https://www.example.com:8443`:</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># - name: www.example.com:8443</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># user: ...</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic>#</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>'webhook1.ns1.svc'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>user</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>client-certificate-data</span>:<span style=color:#bbb> </span><span style=color:#b44>"<pem encoded certificate>"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>client-key-data</span>:<span style=color:#bbb> </span><span style=color:#b44>"<pem encoded key>"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># The `name` supports using * to wildcard-match prefixing segments.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>'*.webhook-company.org'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>user</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>password</span>:<span style=color:#bbb> </span><span style=color:#b44>"<password>"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>username</span>:<span style=color:#bbb> </span><span style=color:#b44>"<name>"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># '*' is the default match.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>'*'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>user</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>token</span>:<span style=color:#bbb> </span><span style=color:#b44>"<token>"</span><span style=color:#bbb> </span></span></span></code></pre></div><p>Of course you need to set up the webhook server to handle these authentication requests.</p><h2 id=webhook-request-and-response>Webhook request and response</h2><h3 id=request>Request</h3><p>Webhooks are sent as POST requests, with <code>Content-Type: application/json</code>, with an <code>AdmissionReview</code> API object in the <code>admission.k8s.io</code> API group serialized to JSON as the body.</p><p>Webhooks can specify what versions of <code>AdmissionReview</code> objects they accept with the <code>admissionReviewVersions</code> field in their configuration:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ValidatingWebhookConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>webhooks</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-webhook.example.com<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>admissionReviewVersions</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"v1"</span>,<span style=color:#bbb> </span><span style=color:#b44>"v1beta1"</span>]<span style=color:#bbb> </span></span></span></code></pre></div><p><code>admissionReviewVersions</code> is a required field when creating webhook configurations. Webhooks are required to support at least one <code>AdmissionReview</code> version understood by the current and previous API server.</p><p>API servers send the first <code>AdmissionReview</code> version in the <code>admissionReviewVersions</code> list they support. If none of the versions in the list are supported by the API server, the configuration will not be allowed to be created. If an API server encounters a webhook configuration that was previously created and does not support any of the <code>AdmissionReview</code> versions the API server knows how to send, attempts to call to the webhook will fail and be subject to the <a href=#failure-policy>failure policy</a>.</p><p>This example shows the data contained in an <code>AdmissionReview</code> object for a request to update the <code>scale</code> subresource of an <code>apps/v1</code> <code>Deployment</code>:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admission.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>AdmissionReview<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>request</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Random uid uniquely identifying this admission call</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>uid</span>:<span style=color:#bbb> </span>705ab4f5-6393-11e8-b7cc-42010a800002<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Fully-qualified group/version/kind of the incoming object</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>group</span>:<span style=color:#bbb> </span>autoscaling<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>version</span>:<span style=color:#bbb> </span>v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Scale<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Fully-qualified group/version/kind of the resource being modified</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resource</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>group</span>:<span style=color:#bbb> </span>apps<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>version</span>:<span style=color:#bbb> </span>v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resource</span>:<span style=color:#bbb> </span>deployments<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># subresource, if the request is to a subresource</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>subResource</span>:<span style=color:#bbb> </span>scale<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Fully-qualified group/version/kind of the incoming object in the original request to the API server.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># This only differs from `kind` if the webhook specified `matchPolicy: Equivalent` and the</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># original request to the API server was converted to a version the webhook registered for.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>requestKind</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>group</span>:<span style=color:#bbb> </span>autoscaling<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>version</span>:<span style=color:#bbb> </span>v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Scale<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Fully-qualified group/version/kind of the resource being modified in the original request to the API server.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># This only differs from `resource` if the webhook specified `matchPolicy: Equivalent` and the</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># original request to the API server was converted to a version the webhook registered for.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>requestResource</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>group</span>:<span style=color:#bbb> </span>apps<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>version</span>:<span style=color:#bbb> </span>v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resource</span>:<span style=color:#bbb> </span>deployments<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># subresource, if the request is to a subresource</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># This only differs from `subResource` if the webhook specified `matchPolicy: Equivalent` and the</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># original request to the API server was converted to a version the webhook registered for.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>requestSubResource</span>:<span style=color:#bbb> </span>scale<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Name of the resource being modified</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-deployment<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Namespace of the resource being modified, if the resource is namespaced (or is a Namespace object)</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespace</span>:<span style=color:#bbb> </span>my-namespace<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># operation can be CREATE, UPDATE, DELETE, or CONNECT</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>operation</span>:<span style=color:#bbb> </span>UPDATE<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>userInfo</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Username of the authenticated user making the request to the API server</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>username</span>:<span style=color:#bbb> </span>admin<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># UID of the authenticated user making the request to the API server</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>uid</span>:<span style=color:#bbb> </span>014fbff9a07c<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Group memberships of the authenticated user making the request to the API server</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>groups</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- system:authenticated<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- my-admin-group<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Arbitrary extra info associated with the user making the request to the API server.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># This is populated by the API server authentication layer and should be included</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># if any SubjectAccessReview checks are performed by the webhook.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>extra</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>some-key</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- some-value1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- some-value2<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># object is the new object being admitted.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># It is null for DELETE operations.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>object</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>autoscaling/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Scale<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># oldObject is the existing object.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># It is null for CREATE and CONNECT operations.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>oldObject</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>autoscaling/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Scale<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># options contains the options for the operation being admitted, like meta.k8s.io/v1 CreateOptions, UpdateOptions, or DeleteOptions.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># It is null for CONNECT operations.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>options</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>meta.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>UpdateOptions<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># dryRun indicates the API request is running in dry run mode and will not be persisted.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Webhooks with side effects should avoid actuating those side effects when dryRun is true.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># See http://k8s.io/docs/reference/using-api/api-concepts/#make-a-dry-run-request for more details.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>dryRun</span>:<span style=color:#bbb> </span><span style=color:#a2f;font-weight:700>False</span><span style=color:#bbb> </span></span></span></code></pre></div><h3 id=response>Response</h3><p>Webhooks respond with a 200 HTTP status code, <code>Content-Type: application/json</code>, and a body containing an <code>AdmissionReview</code> object (in the same version they were sent), with the <code>response</code> stanza populated, serialized to JSON.</p><p>At a minimum, the <code>response</code> stanza must contain the following fields:</p><ul><li><code>uid</code>, copied from the <code>request.uid</code> sent to the webhook</li><li><code>allowed</code>, either set to <code>true</code> or <code>false</code></li></ul><p>Example of a minimal response from a webhook to allow a request:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"admission.k8s.io/v1"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"AdmissionReview"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"response"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"uid"</span>: <span style=color:#b44>"<value from request.uid>"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"allowed"</span>: <span style=color:#a2f;font-weight:700>true</span> </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div><p>Example of a minimal response from a webhook to forbid a request:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"admission.k8s.io/v1"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"AdmissionReview"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"response"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"uid"</span>: <span style=color:#b44>"<value from request.uid>"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"allowed"</span>: <span style=color:#a2f;font-weight:700>false</span> </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div><p>When rejecting a request, the webhook can customize the http code and message returned to the user using the <code>status</code> field. The specified status object is returned to the user. See the <a href=/docs/reference/generated/kubernetes-api/v1.31/#status-v1-meta>API documentation</a> for details about the <code>status</code> type. Example of a response to forbid a request, customizing the HTTP status code and message presented to the user:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"admission.k8s.io/v1"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"AdmissionReview"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"response"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"uid"</span>: <span style=color:#b44>"<value from request.uid>"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"allowed"</span>: <span style=color:#a2f;font-weight:700>false</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"status"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"code"</span>: <span style=color:#666>403</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"message"</span>: <span style=color:#b44>"You cannot do this because it is Tuesday and your name starts with A"</span> </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div><p>When allowing a request, a mutating admission webhook may optionally modify the incoming object as well. This is done using the <code>patch</code> and <code>patchType</code> fields in the response. The only currently supported <code>patchType</code> is <code>JSONPatch</code>. See <a href=https://jsonpatch.com/>JSON patch</a> documentation for more details. For <code>patchType: JSONPatch</code>, the <code>patch</code> field contains a base64-encoded array of JSON patch operations.</p><p>As an example, a single patch operation that would set <code>spec.replicas</code> would be <code>[{"op": "add", "path": "/spec/replicas", "value": 3}]</code></p><p>Base64-encoded, this would be <code>W3sib3AiOiAiYWRkIiwgInBhdGgiOiAiL3NwZWMvcmVwbGljYXMiLCAidmFsdWUiOiAzfV0=</code></p><p>So a webhook response to add that label would be:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"admission.k8s.io/v1"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"AdmissionReview"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"response"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"uid"</span>: <span style=color:#b44>"<value from request.uid>"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"allowed"</span>: <span style=color:#a2f;font-weight:700>true</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"patchType"</span>: <span style=color:#b44>"JSONPatch"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"patch"</span>: <span style=color:#b44>"W3sib3AiOiAiYWRkIiwgInBhdGgiOiAiL3NwZWMvcmVwbGljYXMiLCAidmFsdWUiOiAzfV0="</span> </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div><p>Admission webhooks can optionally return warning messages that are returned to the requesting client in HTTP <code>Warning</code> headers with a warning code of 299. Warnings can be sent with allowed or rejected admission responses.</p><p>If you're implementing a webhook that returns a warning:</p><ul><li>Don't include a "Warning:" prefix in the message</li><li>Use warning messages to describe problems the client making the API request should correct or be aware of</li><li>Limit warnings to 120 characters if possible</li></ul><div class="alert alert-caution" role=alert><h4 class=alert-heading>Caution:</h4>Individual warning messages over 256 characters may be truncated by the API server before being returned to clients. If more than 4096 characters of warning messages are added (from all sources), additional warning messages are ignored.</div><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"apiVersion"</span>: <span style=color:#b44>"admission.k8s.io/v1"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"kind"</span>: <span style=color:#b44>"AdmissionReview"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"response"</span>: { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"uid"</span>: <span style=color:#b44>"<value from request.uid>"</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"allowed"</span>: <span style=color:#a2f;font-weight:700>true</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>"warnings"</span>: [ </span></span><span style=display:flex><span> <span style=color:#b44>"duplicate envvar entries specified with name MY_ENV"</span>, </span></span><span style=display:flex><span> <span style=color:#b44>"memory request less than 4MB specified for container mycontainer, which will not start successfully"</span> </span></span><span style=display:flex><span> ] </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span>} </span></span></code></pre></div><h2 id=webhook-configuration>Webhook configuration</h2><p>To register admission webhooks, create <code>MutatingWebhookConfiguration</code> or <code>ValidatingWebhookConfiguration</code> API objects. The name of a <code>MutatingWebhookConfiguration</code> or a <code>ValidatingWebhookConfiguration</code> object must be a valid <a href=/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names>DNS subdomain name</a>.</p><p>Each configuration can contain one or more webhooks. If multiple webhooks are specified in a single configuration, each must be given a unique name. This is required in order to make resulting audit logs and metrics easier to match up to active configurations.</p><p>Each webhook defines the following things.</p><h3 id=matching-requests-rules>Matching requests: rules</h3><p>Each webhook must specify a list of rules used to determine if a request to the API server should be sent to the webhook. Each rule specifies one or more operations, apiGroups, apiVersions, and resources, and a resource scope:</p><ul><li><p><code>operations</code> lists one or more operations to match. Can be <code>"CREATE"</code>, <code>"UPDATE"</code>, <code>"DELETE"</code>, <code>"CONNECT"</code>, or <code>"*"</code> to match all.</p></li><li><p><code>apiGroups</code> lists one or more API groups to match. <code>""</code> is the core API group. <code>"*"</code> matches all API groups.</p></li><li><p><code>apiVersions</code> lists one or more API versions to match. <code>"*"</code> matches all API versions.</p></li><li><p><code>resources</code> lists one or more resources to match.</p><ul><li><code>"*"</code> matches all resources, but not subresources.</li><li><code>"*/*"</code> matches all resources and subresources.</li><li><code>"pods/*"</code> matches all subresources of pods.</li><li><code>"*/status"</code> matches all status subresources.</li></ul></li><li><p><code>scope</code> specifies a scope to match. Valid values are <code>"Cluster"</code>, <code>"Namespaced"</code>, and <code>"*"</code>. Subresources match the scope of their parent resource. Default is <code>"*"</code>.</p><ul><li><code>"Cluster"</code> means that only cluster-scoped resources will match this rule (Namespace API objects are cluster-scoped).</li><li><code>"Namespaced"</code> means that only namespaced resources will match this rule.</li><li><code>"*"</code> means that there are no scope restrictions.</li></ul></li></ul><p>If an incoming request matches one of the specified <code>operations</code>, <code>groups</code>, <code>versions</code>, <code>resources</code>, and <code>scope</code> for any of a webhook's <code>rules</code>, the request is sent to the webhook.</p><p>Here are other examples of rules that could be used to specify which resources should be intercepted.</p><p>Match <code>CREATE</code> or <code>UPDATE</code> requests to <code>apps/v1</code> and <code>apps/v1beta1</code> <code>deployments</code> and <code>replicasets</code>:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ValidatingWebhookConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#00f;font-weight:700>...</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>webhooks</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-webhook.example.com<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>operations</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"CREATE"</span>,<span style=color:#bbb> </span><span style=color:#b44>"UPDATE"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"apps"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersions</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"v1"</span>,<span style=color:#bbb> </span><span style=color:#b44>"v1beta1"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"deployments"</span>,<span style=color:#bbb> </span><span style=color:#b44>"replicasets"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>scope</span>:<span style=color:#bbb> </span><span style=color:#b44>"Namespaced"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>...<span style=color:#bbb> </span></span></span></code></pre></div><p>Match create requests for all resources (but not subresources) in all API groups and versions:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ValidatingWebhookConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>webhooks</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-webhook.example.com<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>operations</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"CREATE"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"*"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersions</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"*"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"*"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>scope</span>:<span style=color:#bbb> </span><span style=color:#b44>"*"</span><span style=color:#bbb> </span></span></span></code></pre></div><p>Match update requests for all <code>status</code> subresources in all API groups and versions:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ValidatingWebhookConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>webhooks</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-webhook.example.com<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>operations</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"UPDATE"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"*"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersions</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"*"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"*/status"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>scope</span>:<span style=color:#bbb> </span><span style=color:#b44>"*"</span><span style=color:#bbb> </span></span></span></code></pre></div><h3 id=matching-requests-objectselector>Matching requests: objectSelector</h3><p>Webhooks may optionally limit which requests are intercepted based on the labels of the objects they would be sent, by specifying an <code>objectSelector</code>. If specified, the objectSelector is evaluated against both the object and oldObject that would be sent to the webhook, and is considered to match if either object matches the selector.</p><p>A null object (<code>oldObject</code> in the case of create, or <code>newObject</code> in the case of delete), or an object that cannot have labels (like a <code>DeploymentRollback</code> or a <code>PodProxyOptions</code> object) is not considered to match.</p><p>Use the object selector only if the webhook is opt-in, because end users may skip the admission webhook by setting the labels.</p><p>This example shows a mutating webhook that would match a <code>CREATE</code> of any resource (but not subresources) with the label <code>foo: bar</code>:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>MutatingWebhookConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>webhooks</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-webhook.example.com<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>objectSelector</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>matchLabels</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>foo</span>:<span style=color:#bbb> </span>bar<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>operations</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"CREATE"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"*"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersions</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"*"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"*"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>scope</span>:<span style=color:#bbb> </span><span style=color:#b44>"*"</span><span style=color:#bbb> </span></span></span></code></pre></div><p>See <a href=/docs/concepts/overview/working-with-objects/labels/>labels concept</a> for more examples of label selectors.</p><h3 id=matching-requests-namespaceselector>Matching requests: namespaceSelector</h3><p>Webhooks may optionally limit which requests for namespaced resources are intercepted, based on the labels of the containing namespace, by specifying a <code>namespaceSelector</code>.</p><p>The <code>namespaceSelector</code> decides whether to run the webhook on a request for a namespaced resource (or a Namespace object), based on whether the namespace's labels match the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is a cluster scoped resource other than a Namespace, <code>namespaceSelector</code> has no effect.</p><p>This example shows a mutating webhook that matches a <code>CREATE</code> of any namespaced resource inside a namespace that does not have a "runlevel" label of "0" or "1":</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>MutatingWebhookConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>webhooks</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-webhook.example.com<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespaceSelector</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>matchExpressions</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>key</span>:<span style=color:#bbb> </span>runlevel<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>operator</span>:<span style=color:#bbb> </span>NotIn<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>values</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"0"</span>,<span style=color:#b44>"1"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>operations</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"CREATE"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"*"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersions</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"*"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"*"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>scope</span>:<span style=color:#bbb> </span><span style=color:#b44>"Namespaced"</span><span style=color:#bbb> </span></span></span></code></pre></div><p>This example shows a validating webhook that matches a <code>CREATE</code> of any namespaced resource inside a namespace that is associated with the "environment" of "prod" or "staging":</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ValidatingWebhookConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>webhooks</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-webhook.example.com<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespaceSelector</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>matchExpressions</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>key</span>:<span style=color:#bbb> </span>environment<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>operator</span>:<span style=color:#bbb> </span>In<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>values</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"prod"</span>,<span style=color:#b44>"staging"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>operations</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"CREATE"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"*"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersions</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"*"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"*"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>scope</span>:<span style=color:#bbb> </span><span style=color:#b44>"Namespaced"</span><span style=color:#bbb> </span></span></span></code></pre></div><p>See <a href=/docs/concepts/overview/working-with-objects/labels/>labels concept</a> for more examples of label selectors.</p><h3 id=matching-requests-matchpolicy>Matching requests: matchPolicy</h3><p>API servers can make objects available via multiple API groups or versions.</p><p>For example, if a webhook only specified a rule for some API groups/versions (like <code>apiGroups:["apps"], apiVersions:["v1","v1beta1"]</code>), and a request was made to modify the resource via another API group/version (like <code>extensions/v1beta1</code>), the request would not be sent to the webhook.</p><p>The <code>matchPolicy</code> lets a webhook define how its <code>rules</code> are used to match incoming requests. Allowed values are <code>Exact</code> or <code>Equivalent</code>.</p><ul><li><code>Exact</code> means a request should be intercepted only if it exactly matches a specified rule.</li><li><code>Equivalent</code> means a request should be intercepted if it modifies a resource listed in <code>rules</code>, even via another API group or version.</li></ul><p>In the example given above, the webhook that only registered for <code>apps/v1</code> could use <code>matchPolicy</code>:</p><ul><li><code>matchPolicy: Exact</code> would mean the <code>extensions/v1beta1</code> request would not be sent to the webhook</li><li><code>matchPolicy: Equivalent</code> means the <code>extensions/v1beta1</code> request would be sent to the webhook (with the objects converted to a version the webhook had specified: <code>apps/v1</code>)</li></ul><p>Specifying <code>Equivalent</code> is recommended, and ensures that webhooks continue to intercept the resources they expect when upgrades enable new versions of the resource in the API server.</p><p>When a resource stops being served by the API server, it is no longer considered equivalent to other versions of that resource that are still served. For example, <code>extensions/v1beta1</code> deployments were first deprecated and then removed (in Kubernetes v1.16).</p><p>Since that removal, a webhook with a <code>apiGroups:["extensions"], apiVersions:["v1beta1"], resources:["deployments"]</code> rule does not intercept deployments created via <code>apps/v1</code> APIs. For that reason, webhooks should prefer registering for stable versions of resources.</p><p>This example shows a validating webhook that intercepts modifications to deployments (no matter the API group or version), and is always sent an <code>apps/v1</code> <code>Deployment</code> object:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ValidatingWebhookConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>webhooks</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-webhook.example.com<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>matchPolicy</span>:<span style=color:#bbb> </span>Equivalent<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>operations</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"CREATE"</span>,<span style=color:#b44>"UPDATE"</span>,<span style=color:#b44>"DELETE"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"apps"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersions</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"v1"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"deployments"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>scope</span>:<span style=color:#bbb> </span><span style=color:#b44>"Namespaced"</span><span style=color:#bbb> </span></span></span></code></pre></div><p>The <code>matchPolicy</code> for an admission webhooks defaults to <code>Equivalent</code>.</p><h3 id=matching-requests-matchconditions>Matching requests: <code>matchConditions</code></h3><div class="feature-state-notice feature-stable" title="Feature Gate: AdmissionWebhookMatchConditions"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.30 [stable]</code> (enabled by default: true)</div><p>You can define <em>match conditions</em> for webhooks if you need fine-grained request filtering. These conditions are useful if you find that match rules, <code>objectSelectors</code> and <code>namespaceSelectors</code> still doesn't provide the filtering you want over when to call out over HTTP. Match conditions are <a href=/docs/reference/using-api/cel/>CEL expressions</a>. All match conditions must evaluate to true for the webhook to be called.</p><p>Here is an example illustrating a few different uses for match conditions:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ValidatingWebhookConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>webhooks</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-webhook.example.com<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>matchPolicy</span>:<span style=color:#bbb> </span>Equivalent<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>operations</span>:<span style=color:#bbb> </span>[<span style=color:#b44>'CREATE'</span>,<span style=color:#b44>'UPDATE'</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>'*'</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersions</span>:<span style=color:#bbb> </span>[<span style=color:#b44>'*'</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>'*'</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>failurePolicy</span>:<span style=color:#bbb> </span><span style=color:#b44>'Ignore'</span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Fail-open (optional)</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>sideEffects</span>:<span style=color:#bbb> </span>None<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>clientConfig</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>service</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespace</span>:<span style=color:#bbb> </span>my-namespace<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-webhook<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>caBundle</span>:<span style=color:#bbb> </span><span style=color:#b44>'<omitted>'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># You can have up to 64 matchConditions per webhook</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>matchConditions</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>'exclude-leases'</span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Each match condition must have a unique name</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>'!(request.resource.group == "coordination.k8s.io" && request.resource.resource == "leases")'</span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Match non-lease resources.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>'exclude-kubelet-requests'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>'!("system:nodes" in request.userInfo.groups)'</span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Match requests made by non-node users.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>'rbac'</span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Skip RBAC requests, which are handled by the second webhook.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>'request.resource.group != "rbac.authorization.k8s.io"'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># This example illustrates the use of the 'authorizer'. The authorization check is more expensive</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># than a simple expression, so in this example it is scoped to only RBAC requests by using a second</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># webhook. Both webhooks can be served by the same endpoint.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>rbac.my-webhook.example.com<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>matchPolicy</span>:<span style=color:#bbb> </span>Equivalent<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>operations</span>:<span style=color:#bbb> </span>[<span style=color:#b44>'CREATE'</span>,<span style=color:#b44>'UPDATE'</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>'rbac.authorization.k8s.io'</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersions</span>:<span style=color:#bbb> </span>[<span style=color:#b44>'*'</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>'*'</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>failurePolicy</span>:<span style=color:#bbb> </span><span style=color:#b44>'Fail'</span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Fail-closed (the default)</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>sideEffects</span>:<span style=color:#bbb> </span>None<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>clientConfig</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>service</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespace</span>:<span style=color:#bbb> </span>my-namespace<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-webhook<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>caBundle</span>:<span style=color:#bbb> </span><span style=color:#b44>'<omitted>'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># You can have up to 64 matchConditions per webhook</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>matchConditions</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>'breakglass'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Skip requests made by users authorized to 'breakglass' on this webhook.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># The 'breakglass' API verb does not need to exist outside this check.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>'!authorizer.group("admissionregistration.k8s.io").resource("validatingwebhookconfigurations").name("my-webhook.example.com").check("breakglass").allowed()'</span><span style=color:#bbb> </span></span></span></code></pre></div><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>You can define up to 64 elements in the <code>matchConditions</code> field per webhook.</div><p>Match conditions have access to the following CEL variables:</p><ul><li><code>object</code> - The object from the incoming request. The value is null for DELETE requests. The object version may be converted based on the <a href=#matching-requests-matchpolicy>matchPolicy</a>.</li><li><code>oldObject</code> - The existing object. The value is null for CREATE requests.</li><li><code>request</code> - The request portion of the <a href=#request>AdmissionReview</a>, excluding <code>object</code> and <code>oldObject</code>.</li><li><code>authorizer</code> - A CEL Authorizer. May be used to perform authorization checks for the principal (authenticated user) of the request. See <a href=https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz>Authz</a> in the Kubernetes CEL library documentation for more details.</li><li><code>authorizer.requestResource</code> - A shortcut for an authorization check configured with the request resource (group, resource, (subresource), namespace, name).</li></ul><p>For more information on CEL expressions, refer to the <a href=/docs/reference/using-api/cel/>Common Expression Language in Kubernetes reference</a>.</p><p>In the event of an error evaluating a match condition the webhook is never called. Whether to reject the request is determined as follows:</p><ol><li>If <strong>any</strong> match condition evaluated to <code>false</code> (regardless of other errors), the API server skips the webhook.</li><li>Otherwise:<ul><li>for <a href=#failure-policy><code>failurePolicy: Fail</code></a>, reject the request (without calling the webhook).</li><li>for <a href=#failure-policy><code>failurePolicy: Ignore</code></a>, proceed with the request but skip the webhook.</li></ul></li></ol><h3 id=contacting-the-webhook>Contacting the webhook</h3><p>Once the API server has determined a request should be sent to a webhook, it needs to know how to contact the webhook. This is specified in the <code>clientConfig</code> stanza of the webhook configuration.</p><p>Webhooks can either be called via a URL or a service reference, and can optionally include a custom CA bundle to use to verify the TLS connection.</p><h4 id=url>URL</h4><p><code>url</code> gives the location of the webhook, in standard URL form (<code>scheme://host:port/path</code>).</p><p>The <code>host</code> should not refer to a service running in the cluster; use a service reference by specifying the <code>service</code> field instead. The host might be resolved via external DNS in some API servers (e.g., <code>kube-apiserver</code> cannot resolve in-cluster DNS as that would be a layering violation). <code>host</code> may also be an IP address.</p><p>Please note that using <code>localhost</code> or <code>127.0.0.1</code> as a <code>host</code> is risky unless you take great care to run this webhook on all hosts which run an API server which might need to make calls to this webhook. Such installations are likely to be non-portable or not readily run in a new cluster.</p><p>The scheme must be "https"; the URL must begin with "https://".</p><p>Attempting to use a user or basic auth (for example <code>user:password@</code>) is not allowed. Fragments (<code>#...</code>) and query parameters (<code>?...</code>) are also not allowed.</p><p>Here is an example of a mutating webhook configured to call a URL (and expects the TLS certificate to be verified using system trust roots, so does not specify a caBundle):</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>MutatingWebhookConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>webhooks</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-webhook.example.com<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>clientConfig</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>url</span>:<span style=color:#bbb> </span><span style=color:#b44>"https://my-webhook.example.com:9443/my-webhook-path"</span><span style=color:#bbb> </span></span></span></code></pre></div><h4 id=service-reference>Service reference</h4><p>The <code>service</code> stanza inside <code>clientConfig</code> is a reference to the service for this webhook. If the webhook is running within the cluster, then you should use <code>service</code> instead of <code>url</code>. The service namespace and name are required. The port is optional and defaults to 443. The path is optional and defaults to "/".</p><p>Here is an example of a mutating webhook configured to call a service on port "1234" at the subpath "/my-path", and to verify the TLS connection against the ServerName <code>my-service-name.my-service-namespace.svc</code> using a custom CA bundle:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>MutatingWebhookConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>webhooks</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-webhook.example.com<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>clientConfig</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>caBundle</span>:<span style=color:#bbb> </span><CA_BUNDLE><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>service</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespace</span>:<span style=color:#bbb> </span>my-service-namespace<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-service-name<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>path</span>:<span style=color:#bbb> </span>/my-path<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>port</span>:<span style=color:#bbb> </span><span style=color:#666>1234</span><span style=color:#bbb> </span></span></span></code></pre></div><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>You must replace the <code><CA_BUNDLE></code> in the above example by a valid CA bundle which is a PEM-encoded CA bundle for validating the webhook's server certificate.</div><h3 id=side-effects>Side effects</h3><p>Webhooks typically operate only on the content of the <code>AdmissionReview</code> sent to them. Some webhooks, however, make out-of-band changes as part of processing admission requests.</p><p>Webhooks that make out-of-band changes ("side effects") must also have a reconciliation mechanism (like a controller) that periodically determines the actual state of the world, and adjusts the out-of-band data modified by the admission webhook to reflect reality. This is because a call to an admission webhook does not guarantee the admitted object will be persisted as is, or at all. Later webhooks can modify the content of the object, a conflict could be encountered while writing to storage, or the server could power off before persisting the object.</p><p>Additionally, webhooks with side effects must skip those side-effects when <code>dryRun: true</code> admission requests are handled. A webhook must explicitly indicate that it will not have side-effects when run with <code>dryRun</code>, or the dry-run request will not be sent to the webhook and the API request will fail instead.</p><p>Webhooks indicate whether they have side effects using the <code>sideEffects</code> field in the webhook configuration:</p><ul><li><code>None</code>: calling the webhook will have no side effects.</li><li><code>NoneOnDryRun</code>: calling the webhook will possibly have side effects, but if a request with <code>dryRun: true</code> is sent to the webhook, the webhook will suppress the side effects (the webhook is <code>dryRun</code>-aware).</li></ul><p>Here is an example of a validating webhook indicating it has no side effects on <code>dryRun: true</code> requests:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ValidatingWebhookConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>webhooks</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-webhook.example.com<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>sideEffects</span>:<span style=color:#bbb> </span>NoneOnDryRun<span style=color:#bbb> </span></span></span></code></pre></div><h3 id=timeouts>Timeouts</h3><p>Because webhooks add to API request latency, they should evaluate as quickly as possible. <code>timeoutSeconds</code> allows configuring how long the API server should wait for a webhook to respond before treating the call as a failure.</p><p>If the timeout expires before the webhook responds, the webhook call will be ignored or the API call will be rejected based on the <a href=#failure-policy>failure policy</a>.</p><p>The timeout value must be between 1 and 30 seconds.</p><p>Here is an example of a validating webhook with a custom timeout of 2 seconds:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ValidatingWebhookConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>webhooks</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-webhook.example.com<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>timeoutSeconds</span>:<span style=color:#bbb> </span><span style=color:#666>2</span><span style=color:#bbb> </span></span></span></code></pre></div><p>The timeout for an admission webhook defaults to 10 seconds.</p><h3 id=reinvocation-policy>Reinvocation policy</h3><p>A single ordering of mutating admissions plugins (including webhooks) does not work for all cases (see <a href=https://issue.k8s.io/64333>https://issue.k8s.io/64333</a> as an example). A mutating webhook can add a new sub-structure to the object (like adding a <code>container</code> to a <code>pod</code>), and other mutating plugins which have already run may have opinions on those new structures (like setting an <code>imagePullPolicy</code> on all containers).</p><p>To allow mutating admission plugins to observe changes made by other plugins, built-in mutating admission plugins are re-run if a mutating webhook modifies an object, and mutating webhooks can specify a <code>reinvocationPolicy</code> to control whether they are reinvoked as well.</p><p><code>reinvocationPolicy</code> may be set to <code>Never</code> or <code>IfNeeded</code>. It defaults to <code>Never</code>.</p><ul><li><code>Never</code>: the webhook must not be called more than once in a single admission evaluation.</li><li><code>IfNeeded</code>: the webhook may be called again as part of the admission evaluation if the object being admitted is modified by other admission plugins after the initial webhook call.</li></ul><p>The important elements to note are:</p><ul><li>The number of additional invocations is not guaranteed to be exactly one.</li><li>If additional invocations result in further modifications to the object, webhooks are not guaranteed to be invoked again.</li><li>Webhooks that use this option may be reordered to minimize the number of additional invocations.</li><li>To validate an object after all mutations are guaranteed complete, use a validating admission webhook instead (recommended for webhooks with side-effects).</li></ul><p>Here is an example of a mutating webhook opting into being re-invoked if later admission plugins modify the object:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>MutatingWebhookConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>webhooks</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-webhook.example.com<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>reinvocationPolicy</span>:<span style=color:#bbb> </span>IfNeeded<span style=color:#bbb> </span></span></span></code></pre></div><p>Mutating webhooks must be <a href=#idempotence>idempotent</a>, able to successfully process an object they have already admitted and potentially modified. This is true for all mutating admission webhooks, since any change they can make in an object could already exist in the user-provided object, but it is essential for webhooks that opt into reinvocation.</p><h3 id=failure-policy>Failure policy</h3><p><code>failurePolicy</code> defines how unrecognized errors and timeout errors from the admission webhook are handled. Allowed values are <code>Ignore</code> or <code>Fail</code>.</p><ul><li><code>Ignore</code> means that an error calling the webhook is ignored and the API request is allowed to continue.</li><li><code>Fail</code> means that an error calling the webhook causes the admission to fail and the API request to be rejected.</li></ul><p>Here is a mutating webhook configured to reject an API request if errors are encountered calling the admission webhook:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>MutatingWebhookConfiguration<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>webhooks</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>my-webhook.example.com<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>failurePolicy</span>:<span style=color:#bbb> </span>Fail<span style=color:#bbb> </span></span></span></code></pre></div><p>The default <code>failurePolicy</code> for an admission webhooks is <code>Fail</code>.</p><h2 id=monitoring-admission-webhooks>Monitoring admission webhooks</h2><p>The API server provides ways to monitor admission webhook behaviors. These monitoring mechanisms help cluster admins to answer questions like:</p><ol><li><p>Which mutating webhook mutated the object in a API request?</p></li><li><p>What change did the mutating webhook applied to the object?</p></li><li><p>Which webhooks are frequently rejecting API requests? What's the reason for a rejection?</p></li></ol><h3 id=mutating-webhook-auditing-annotations>Mutating webhook auditing annotations</h3><p>Sometimes it's useful to know which mutating webhook mutated the object in a API request, and what change did the webhook apply.</p><p>The Kubernetes API server performs <a href=/docs/tasks/debug/debug-cluster/audit/>auditing</a> on each mutating webhook invocation. Each invocation generates an auditing annotation capturing if a request object is mutated by the invocation, and optionally generates an annotation capturing the applied patch from the webhook admission response. The annotations are set in the audit event for given request on given stage of its execution, which is then pre-processed according to a certain policy and written to a backend.</p><p>The audit level of a event determines which annotations get recorded:</p><ul><li><p>At <code>Metadata</code> audit level or higher, an annotation with key <code>mutation.webhook.admission.k8s.io/round_{round idx}_index_{order idx}</code> gets logged with JSON payload indicating a webhook gets invoked for given request and whether it mutated the object or not.</p><p>For example, the following annotation gets recorded for a webhook being reinvoked. The webhook is ordered the third in the mutating webhook chain, and didn't mutated the request object during the invocation.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:#080;font-style:italic># the audit event recorded</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"kind": </span><span style=color:#b44>"Event"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"apiVersion": </span><span style=color:#b44>"audit.k8s.io/v1"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"annotations": </span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"mutation.webhook.admission.k8s.io/round_1_index_2": "{\"configuration\":\"my-mutating-webhook-configuration.example.com\",\"webhook\":\"my-webhook.example.com\",\"mutated\": </span><span style=color:#a2f;font-weight:700>false</span>}"<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># other annotations</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>...<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>}<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># other fields</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>...<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>}<span style=color:#bbb> </span></span></span></code></pre></div><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:#080;font-style:italic># the annotation value deserialized</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"configuration": </span><span style=color:#b44>"my-mutating-webhook-configuration.example.com"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"webhook": </span><span style=color:#b44>"my-webhook.example.com"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"mutated": </span><span style=color:#a2f;font-weight:700>false</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>}<span style=color:#bbb> </span></span></span></code></pre></div><p>The following annotation gets recorded for a webhook being invoked in the first round. The webhook is ordered the first in the mutating webhook chain, and mutated the request object during the invocation.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:#080;font-style:italic># the audit event recorded</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"kind": </span><span style=color:#b44>"Event"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"apiVersion": </span><span style=color:#b44>"audit.k8s.io/v1"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"annotations": </span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"mutation.webhook.admission.k8s.io/round_0_index_0": "{\"configuration\":\"my-mutating-webhook-configuration.example.com\",\"webhook\":\"my-webhook-always-mutate.example.com\",\"mutated\": </span><span style=color:#a2f;font-weight:700>true</span>}"<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># other annotations</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>...<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>}<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># other fields</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>...<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>}<span style=color:#bbb> </span></span></span></code></pre></div><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:#080;font-style:italic># the annotation value deserialized</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"configuration": </span><span style=color:#b44>"my-mutating-webhook-configuration.example.com"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"webhook": </span><span style=color:#b44>"my-webhook-always-mutate.example.com"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"mutated": </span><span style=color:#a2f;font-weight:700>true</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>}<span style=color:#bbb> </span></span></span></code></pre></div></li><li><p>At <code>Request</code> audit level or higher, an annotation with key <code>patch.webhook.admission.k8s.io/round_{round idx}_index_{order idx}</code> gets logged with JSON payload indicating a webhook gets invoked for given request and what patch gets applied to the request object.</p><p>For example, the following annotation gets recorded for a webhook being reinvoked. The webhook is ordered the fourth in the mutating webhook chain, and responded with a JSON patch which got applied to the request object.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:#080;font-style:italic># the audit event recorded</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"kind": </span><span style=color:#b44>"Event"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"apiVersion": </span><span style=color:#b44>"audit.k8s.io/v1"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"annotations": </span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"patch.webhook.admission.k8s.io/round_1_index_3": </span><span style=color:#b44>"{\"configuration\":\"my-other-mutating-webhook-configuration.example.com\",\"webhook\":\"my-webhook-always-mutate.example.com\",\"patch\":[{\"op\":\"add\",\"path\":\"/data/mutation-stage\",\"value\":\"yes\"}],\"patchType\":\"JSONPatch\"}"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># other annotations</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>...<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>}<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># other fields</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>...<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>}<span style=color:#bbb> </span></span></span></code></pre></div><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:#080;font-style:italic># the annotation value deserialized</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"configuration": </span><span style=color:#b44>"my-other-mutating-webhook-configuration.example.com"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"webhook": </span><span style=color:#b44>"my-webhook-always-mutate.example.com"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"patchType": </span><span style=color:#b44>"JSONPatch"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"patch": </span>[<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>{<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"op": </span><span style=color:#b44>"add"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"path": </span><span style=color:#b44>"/data/mutation-stage"</span>,<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>"value": </span><span style=color:#b44>"yes"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>}<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>}<span style=color:#bbb> </span></span></span></code></pre></div></li></ul><h3 id=admission-webhook-metrics>Admission webhook metrics</h3><p>The API server exposes Prometheus metrics from the <code>/metrics</code> endpoint, which can be used for monitoring and diagnosing API server status. The following metrics record status related to admission webhooks.</p><h4 id=api-server-admission-webhook-rejection-count>API server admission webhook rejection count</h4><p>Sometimes it's useful to know which admission webhooks are frequently rejecting API requests, and the reason for a rejection.</p><p>The API server exposes a Prometheus counter metric recording admission webhook rejections. The metrics are labelled to identify the causes of webhook rejection(s):</p><ul><li><p><code>name</code>: the name of the webhook that rejected a request.</p></li><li><p><code>operation</code>: the operation type of the request, can be one of <code>CREATE</code>, <code>UPDATE</code>, <code>DELETE</code> and <code>CONNECT</code>.</p></li><li><p><code>type</code>: the admission webhook type, can be one of <code>admit</code> and <code>validating</code>.</p></li><li><p><code>error_type</code>: identifies if an error occurred during the webhook invocation that caused the rejection. Its value can be one of:</p><ul><li><code>calling_webhook_error</code>: unrecognized errors or timeout errors from the admission webhook happened and the webhook's <a href=#failure-policy>Failure policy</a> is set to <code>Fail</code>.</li><li><code>no_error</code>: no error occurred. The webhook rejected the request with <code>allowed: false</code> in the admission response. The metrics label <code>rejection_code</code> records the <code>.status.code</code> set in the admission response.</li><li><code>apiserver_internal_error</code>: an API server internal error happened.</li></ul></li><li><p><code>rejection_code</code>: the HTTP status code set in the admission response when a webhook rejected a request.</p></li></ul><p>Example of the rejection count metrics:</p><pre tabindex=0><code># HELP apiserver_admission_webhook_rejection_count [ALPHA] Admission webhook rejection count, identified by name and broken out for each admission type (validating or admit) and operation. Additional labels specify an error type (calling_webhook_error or apiserver_internal_error if an error occurred; no_error otherwise) and optionally a non-zero rejection code if the webhook rejects the request with an HTTP status code (honored by the apiserver when the code is greater or equal to 400). Codes greater than 600 are truncated to 600, to keep the metrics cardinality bounded. # TYPE apiserver_admission_webhook_rejection_count counter apiserver_admission_webhook_rejection_count{error_type="calling_webhook_error",name="always-timeout-webhook.example.com",operation="CREATE",rejection_code="0",type="validating"} 1 apiserver_admission_webhook_rejection_count{error_type="calling_webhook_error",name="invalid-admission-response-webhook.example.com",operation="CREATE",rejection_code="0",type="validating"} 1 apiserver_admission_webhook_rejection_count{error_type="no_error",name="deny-unwanted-configmap-data.example.com",operation="CREATE",rejection_code="400",type="validating"} 13 </code></pre><h2 id=best-practices-and-warnings>Best practices and warnings</h2><h3 id=idempotence>Idempotence</h3><p>An idempotent mutating admission webhook is able to successfully process an object it has already admitted and potentially modified. The admission can be applied multiple times without changing the result beyond the initial application.</p><h4 id=example-of-idempotent-mutating-admission-webhooks>Example of idempotent mutating admission webhooks:</h4><ol><li><p>For a <code>CREATE</code> pod request, set the field <code>.spec.securityContext.runAsNonRoot</code> of the pod to true, to enforce security best practices.</p></li><li><p>For a <code>CREATE</code> pod request, if the field <code>.spec.containers[].resources.limits</code> of a container is not set, set default resource limits.</p></li><li><p>For a <code>CREATE</code> pod request, inject a sidecar container with name <code>foo-sidecar</code> if no container with the name <code>foo-sidecar</code> already exists.</p></li></ol><p>In the cases above, the webhook can be safely reinvoked, or admit an object that already has the fields set.</p><h4 id=example-of-non-idempotent-mutating-admission-webhooks>Example of non-idempotent mutating admission webhooks:</h4><ol><li><p>For a <code>CREATE</code> pod request, inject a sidecar container with name <code>foo-sidecar</code> suffixed with the current timestamp (e.g. <code>foo-sidecar-19700101-000000</code>).</p></li><li><p>For a <code>CREATE</code>/<code>UPDATE</code> pod request, reject if the pod has label <code>"env"</code> set, otherwise add an <code>"env": "prod"</code> label to the pod.</p></li><li><p>For a <code>CREATE</code> pod request, blindly append a sidecar container named <code>foo-sidecar</code> without looking to see if there is already a <code>foo-sidecar</code> container in the pod.</p></li></ol><p>In the first case above, reinvoking the webhook can result in the same sidecar being injected multiple times to a pod, each time with a different container name. Similarly the webhook can inject duplicated containers if the sidecar already exists in a user-provided pod.</p><p>In the second case above, reinvoking the webhook will result in the webhook failing on its own output.</p><p>In the third case above, reinvoking the webhook will result in duplicated containers in the pod spec, which makes the request invalid and rejected by the API server.</p><h3 id=intercepting-all-versions-of-an-object>Intercepting all versions of an object</h3><p>It is recommended that admission webhooks should always intercept all versions of an object by setting <code>.webhooks[].matchPolicy</code> to <code>Equivalent</code>. It is also recommended that admission webhooks should prefer registering for stable versions of resources. Failure to intercept all versions of an object can result in admission policies not being enforced for requests in certain versions. See <a href=#matching-requests-matchpolicy>Matching requests: matchPolicy</a> for examples.</p><h3 id=availability>Availability</h3><p>It is recommended that admission webhooks should evaluate as quickly as possible (typically in milliseconds), since they add to API request latency. It is encouraged to use a small timeout for webhooks. See <a href=#timeouts>Timeouts</a> for more detail.</p><p>It is recommended that admission webhooks should leverage some format of load-balancing, to provide high availability and performance benefits. If a webhook is running within the cluster, you can run multiple webhook backends behind a service to leverage the load-balancing that service supports.</p><h3 id=guaranteeing-the-final-state-of-the-object-is-seen>Guaranteeing the final state of the object is seen</h3><p>Admission webhooks that need to guarantee they see the final state of the object in order to enforce policy should use a validating admission webhook, since objects can be modified after being seen by mutating webhooks.</p><p>For example, a mutating admission webhook is configured to inject a sidecar container with name "foo-sidecar" on every <code>CREATE</code> pod request. If the sidecar <em>must</em> be present, a validating admisson webhook should also be configured to intercept <code>CREATE</code> pod requests, and validate that a container with name "foo-sidecar" with the expected configuration exists in the to-be-created object.</p><h3 id=avoiding-deadlocks-in-self-hosted-webhooks>Avoiding deadlocks in self-hosted webhooks</h3><p>A webhook running inside the cluster might cause deadlocks for its own deployment if it is configured to intercept resources required to start its own pods.</p><p>For example, a mutating admission webhook is configured to admit <code>CREATE</code> pod requests only if a certain label is set in the pod (e.g. <code>"env": "prod"</code>). The webhook server runs in a deployment which doesn't set the <code>"env"</code> label. When a node that runs the webhook server pods becomes unhealthy, the webhook deployment will try to reschedule the pods to another node. However the requests will get rejected by the existing webhook server since the <code>"env"</code> label is unset, and the migration cannot happen.</p><p>It is recommended to exclude the namespace where your webhook is running with a <a href=#matching-requests-namespaceselector>namespaceSelector</a>.</p><h3 id=side-effects-1>Side effects</h3><p>It is recommended that admission webhooks should avoid side effects if possible, which means the webhooks operate only on the content of the <code>AdmissionReview</code> sent to them, and do not make out-of-band changes. The <code>.webhooks[].sideEffects</code> field should be set to <code>None</code> if a webhook doesn't have any side effect.</p><p>If side effects are required during the admission evaluation, they must be suppressed when processing an <code>AdmissionReview</code> object with <code>dryRun</code> set to <code>true</code>, and the <code>.webhooks[].sideEffects</code> field should be set to <code>NoneOnDryRun</code>. See <a href=#side-effects>Side effects</a> for more detail.</p><h3 id=avoiding-operating-on-the-kube-system-namespace>Avoiding operating on the kube-system namespace</h3><p>The <code>kube-system</code> namespace contains objects created by the Kubernetes system, e.g. service accounts for the control plane components, pods like <code>kube-dns</code>. Accidentally mutating or rejecting requests in the <code>kube-system</code> namespace may cause the control plane components to stop functioning or introduce unknown behavior. If your admission webhooks don't intend to modify the behavior of the Kubernetes control plane, exclude the <code>kube-system</code> namespace from being intercepted using a <a href=#matching-requests-namespaceselector><code>namespaceSelector</code></a>.</p></div><div class=td-content style=page-break-before:always><h1 id=pg-bea207258f3576b8ec7444a20d498e1d>10 - Managing Service Accounts</h1><p>A <em>ServiceAccount</em> provides an identity for processes that run in a Pod.</p><p>A process inside a Pod can use the identity of its associated service account to authenticate to the cluster's API server.</p><p>For an introduction to service accounts, read <a href=/docs/tasks/configure-pod-container/configure-service-account/>configure service accounts</a>.</p><p>This task guide explains some of the concepts behind ServiceAccounts. The guide also explains how to obtain or revoke tokens that represent ServiceAccounts.</p><h2 id=before-you-begin>Before you begin</h2><p>You need to have a Kubernetes cluster, and the kubectl command-line tool must be configured to communicate with your cluster. It is recommended to run this tutorial on a cluster with at least two nodes that are not acting as control plane hosts. If you do not already have a cluster, you can create one by using <a href=https://minikube.sigs.k8s.io/docs/tutorials/multi_node/>minikube</a> or you can use one of these Kubernetes playgrounds:</p><ul><li><a href=https://killercoda.com/playgrounds/scenario/kubernetes>Killercoda</a></li><li><a href=https://labs.play-with-k8s.com/>Play with Kubernetes</a></li></ul><p>To be able to follow these steps exactly, ensure you have a namespace named <code>examplens</code>. If you don't, create one by running:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create namespace examplens </span></span></code></pre></div><h2 id=user-accounts-versus-service-accounts>User accounts versus service accounts</h2><p>Kubernetes distinguishes between the concept of a user account and a service account for a number of reasons:</p><ul><li>User accounts are for humans. Service accounts are for application processes, which (for Kubernetes) run in containers that are part of pods.</li><li>User accounts are intended to be global: names must be unique across all namespaces of a cluster. No matter what namespace you look at, a particular username that represents a user represents the same user. In Kubernetes, service accounts are namespaced: two different namespaces can contain ServiceAccounts that have identical names.</li><li>Typically, a cluster's user accounts might be synchronised from a corporate database, where new user account creation requires special privileges and is tied to complex business processes. By contrast, service account creation is intended to be more lightweight, allowing cluster users to create service accounts for specific tasks on demand. Separating ServiceAccount creation from the steps to onboard human users makes it easier for workloads to follow the principle of least privilege.</li><li>Auditing considerations for humans and service accounts may differ; the separation makes that easier to achieve.</li><li>A configuration bundle for a complex system may include definition of various service accounts for components of that system. Because service accounts can be created without many constraints and have namespaced names, such configuration is usually portable.</li></ul><h2 id=bound-service-account-tokens>Bound service account tokens</h2><p>ServiceAccount tokens can be bound to API objects that exist in the kube-apiserver. This can be used to tie the validity of a token to the existence of another API object. Supported object types are as follows:</p><ul><li>Pod (used for projected volume mounts, see below)</li><li>Secret (can be used to allow revoking a token by deleting the Secret)</li><li>Node (in v1.30, creating new node-bound tokens is alpha, using existing node-bound tokens is beta)</li></ul><p>When a token is bound to an object, the object's <code>metadata.name</code> and <code>metadata.uid</code> are stored as extra 'private claims' in the issued JWT.</p><p>When a bound token is presented to the kube-apiserver, the service account authenticator will extract and verify these claims. If the referenced object or the ServiceAccount is pending deletion (for example, due to finalizers), then for any instant that is 60 seconds (or more) after the <code>.metadata.deletionTimestamp</code> date, authentication with that token would fail. If the referenced object no longer exists (or its <code>metadata.uid</code> does not match), the request will not be authenticated.</p><h3 id=additional-metadata-in-pod-bound-tokens>Additional metadata in Pod bound tokens</h3><div class="feature-state-notice feature-beta" title="Feature Gate: ServiceAccountTokenPodNodeInfo"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.30 [beta]</code> (enabled by default: true)</div><p>When a service account token is bound to a Pod object, additional metadata is also embedded into the token that indicates the value of the bound pod's <code>spec.nodeName</code> field, and the uid of that Node, if available.</p><p>This node information is <strong>not</strong> verified by the kube-apiserver when the token is used for authentication. It is included so integrators do not have to fetch Pod or Node API objects to check the associated Node name and uid when inspecting a JWT.</p><h3 id=verifying-and-inspecting-private-claims>Verifying and inspecting private claims</h3><p>The <code>TokenReview</code> API can be used to verify and extract private claims from a token:</p><ol><li>First, assume you have a pod named <code>test-pod</code> and a service account named <code>my-sa</code>.</li><li>Create a token that is bound to this Pod:</li></ol><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create token my-sa --bound-object-kind<span style=color:#666>=</span><span style=color:#b44>"Pod"</span> --bound-object-name<span style=color:#666>=</span><span style=color:#b44>"test-pod"</span> </span></span></code></pre></div><ol start=3><li>Copy this token into a new file named <code>tokenreview.yaml</code>:</li></ol><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>authentication.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>TokenReview<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>spec</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>token</span>:<span style=color:#bbb> </span><token from step 2><span style=color:#bbb> </span></span></span></code></pre></div><ol start=4><li>Submit this resource to the apiserver for review:</li></ol><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create -o yaml -f tokenreview.yaml <span style=color:#080;font-style:italic># we use '-o yaml' so we can inspect the output</span> </span></span></code></pre></div><p>You should see an output like below:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>authentication.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>TokenReview<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>creationTimestamp</span>:<span style=color:#bbb> </span><span style=color:#a2f;font-weight:700>null</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>spec</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>token</span>:<span style=color:#bbb> </span><token><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>status</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>audiences</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- https://kubernetes.default.svc.cluster.local<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>authenticated</span>:<span style=color:#bbb> </span><span style=color:#a2f;font-weight:700>true</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>user</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>extra</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>authentication.kubernetes.io/credential-id</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- JTI=7ee52be0-9045-4653-aa5e-0da57b8dccdc<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>authentication.kubernetes.io/node-name</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- kind-control-plane<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>authentication.kubernetes.io/node-uid</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- 497e9d9a-47aa-4930-b0f6-9f2fb574c8c6<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>authentication.kubernetes.io/pod-name</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- test-pod<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>authentication.kubernetes.io/pod-uid</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- e87dbbd6-3d7e-45db-aafb-72b24627dff5<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>groups</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- system:serviceaccounts<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- system:serviceaccounts:default<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- system:authenticated<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>uid</span>:<span style=color:#bbb> </span>f8b4161b-2e2b-11e9-86b7-2afc33b31a7e<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>username</span>:<span style=color:#bbb> </span>system:serviceaccount:default:my-sa<span style=color:#bbb> </span></span></span></code></pre></div><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>Despite using <code>kubectl create -f</code> to create this resource, and defining it similar to other resource types in Kubernetes, TokenReview is a special type and the kube-apiserver does not actually persist the TokenReview object into etcd. Hence <code>kubectl get tokenreview</code> is not a valid command.</div><h2 id=bound-service-account-token-volume>Bound service account token volume mechanism</h2><div class="feature-state-notice feature-stable" title="Feature Gate: BoundServiceAccountTokenVolume"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.22 [stable]</code> (enabled by default: true)</div><p>By default, the Kubernetes control plane (specifically, the <a href=#serviceaccount-admission-controller>ServiceAccount admission controller</a>) adds a <a href=/docs/concepts/storage/projected-volumes/>projected volume</a> to Pods, and this volume includes a token for Kubernetes API access.</p><p>Here's an example of how that looks for a launched Pod:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:#00f;font-weight:700>...</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>kube-api-access-<random-suffix><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>projected</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>sources</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>serviceAccountToken</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>path</span>:<span style=color:#bbb> </span>token<span style=color:#bbb> </span><span style=color:#080;font-style:italic># must match the path the app expects</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>configMap</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>items</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>key</span>:<span style=color:#bbb> </span>ca.crt<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>path</span>:<span style=color:#bbb> </span>ca.crt<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>kube-root-ca.crt<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>downwardAPI</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>items</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>fieldRef</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>fieldPath</span>:<span style=color:#bbb> </span>metadata.namespace<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>path</span>:<span style=color:#bbb> </span>namespace<span style=color:#bbb> </span></span></span></code></pre></div><p>That manifest snippet defines a projected volume that consists of three sources. In this case, each source also represents a single path within that volume. The three sources are:</p><ol><li>A <code>serviceAccountToken</code> source, that contains a token that the kubelet acquires from kube-apiserver. The kubelet fetches time-bound tokens using the TokenRequest API. A token served for a TokenRequest expires either when the pod is deleted or after a defined lifespan (by default, that is 1 hour). The kubelet also refreshes that token before the token expires. The token is bound to the specific Pod and has the kube-apiserver as its audience. This mechanism superseded an earlier mechanism that added a volume based on a Secret, where the Secret represented the ServiceAccount for the Pod, but did not expire.</li><li>A <code>configMap</code> source. The ConfigMap contains a bundle of certificate authority data. Pods can use these certificates to make sure that they are connecting to your cluster's kube-apiserver (and not to middlebox or an accidentally misconfigured peer).</li><li>A <code>downwardAPI</code> source that looks up the name of the namespace containing the Pod, and makes that name information available to application code running inside the Pod.</li></ol><p>Any container within the Pod that mounts this particular volume can access the above information.</p><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>There is no specific mechanism to invalidate a token issued via TokenRequest. If you no longer trust a bound service account token for a Pod, you can delete that Pod. Deleting a Pod expires its bound service account tokens.</div><h2 id=manual-secret-management-for-serviceaccounts>Manual Secret management for ServiceAccounts</h2><p>Versions of Kubernetes before v1.22 automatically created credentials for accessing the Kubernetes API. This older mechanism was based on creating token Secrets that could then be mounted into running Pods.</p><p>In more recent versions, including Kubernetes v1.31, API credentials are <a href=#bound-service-account-token-volume>obtained directly</a> using the <a href=/docs/reference/kubernetes-api/authentication-resources/token-request-v1/>TokenRequest</a> API, and are mounted into Pods using a projected volume. The tokens obtained using this method have bounded lifetimes, and are automatically invalidated when the Pod they are mounted into is deleted.</p><p>You can still <a href=/docs/tasks/configure-pod-container/configure-service-account/#manually-create-an-api-token-for-a-serviceaccount>manually create</a> a Secret to hold a service account token; for example, if you need a token that never expires.</p><p>Once you manually create a Secret and link it to a ServiceAccount, the Kubernetes control plane automatically populates the token into that Secret.</p><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>Although the manual mechanism for creating a long-lived ServiceAccount token exists, using <a href=/docs/reference/kubernetes-api/authentication-resources/token-request-v1/>TokenRequest</a> to obtain short-lived API access tokens is recommended instead.</div><h2 id=auto-generated-legacy-serviceaccount-token-clean-up>Auto-generated legacy ServiceAccount token clean up</h2><p>Before version 1.24, Kubernetes automatically generated Secret-based tokens for ServiceAccounts. To distinguish between automatically generated tokens and manually created ones, Kubernetes checks for a reference from the ServiceAccount's secrets field. If the Secret is referenced in the <code>secrets</code> field, it is considered an auto-generated legacy token. Otherwise, it is considered a manually created legacy token. For example:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ServiceAccount<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>build-robot<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespace</span>:<span style=color:#bbb> </span>default<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>secrets</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>build-robot-secret<span style=color:#bbb> </span><span style=color:#080;font-style:italic># usually NOT present for a manually generated token </span><span style=color:#bbb> </span></span></span></code></pre></div><p>Beginning from version 1.29, legacy ServiceAccount tokens that were generated automatically will be marked as invalid if they remain unused for a certain period of time (set to default at one year). Tokens that continue to be unused for this defined period (again, by default, one year) will subsequently be purged by the control plane.</p><p>If users use an invalidated auto-generated token, the token validator will</p><ol><li>add an audit annotation for the key-value pair <code>authentication.k8s.io/legacy-token-invalidated: <secret name>/<namespace></code>,</li><li>increment the <code>invalid_legacy_auto_token_uses_total</code> metric count,</li><li>update the Secret label <code>kubernetes.io/legacy-token-last-used</code> with the new date,</li><li>return an error indicating that the token has been invalidated.</li></ol><p>When receiving this validation error, users can update the Secret to remove the <code>kubernetes.io/legacy-token-invalid-since</code> label to temporarily allow use of this token.</p><p>Here's an example of an auto-generated legacy token that has been marked with the <code>kubernetes.io/legacy-token-last-used</code> and <code>kubernetes.io/legacy-token-invalid-since</code> labels:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Secret<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>build-robot-secret<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespace</span>:<span style=color:#bbb> </span>default<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>labels</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kubernetes.io/legacy-token-last-used</span>:<span style=color:#bbb> </span>2022-10-24<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kubernetes.io/legacy-token-invalid-since</span>:<span style=color:#bbb> </span>2023-10-25<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>annotations</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kubernetes.io/service-account.name</span>:<span style=color:#bbb> </span>build-robot<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>type</span>:<span style=color:#bbb> </span>kubernetes.io/service-account-token<span style=color:#bbb> </span></span></span></code></pre></div><h2 id=control-plane-details>Control plane details</h2><h3 id=serviceaccount-controller>ServiceAccount controller</h3><p>A ServiceAccount controller manages the ServiceAccounts inside namespaces, and ensures a ServiceAccount named "default" exists in every active namespace.</p><h3 id=token-controller>Token controller</h3><p>The service account token controller runs as part of <code>kube-controller-manager</code>. This controller acts asynchronously. It:</p><ul><li>watches for ServiceAccount deletion and deletes all corresponding ServiceAccount token Secrets.</li><li>watches for ServiceAccount token Secret addition, and ensures the referenced ServiceAccount exists, and adds a token to the Secret if needed.</li><li>watches for Secret deletion and removes a reference from the corresponding ServiceAccount if needed.</li></ul><p>You must pass a service account private key file to the token controller in the <code>kube-controller-manager</code> using the <code>--service-account-private-key-file</code> flag. The private key is used to sign generated service account tokens. Similarly, you must pass the corresponding public key to the <code>kube-apiserver</code> using the <code>--service-account-key-file</code> flag. The public key will be used to verify the tokens during authentication.</p><h3 id=serviceaccount-admission-controller>ServiceAccount admission controller</h3><p>The modification of pods is implemented via a plugin called an <a href=/docs/reference/access-authn-authz/admission-controllers/>Admission Controller</a>. It is part of the API server. This admission controller acts synchronously to modify pods as they are created. When this plugin is active (and it is by default on most distributions), then it does the following when a Pod is created:</p><ol><li>If the pod does not have a <code>.spec.serviceAccountName</code> set, the admission controller sets the name of the ServiceAccount for this incoming Pod to <code>default</code>.</li><li>The admission controller ensures that the ServiceAccount referenced by the incoming Pod exists. If there is no ServiceAccount with a matching name, the admission controller rejects the incoming Pod. That check applies even for the <code>default</code> ServiceAccount.</li><li>Provided that neither the ServiceAccount's <code>automountServiceAccountToken</code> field nor the Pod's <code>automountServiceAccountToken</code> field is set to <code>false</code>:<ul><li>the admission controller mutates the incoming Pod, adding an extra <a class=glossary-tooltip title='A directory containing data, accessible to the containers in a pod.' data-toggle=tooltip data-placement=top href=/docs/concepts/storage/volumes/ target=_blank aria-label=volume>volume</a> that contains a token for API access.</li><li>the admission controller adds a <code>volumeMount</code> to each container in the Pod, skipping any containers that already have a volume mount defined for the path <code>/var/run/secrets/kubernetes.io/serviceaccount</code>. For Linux containers, that volume is mounted at <code>/var/run/secrets/kubernetes.io/serviceaccount</code>; on Windows nodes, the mount is at the equivalent path.</li></ul></li><li>If the spec of the incoming Pod doesn't already contain any <code>imagePullSecrets</code>, then the admission controller adds <code>imagePullSecrets</code>, copying them from the <code>ServiceAccount</code>.</li></ol><h3 id=legacy-serviceaccount-token-tracking-controller>Legacy ServiceAccount token tracking controller</h3><div class="feature-state-notice feature-stable" title="Feature Gate: LegacyServiceAccountTokenTracking"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.28 [stable]</code> (enabled by default: true)</div><p>This controller generates a ConfigMap called <code>kube-system/kube-apiserver-legacy-service-account-token-tracking</code> in the <code>kube-system</code> namespace. The ConfigMap records the timestamp when legacy service account tokens began to be monitored by the system.</p><h3 id=legacy-serviceaccount-token-cleaner>Legacy ServiceAccount token cleaner</h3><div class="feature-state-notice feature-stable" title="Feature Gate: LegacyServiceAccountTokenCleanUp"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.30 [stable]</code> (enabled by default: true)</div><p>The legacy ServiceAccount token cleaner runs as part of the <code>kube-controller-manager</code> and checks every 24 hours to see if any auto-generated legacy ServiceAccount token has not been used in a <em>specified amount of time</em>. If so, the cleaner marks those tokens as invalid.</p><p>The cleaner works by first checking the ConfigMap created by the control plane (provided that <code>LegacyServiceAccountTokenTracking</code> is enabled). If the current time is a <em>specified amount of time</em> after the date in the ConfigMap, the cleaner then loops through the list of Secrets in the cluster and evaluates each Secret that has the type <code>kubernetes.io/service-account-token</code>.</p><p>If a Secret meets all of the following conditions, the cleaner marks it as invalid:</p><ul><li>The Secret is auto-generated, meaning that it is bi-directionally referenced by a ServiceAccount.</li><li>The Secret is not currently mounted by any pods.</li><li>The Secret has not been used in a <em>specified amount of time</em> since it was created or since it was last used.</li></ul><p>The cleaner marks a Secret invalid by adding a label called <code>kubernetes.io/legacy-token-invalid-since</code> to the Secret, with the current date as the value. If an invalid Secret is not used in a <em>specified amount of time</em>, the cleaner will delete it.</p><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>All the <em>specified amount of time</em> above defaults to one year. The cluster administrator can configure this value through the <code>--legacy-service-account-token-clean-up-period</code> command line argument for the <code>kube-controller-manager</code> component.</div><h3 id=tokenrequest-api>TokenRequest API</h3><div class="feature-state-notice feature-stable"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.22 [stable]</code></div><p>You use the <a href=/docs/reference/kubernetes-api/authentication-resources/token-request-v1/>TokenRequest</a> subresource of a ServiceAccount to obtain a time-bound token for that ServiceAccount. You don't need to call this to obtain an API token for use within a container, since the kubelet sets this up for you using a <em>projected volume</em>.</p><p>If you want to use the TokenRequest API from <code>kubectl</code>, see <a href=/docs/tasks/configure-pod-container/configure-service-account/#manually-create-an-api-token-for-a-serviceaccount>Manually create an API token for a ServiceAccount</a>.</p><p>The Kubernetes control plane (specifically, the ServiceAccount admission controller) adds a projected volume to Pods, and the kubelet ensures that this volume contains a token that lets containers authenticate as the right ServiceAccount.</p><p>(This mechanism superseded an earlier mechanism that added a volume based on a Secret, where the Secret represented the ServiceAccount for the Pod but did not expire.)</p><p>Here's an example of how that looks for a launched Pod:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:#00f;font-weight:700>...</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>kube-api-access-<random-suffix><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>projected</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>defaultMode</span>:<span style=color:#bbb> </span><span style=color:#666>420</span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># decimal equivalent of octal 0644</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>sources</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>serviceAccountToken</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expirationSeconds</span>:<span style=color:#bbb> </span><span style=color:#666>3607</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>path</span>:<span style=color:#bbb> </span>token<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>configMap</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>items</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>key</span>:<span style=color:#bbb> </span>ca.crt<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>path</span>:<span style=color:#bbb> </span>ca.crt<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>kube-root-ca.crt<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>downwardAPI</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>items</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>fieldRef</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>fieldPath</span>:<span style=color:#bbb> </span>metadata.namespace<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>path</span>:<span style=color:#bbb> </span>namespace<span style=color:#bbb> </span></span></span></code></pre></div><p>That manifest snippet defines a projected volume that combines information from three sources:</p><ol><li>A <code>serviceAccountToken</code> source, that contains a token that the kubelet acquires from kube-apiserver. The kubelet fetches time-bound tokens using the TokenRequest API. A token served for a TokenRequest expires either when the pod is deleted or after a defined lifespan (by default, that is 1 hour). The token is bound to the specific Pod and has the kube-apiserver as its audience.</li><li>A <code>configMap</code> source. The ConfigMap contains a bundle of certificate authority data. Pods can use these certificates to make sure that they are connecting to your cluster's kube-apiserver (and not to middlebox or an accidentally misconfigured peer).</li><li>A <code>downwardAPI</code> source. This <code>downwardAPI</code> volume makes the name of the namespace containing the Pod available to application code running inside the Pod.</li></ol><p>Any container within the Pod that mounts this volume can access the above information.</p><h2 id=create-token>Create additional API tokens</h2><div class="alert alert-caution" role=alert><h4 class=alert-heading>Caution:</h4>Only create long-lived API tokens if the <a href=#tokenrequest-api>token request</a> mechanism is not suitable. The token request mechanism provides time-limited tokens; because these expire, they represent a lower risk to information security.</div><p>To create a non-expiring, persisted API token for a ServiceAccount, create a Secret of type <code>kubernetes.io/service-account-token</code> with an annotation referencing the ServiceAccount. The control plane then generates a long-lived token and updates that Secret with that generated token data.</p><p>Here is a sample manifest for such a Secret:</p><div class="highlight code-sample"><div class=copy-code-icon><a href=https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/secret/serviceaccount/mysecretname.yaml download=secret/serviceaccount/mysecretname.yaml><code>secret/serviceaccount/mysecretname.yaml</code> </a><img src=/images/copycode.svg class=icon-copycode onclick='copyCode("secret-serviceaccount-mysecretname-yaml")' title="Copy secret/serviceaccount/mysecretname.yaml to clipboard"></img></div><div class=includecode id=secret-serviceaccount-mysecretname-yaml><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Secret<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>type</span>:<span style=color:#bbb> </span>kubernetes.io/service-account-token<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>mysecretname<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>annotations</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kubernetes.io/service-account.name</span>:<span style=color:#bbb> </span>myserviceaccount<span style=color:#bbb> </span></span></span></code></pre></div></div></div><p>To create a Secret based on this example, run:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl -n examplens create -f https://k8s.io/examples/secret/serviceaccount/mysecretname.yaml </span></span></code></pre></div><p>To see the details for that Secret, run:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl -n examplens describe secret mysecretname </span></span></code></pre></div><p>The output is similar to:</p><pre tabindex=0><code>Name: mysecretname Namespace: examplens Labels: <none> Annotations: kubernetes.io/service-account.name=myserviceaccount kubernetes.io/service-account.uid=8a85c4c4-8483-11e9-bc42-526af7764f64 Type: kubernetes.io/service-account-token Data ==== ca.crt: 1362 bytes namespace: 9 bytes token: ... </code></pre><p>If you launch a new Pod into the <code>examplens</code> namespace, it can use the <code>myserviceaccount</code> service-account-token Secret that you just created.</p><div class="alert alert-caution" role=alert><h4 class=alert-heading>Caution:</h4>Do not reference manually created Secrets in the <code>secrets</code> field of a ServiceAccount. Or the manually created Secrets will be cleaned if it is not used for a long time. Please refer to <a href=#auto-generated-legacy-serviceaccount-token-clean-up>auto-generated legacy ServiceAccount token clean up</a>.</div><h2 id=delete-token>Delete/invalidate a ServiceAccount token</h2><p>If you know the name of the Secret that contains the token you want to remove:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl delete secret name-of-secret </span></span></code></pre></div><p>Otherwise, first find the Secret for the ServiceAccount.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span><span style=color:#080;font-style:italic># This assumes that you already have a namespace named 'examplens'</span> </span></span><span style=display:flex><span>kubectl -n examplens get serviceaccount/example-automated-thing -o yaml </span></span></code></pre></div><p>The output is similar to:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ServiceAccount<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>annotations</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kubectl.kubernetes.io/last-applied-configuration</span>:<span style=color:#bbb> </span>|<span style=color:#b44;font-style:italic> </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> {"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{},"name":"example-automated-thing","namespace":"examplens"}}</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>creationTimestamp</span>:<span style=color:#bbb> </span><span style=color:#b44>"2019-07-21T07:07:07Z"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>example-automated-thing<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespace</span>:<span style=color:#bbb> </span>examplens<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resourceVersion</span>:<span style=color:#bbb> </span><span style=color:#b44>"777"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>selfLink</span>:<span style=color:#bbb> </span>/api/v1/namespaces/examplens/serviceaccounts/example-automated-thing<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>uid</span>:<span style=color:#bbb> </span>f23fd170-66f2-4697-b049-e1e266b7f835<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>secrets</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>example-automated-thing-token-zyxwv<span style=color:#bbb> </span></span></span></code></pre></div><p>Then, delete the Secret you now know the name of:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl -n examplens delete secret/example-automated-thing-token-zyxwv </span></span></code></pre></div><h2 id=clean-up>Clean up</h2><p>If you created a namespace <code>examplens</code> to experiment with, you can remove it:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl delete namespace examplens </span></span></code></pre></div><h2 id=what-s-next>What's next</h2><ul><li>Read more details about <a href=/docs/concepts/storage/projected-volumes/>projected volumes</a>.</li></ul></div><div class=td-content style=page-break-before:always><h1 id=pg-3d0c14d1e3cfade38febc343cd044c73>11 - Certificates and Certificate Signing Requests</h1><p>Kubernetes certificate and trust bundle APIs enable automation of <a href=https://www.itu.int/rec/T-REC-X.509>X.509</a> credential provisioning by providing a programmatic interface for clients of the Kubernetes API to request and obtain X.509 <a class=glossary-tooltip title='A cryptographically secure file used to validate access to the Kubernetes cluster.' data-toggle=tooltip data-placement=top href=/docs/tasks/tls/managing-tls-in-a-cluster/ target=_blank aria-label=certificates>certificates</a> from a Certificate Authority (CA).</p><p>There is also experimental (alpha) support for distributing <a href=#cluster-trust-bundles>trust bundles</a>.</p><h2 id=certificate-signing-requests>Certificate signing requests</h2><div class="feature-state-notice feature-stable"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.19 [stable]</code></div><p>A <a href=/docs/reference/kubernetes-api/authentication-resources/certificate-signing-request-v1/>CertificateSigningRequest</a> (CSR) resource is used to request that a certificate be signed by a denoted signer, after which the request may be approved or denied before finally being signed.</p><h3 id=request-signing-process>Request signing process</h3><p>The CertificateSigningRequest resource type allows a client to ask for an X.509 certificate be issued, based on a signing request. The CertificateSigningRequest object includes a PEM-encoded PKCS#10 signing request in the <code>spec.request</code> field. The CertificateSigningRequest denotes the signer (the recipient that the request is being made to) using the <code>spec.signerName</code> field. Note that <code>spec.signerName</code> is a required key after API version <code>certificates.k8s.io/v1</code>. In Kubernetes v1.22 and later, clients may optionally set the <code>spec.expirationSeconds</code> field to request a particular lifetime for the issued certificate. The minimum valid value for this field is <code>600</code>, i.e. ten minutes.</p><p>Once created, a CertificateSigningRequest must be approved before it can be signed. Depending on the signer selected, a CertificateSigningRequest may be automatically approved by a <a class=glossary-tooltip title='A control loop that watches the shared state of the cluster through the apiserver and makes changes attempting to move the current state towards the desired state.' data-toggle=tooltip data-placement=top href=/docs/concepts/architecture/controller/ target=_blank aria-label=controller>controller</a>. Otherwise, a CertificateSigningRequest must be manually approved either via the REST API (or client-go) or by running <code>kubectl certificate approve</code>. Likewise, a CertificateSigningRequest may also be denied, which tells the configured signer that it must not sign the request.</p><p>For certificates that have been approved, the next step is signing. The relevant signing controller first validates that the signing conditions are met and then creates a certificate. The signing controller then updates the CertificateSigningRequest, storing the new certificate into the <code>status.certificate</code> field of the existing CertificateSigningRequest object. The <code>status.certificate</code> field is either empty or contains a X.509 certificate, encoded in PEM format. The CertificateSigningRequest <code>status.certificate</code> field is empty until the signer does this.</p><p>Once the <code>status.certificate</code> field has been populated, the request has been completed and clients can now fetch the signed certificate PEM data from the CertificateSigningRequest resource. The signers can instead deny certificate signing if the approval conditions are not met.</p><p>In order to reduce the number of old CertificateSigningRequest resources left in a cluster, a garbage collection controller runs periodically. The garbage collection removes CertificateSigningRequests that have not changed state for some duration:</p><ul><li>Approved requests: automatically deleted after 1 hour</li><li>Denied requests: automatically deleted after 1 hour</li><li>Failed requests: automatically deleted after 1 hour</li><li>Pending requests: automatically deleted after 24 hours</li><li>All requests: automatically deleted after the issued certificate has expired</li></ul><h3 id=authorization>Certificate signing authorization</h3><p>To allow creating a CertificateSigningRequest and retrieving any CertificateSigningRequest:</p><ul><li>Verbs: <code>create</code>, <code>get</code>, <code>list</code>, <code>watch</code>, group: <code>certificates.k8s.io</code>, resource: <code>certificatesigningrequests</code></li></ul><p>For example:</p><div class="highlight code-sample"><div class=copy-code-icon><a href=https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/access/certificate-signing-request/clusterrole-create.yaml download=access/certificate-signing-request/clusterrole-create.yaml><code>access/certificate-signing-request/clusterrole-create.yaml</code> </a><img src=/images/copycode.svg class=icon-copycode onclick='copyCode("access-certificate-signing-request-clusterrole-create-yaml")' title="Copy access/certificate-signing-request/clusterrole-create.yaml to clipboard"></img></div><div class=includecode id=access-certificate-signing-request-clusterrole-create-yaml><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ClusterRole<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>csr-creator<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- certificates.k8s.io<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- certificatesigningrequests<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- create<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- get<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- list<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- watch<span style=color:#bbb> </span></span></span></code></pre></div></div></div><p>To allow approving a CertificateSigningRequest:</p><ul><li>Verbs: <code>get</code>, <code>list</code>, <code>watch</code>, group: <code>certificates.k8s.io</code>, resource: <code>certificatesigningrequests</code></li><li>Verbs: <code>update</code>, group: <code>certificates.k8s.io</code>, resource: <code>certificatesigningrequests/approval</code></li><li>Verbs: <code>approve</code>, group: <code>certificates.k8s.io</code>, resource: <code>signers</code>, resourceName: <code><signerNameDomain>/<signerNamePath></code> or <code><signerNameDomain>/*</code></li></ul><p>For example:</p><div class="highlight code-sample"><div class=copy-code-icon><a href=https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/access/certificate-signing-request/clusterrole-approve.yaml download=access/certificate-signing-request/clusterrole-approve.yaml><code>access/certificate-signing-request/clusterrole-approve.yaml</code> </a><img src=/images/copycode.svg class=icon-copycode onclick='copyCode("access-certificate-signing-request-clusterrole-approve-yaml")' title="Copy access/certificate-signing-request/clusterrole-approve.yaml to clipboard"></img></div><div class=includecode id=access-certificate-signing-request-clusterrole-approve-yaml><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ClusterRole<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>csr-approver<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- certificates.k8s.io<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- certificatesigningrequests<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- get<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- list<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- watch<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- certificates.k8s.io<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- certificatesigningrequests/approval<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- update<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- certificates.k8s.io<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- signers<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resourceNames</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- example.com/my-signer-name<span style=color:#bbb> </span><span style=color:#080;font-style:italic># example.com/* can be used to authorize for all signers in the 'example.com' domain</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- approve<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span></code></pre></div></div></div><p>To allow signing a CertificateSigningRequest:</p><ul><li>Verbs: <code>get</code>, <code>list</code>, <code>watch</code>, group: <code>certificates.k8s.io</code>, resource: <code>certificatesigningrequests</code></li><li>Verbs: <code>update</code>, group: <code>certificates.k8s.io</code>, resource: <code>certificatesigningrequests/status</code></li><li>Verbs: <code>sign</code>, group: <code>certificates.k8s.io</code>, resource: <code>signers</code>, resourceName: <code><signerNameDomain>/<signerNamePath></code> or <code><signerNameDomain>/*</code></li></ul><div class="highlight code-sample"><div class=copy-code-icon><a href=https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/access/certificate-signing-request/clusterrole-sign.yaml download=access/certificate-signing-request/clusterrole-sign.yaml><code>access/certificate-signing-request/clusterrole-sign.yaml</code> </a><img src=/images/copycode.svg class=icon-copycode onclick='copyCode("access-certificate-signing-request-clusterrole-sign-yaml")' title="Copy access/certificate-signing-request/clusterrole-sign.yaml to clipboard"></img></div><div class=includecode id=access-certificate-signing-request-clusterrole-sign-yaml><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ClusterRole<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>csr-signer<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>rules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- certificates.k8s.io<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- certificatesigningrequests<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- get<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- list<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- watch<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- certificates.k8s.io<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- certificatesigningrequests/status<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- update<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- certificates.k8s.io<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- signers<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resourceNames</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- example.com/my-signer-name<span style=color:#bbb> </span><span style=color:#080;font-style:italic># example.com/* can be used to authorize for all signers in the 'example.com' domain</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>verbs</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- sign<span style=color:#bbb> </span></span></span></code></pre></div></div></div><h2 id=signers>Signers</h2><p>Signers abstractly represent the entity or entities that might sign, or have signed, a security certificate.</p><p>Any signer that is made available for outside a particular cluster should provide information about how the signer works, so that consumers can understand what that means for CertifcateSigningRequests and (if enabled) <a href=#cluster-trust-bundles>ClusterTrustBundles</a>. This includes:</p><ol><li><strong>Trust distribution</strong>: how trust anchors (CA certificates or certificate bundles) are distributed.</li><li><strong>Permitted subjects</strong>: any restrictions on and behavior when a disallowed subject is requested.</li><li><strong>Permitted x509 extensions</strong>: including IP subjectAltNames, DNS subjectAltNames, Email subjectAltNames, URI subjectAltNames etc, and behavior when a disallowed extension is requested.</li><li><strong>Permitted key usages / extended key usages</strong>: any restrictions on and behavior when usages different than the signer-determined usages are specified in the CSR.</li><li><strong>Expiration/certificate lifetime</strong>: whether it is fixed by the signer, configurable by the admin, determined by the CSR <code>spec.expirationSeconds</code> field, etc and the behavior when the signer-determined expiration is different from the CSR <code>spec.expirationSeconds</code> field.</li><li><strong>CA bit allowed/disallowed</strong>: and behavior if a CSR contains a request a for a CA certificate when the signer does not permit it.</li></ol><p>Commonly, the <code>status.certificate</code> field of a CertificateSigningRequest contains a single PEM-encoded X.509 certificate once the CSR is approved and the certificate is issued. Some signers store multiple certificates into the <code>status.certificate</code> field. In that case, the documentation for the signer should specify the meaning of additional certificates; for example, this might be the certificate plus intermediates to be presented during TLS handshakes.</p><p>If you want to make the <em>trust anchor</em> (root certificate) available, this should be done separately from a CertificateSigningRequest and its <code>status.certificate</code> field. For example, you could use a ClusterTrustBundle.</p><p>The PKCS#10 signing request format does not have a standard mechanism to specify a certificate expiration or lifetime. The expiration or lifetime therefore has to be set through the <code>spec.expirationSeconds</code> field of the CSR object. The built-in signers use the <code>ClusterSigningDuration</code> configuration option, which defaults to 1 year, (the <code>--cluster-signing-duration</code> command-line flag of the kube-controller-manager) as the default when no <code>spec.expirationSeconds</code> is specified. When <code>spec.expirationSeconds</code> is specified, the minimum of <code>spec.expirationSeconds</code> and <code>ClusterSigningDuration</code> is used.</p><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>The <code>spec.expirationSeconds</code> field was added in Kubernetes v1.22. Earlier versions of Kubernetes do not honor this field. Kubernetes API servers prior to v1.22 will silently drop this field when the object is created.</div><h3 id=kubernetes-signers>Kubernetes signers</h3><p>Kubernetes provides built-in signers that each have a well-known <code>signerName</code>:</p><ol><li><p><code>kubernetes.io/kube-apiserver-client</code>: signs certificates that will be honored as client certificates by the API server. Never auto-approved by <a class=glossary-tooltip title='Control Plane component that runs controller processes.' data-toggle=tooltip data-placement=top href=/docs/reference/command-line-tools-reference/kube-controller-manager/ target=_blank aria-label=kube-controller-manager>kube-controller-manager</a>.</p><ol><li>Trust distribution: signed certificates must be honored as client certificates by the API server. The CA bundle is not distributed by any other means.</li><li>Permitted subjects - no subject restrictions, but approvers and signers may choose not to approve or sign. Certain subjects like cluster-admin level users or groups vary between distributions and installations, but deserve additional scrutiny before approval and signing. The <code>CertificateSubjectRestriction</code> admission plugin is enabled by default to restrict <code>system:masters</code>, but it is often not the only cluster-admin subject in a cluster.</li><li>Permitted x509 extensions - honors subjectAltName and key usage extensions and discards other extensions.</li><li>Permitted key usages - must include <code>["client auth"]</code>. Must not include key usages beyond <code>["digital signature", "key encipherment", "client auth"]</code>.</li><li>Expiration/certificate lifetime - for the kube-controller-manager implementation of this signer, set to the minimum of the <code>--cluster-signing-duration</code> option or, if specified, the <code>spec.expirationSeconds</code> field of the CSR object.</li><li>CA bit allowed/disallowed - not allowed.</li></ol></li><li><p><code>kubernetes.io/kube-apiserver-client-kubelet</code>: signs client certificates that will be honored as client certificates by the API server. May be auto-approved by <a class=glossary-tooltip title='Control Plane component that runs controller processes.' data-toggle=tooltip data-placement=top href=/docs/reference/command-line-tools-reference/kube-controller-manager/ target=_blank aria-label=kube-controller-manager>kube-controller-manager</a>.</p><ol><li>Trust distribution: signed certificates must be honored as client certificates by the API server. The CA bundle is not distributed by any other means.</li><li>Permitted subjects - organizations are exactly <code>["system:nodes"]</code>, common name is "<code>system:node:${NODE_NAME}</code>".</li><li>Permitted x509 extensions - honors key usage extensions, forbids subjectAltName extensions and drops other extensions.</li><li>Permitted key usages - <code>["key encipherment", "digital signature", "client auth"]</code> or <code>["digital signature", "client auth"]</code>.</li><li>Expiration/certificate lifetime - for the kube-controller-manager implementation of this signer, set to the minimum of the <code>--cluster-signing-duration</code> option or, if specified, the <code>spec.expirationSeconds</code> field of the CSR object.</li><li>CA bit allowed/disallowed - not allowed.</li></ol></li><li><p><code>kubernetes.io/kubelet-serving</code>: signs serving certificates that are honored as a valid kubelet serving certificate by the API server, but has no other guarantees. Never auto-approved by <a class=glossary-tooltip title='Control Plane component that runs controller processes.' data-toggle=tooltip data-placement=top href=/docs/reference/command-line-tools-reference/kube-controller-manager/ target=_blank aria-label=kube-controller-manager>kube-controller-manager</a>.</p><ol><li>Trust distribution: signed certificates must be honored by the API server as valid to terminate connections to a kubelet. The CA bundle is not distributed by any other means.</li><li>Permitted subjects - organizations are exactly <code>["system:nodes"]</code>, common name is "<code>system:node:${NODE_NAME}</code>".</li><li>Permitted x509 extensions - honors key usage and DNSName/IPAddress subjectAltName extensions, forbids EmailAddress and URI subjectAltName extensions, drops other extensions. At least one DNS or IP subjectAltName must be present.</li><li>Permitted key usages - <code>["key encipherment", "digital signature", "server auth"]</code> or <code>["digital signature", "server auth"]</code>.</li><li>Expiration/certificate lifetime - for the kube-controller-manager implementation of this signer, set to the minimum of the <code>--cluster-signing-duration</code> option or, if specified, the <code>spec.expirationSeconds</code> field of the CSR object.</li><li>CA bit allowed/disallowed - not allowed.</li></ol></li><li><p><code>kubernetes.io/legacy-unknown</code>: has no guarantees for trust at all. Some third-party distributions of Kubernetes may honor client certificates signed by it. The stable CertificateSigningRequest API (version <code>certificates.k8s.io/v1</code> and later) does not allow to set the <code>signerName</code> as <code>kubernetes.io/legacy-unknown</code>. Never auto-approved by <a class=glossary-tooltip title='Control Plane component that runs controller processes.' data-toggle=tooltip data-placement=top href=/docs/reference/command-line-tools-reference/kube-controller-manager/ target=_blank aria-label=kube-controller-manager>kube-controller-manager</a>.</p><ol><li>Trust distribution: None. There is no standard trust or distribution for this signer in a Kubernetes cluster.</li><li>Permitted subjects - any</li><li>Permitted x509 extensions - honors subjectAltName and key usage extensions and discards other extensions.</li><li>Permitted key usages - any</li><li>Expiration/certificate lifetime - for the kube-controller-manager implementation of this signer, set to the minimum of the <code>--cluster-signing-duration</code> option or, if specified, the <code>spec.expirationSeconds</code> field of the CSR object.</li><li>CA bit allowed/disallowed - not allowed.</li></ol></li></ol><p>The kube-controller-manager implements <a href=#signer-control-plane>control plane signing</a> for each of the built in signers. Failures for all of these are only reported in kube-controller-manager logs.</p><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>The <code>spec.expirationSeconds</code> field was added in Kubernetes v1.22. Earlier versions of Kubernetes do not honor this field. Kubernetes API servers prior to v1.22 will silently drop this field when the object is created.</div><p>Distribution of trust happens out of band for these signers. Any trust outside of those described above are strictly coincidental. For instance, some distributions may honor <code>kubernetes.io/legacy-unknown</code> as client certificates for the kube-apiserver, but this is not a standard. None of these usages are related to ServiceAccount token secrets <code>.data[ca.crt]</code> in any way. That CA bundle is only guaranteed to verify a connection to the API server using the default service (<code>kubernetes.default.svc</code>).</p><h3 id=custom-signers>Custom signers</h3><p>You can also introduce your own custom signer, which should have a similar prefixed name but using your own domain name. For example, if you represent an open source project that uses the domain <code>open-fictional.example</code> then you might use <code>issuer.open-fictional.example/service-mesh</code> as a signer name.</p><p>A custom signer uses the Kubernetes API to issue a certificate. See <a href=#signer-api>API-based signers</a>.</p><h2 id=signing>Signing</h2><h3 id=signer-control-plane>Control plane signer</h3><p>The Kubernetes control plane implements each of the <a href=/docs/reference/access-authn-authz/certificate-signing-requests/#kubernetes-signers>Kubernetes signers</a>, as part of the kube-controller-manager.</p><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>Prior to Kubernetes v1.18, the kube-controller-manager would sign any CSRs that were marked as approved.</div><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>The <code>spec.expirationSeconds</code> field was added in Kubernetes v1.22. Earlier versions of Kubernetes do not honor this field. Kubernetes API servers prior to v1.22 will silently drop this field when the object is created.</div><h3 id=signer-api>API-based signers</h3><p>Users of the REST API can sign CSRs by submitting an UPDATE request to the <code>status</code> subresource of the CSR to be signed.</p><p>As part of this request, the <code>status.certificate</code> field should be set to contain the signed certificate. This field contains one or more PEM-encoded certificates.</p><p>All PEM blocks must have the "CERTIFICATE" label, contain no headers, and the encoded data must be a BER-encoded ASN.1 Certificate structure as described in <a href=https://tools.ietf.org/html/rfc5280#section-4.1>section 4 of RFC5280</a>.</p><p>Example certificate content:</p><pre tabindex=0><code>-----BEGIN CERTIFICATE----- MIIDgjCCAmqgAwIBAgIUC1N1EJ4Qnsd322BhDPRwmg3b/oAwDQYJKoZIhvcNAQEL BQAwXDELMAkGA1UEBhMCeHgxCjAIBgNVBAgMAXgxCjAIBgNVBAcMAXgxCjAIBgNV BAoMAXgxCjAIBgNVBAsMAXgxCzAJBgNVBAMMAmNhMRAwDgYJKoZIhvcNAQkBFgF4 MB4XDTIwMDcwNjIyMDcwMFoXDTI1MDcwNTIyMDcwMFowNzEVMBMGA1UEChMMc3lz dGVtOm5vZGVzMR4wHAYDVQQDExVzeXN0ZW06bm9kZToxMjcuMC4wLjEwggEiMA0G CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDne5X2eQ1JcLZkKvhzCR4Hxl9+ZmU3 +e1zfOywLdoQxrPi+o4hVsUH3q0y52BMa7u1yehHDRSaq9u62cmi5ekgXhXHzGmm kmW5n0itRECv3SFsSm2DSghRKf0mm6iTYHWDHzUXKdm9lPPWoSOxoR5oqOsm3JEh Q7Et13wrvTJqBMJo1GTwQuF+HYOku0NF/DLqbZIcpI08yQKyrBgYz2uO51/oNp8a sTCsV4OUfyHhx2BBLUo4g4SptHFySTBwlpRWBnSjZPOhmN74JcpTLB4J5f4iEeA7 2QytZfADckG4wVkhH3C2EJUmRtFIBVirwDn39GXkSGlnvnMgF3uLZ6zNAgMBAAGj YTBfMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDAjAMBgNVHRMB Af8EAjAAMB0GA1UdDgQWBBTREl2hW54lkQBDeVCcd2f2VSlB1DALBgNVHREEBDAC ggAwDQYJKoZIhvcNAQELBQADggEBABpZjuIKTq8pCaX8dMEGPWtAykgLsTcD2jYr L0/TCrqmuaaliUa42jQTt2OVsVP/L8ofFunj/KjpQU0bvKJPLMRKtmxbhXuQCQi1 qCRkp8o93mHvEz3mTUN+D1cfQ2fpsBENLnpS0F4G/JyY2Vrh19/X8+mImMEK5eOy o0BMby7byUj98WmcUvNCiXbC6F45QTmkwEhMqWns0JZQY+/XeDhEcg+lJvz9Eyo2 aGgPsye1o3DpyXnyfJWAWMhOz7cikS5X2adesbgI86PhEHBXPIJ1v13ZdfCExmdd M1fLPhLyR54fGaY+7/X8P9AZzPefAkwizeXwe9ii6/a08vWoiE4= -----END CERTIFICATE----- </code></pre><p>Non-PEM content may appear before or after the CERTIFICATE PEM blocks and is unvalidated, to allow for explanatory text as described in <a href=https://www.rfc-editor.org/rfc/rfc7468#section-5.2>section 5.2 of RFC7468</a>.</p><p>When encoded in JSON or YAML, this field is base-64 encoded. A CertificateSigningRequest containing the example certificate above would look like this:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>certificates.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>CertificateSigningRequest<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#00f;font-weight:700>...</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>status</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>certificate</span>:<span style=color:#bbb> </span><span style=color:#b44>"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JS..."</span><span style=color:#bbb> </span></span></span></code></pre></div><h2 id=approval-rejection>Approval or rejection</h2><p>Before a <a href=#signers>signer</a> issues a certificate based on a CertificateSigningRequest, the signer typically checks that the issuance for that CSR has been <em>approved</em>.</p><h3 id=approval-rejection-control-plane>Control plane automated approval</h3><p>The kube-controller-manager ships with a built-in approver for certificates with a signerName of <code>kubernetes.io/kube-apiserver-client-kubelet</code> that delegates various permissions on CSRs for node credentials to authorization. The kube-controller-manager POSTs SubjectAccessReview resources to the API server in order to check authorization for certificate approval.</p><h3 id=approval-rejection-kubectl>Approval or rejection using <code>kubectl</code></h3><p>A Kubernetes administrator (with appropriate permissions) can manually approve (or deny) CertificateSigningRequests by using the <code>kubectl certificate approve</code> and <code>kubectl certificate deny</code> commands.</p><p>To approve a CSR with kubectl:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl certificate approve <certificate-signing-request-name> </span></span></code></pre></div><p>Likewise, to deny a CSR:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl certificate deny <certificate-signing-request-name> </span></span></code></pre></div><h3 id=approval-rejection-api-client>Approval or rejection using the Kubernetes API</h3><p>Users of the REST API can approve CSRs by submitting an UPDATE request to the <code>approval</code> subresource of the CSR to be approved. For example, you could write an <a class=glossary-tooltip title='A specialized controller used to manage a custom resource' data-toggle=tooltip data-placement=top href=/docs/concepts/extend-kubernetes/operator/ target=_blank aria-label=operator>operator</a> that watches for a particular kind of CSR and then sends an UPDATE to approve them.</p><p>When you make an approval or rejection request, set either the <code>Approved</code> or <code>Denied</code> status condition based on the state you determine:</p><p>For <code>Approved</code> CSRs:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>certificates.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>CertificateSigningRequest<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#00f;font-weight:700>...</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>status</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>conditions</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>lastUpdateTime</span>:<span style=color:#bbb> </span><span style=color:#b44>"2020-02-08T11:37:35Z"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>lastTransitionTime</span>:<span style=color:#bbb> </span><span style=color:#b44>"2020-02-08T11:37:35Z"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>message</span>:<span style=color:#bbb> </span>Approved by my custom approver controller<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>reason</span>:<span style=color:#bbb> </span>ApprovedByMyPolicy<span style=color:#bbb> </span><span style=color:#080;font-style:italic># You can set this to any string</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>type</span>:<span style=color:#bbb> </span>Approved<span style=color:#bbb> </span></span></span></code></pre></div><p>For <code>Denied</code> CSRs:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>certificates.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>CertificateSigningRequest<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#00f;font-weight:700>...</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>status</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>conditions</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>lastUpdateTime</span>:<span style=color:#bbb> </span><span style=color:#b44>"2020-02-08T11:37:35Z"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>lastTransitionTime</span>:<span style=color:#bbb> </span><span style=color:#b44>"2020-02-08T11:37:35Z"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>message</span>:<span style=color:#bbb> </span>Denied by my custom approver controller<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>reason</span>:<span style=color:#bbb> </span>DeniedByMyPolicy<span style=color:#bbb> </span><span style=color:#080;font-style:italic># You can set this to any string</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>type</span>:<span style=color:#bbb> </span>Denied<span style=color:#bbb> </span></span></span></code></pre></div><p>It's usual to set <code>status.conditions.reason</code> to a machine-friendly reason code using TitleCase; this is a convention but you can set it to anything you like. If you want to add a note for human consumption, use the <code>status.conditions.message</code> field.</p><h2 id=cluster-trust-bundles>Cluster trust bundles</h2><div class="feature-state-notice feature-alpha"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.27 [alpha]</code></div><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>In Kubernetes 1.31, you must enable the <code>ClusterTrustBundle</code> <a href=/docs/reference/command-line-tools-reference/feature-gates/>feature gate</a> <em>and</em> the <code>certificates.k8s.io/v1alpha1</code> <a class=glossary-tooltip title='A set of related paths in the Kubernetes API.' data-toggle=tooltip data-placement=top href=/docs/concepts/overview/kubernetes-api/#api-groups-and-versioning target=_blank aria-label='API group'>API group</a> in order to use this API.</div><p>A ClusterTrustBundles is a cluster-scoped object for distributing X.509 trust anchors (root certificates) to workloads within the cluster. They're designed to work well with the <a href=#signers>signer</a> concept from CertificateSigningRequests.</p><p>ClusterTrustBundles can be used in two modes: <a href=#ctb-signer-linked>signer-linked</a> and <a href=#ctb-signer-unlinked>signer-unlinked</a>.</p><h3 id=ctb-common>Common properties and validation</h3><p>All ClusterTrustBundle objects have strong validation on the contents of their <code>trustBundle</code> field. That field must contain one or more X.509 certificates, DER-serialized, each wrapped in a PEM <code>CERTIFICATE</code> block. The certificates must parse as valid X.509 certificates.</p><p>Esoteric PEM features like inter-block data and intra-block headers are either rejected during object validation, or can be ignored by consumers of the object. Additionally, consumers are allowed to reorder the certificates in the bundle with their own arbitrary but stable ordering.</p><p>ClusterTrustBundle objects should be considered world-readable within the cluster. If your cluster uses <a href=/docs/reference/access-authn-authz/rbac/>RBAC</a> authorization, all ServiceAccounts have a default grant that allows them to <strong>get</strong>, <strong>list</strong>, and <strong>watch</strong> all ClusterTrustBundle objects. If you use your own authorization mechanism and you have enabled ClusterTrustBundles in your cluster, you should set up an equivalent rule to make these objects public within the cluster, so that they work as intended.</p><p>If you do not have permission to list cluster trust bundles by default in your cluster, you can impersonate a service account you have access to in order to see available ClusterTrustBundles:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>kubectl get clustertrustbundles --as<span style=color:#666>=</span><span style=color:#b44>'system:serviceaccount:mynamespace:default'</span> </span></span></code></pre></div><h3 id=ctb-signer-linked>Signer-linked ClusterTrustBundles</h3><p>Signer-linked ClusterTrustBundles are associated with a <em>signer name</em>, like this:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>certificates.k8s.io/v1alpha1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ClusterTrustBundle<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>example.com:mysigner:foo<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>spec</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>signerName</span>:<span style=color:#bbb> </span>example.com/mysigner<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>trustBundle</span>:<span style=color:#bbb> </span><span style=color:#b44>"<... PEM data ...>"</span><span style=color:#bbb> </span></span></span></code></pre></div><p>These ClusterTrustBundles are intended to be maintained by a signer-specific controller in the cluster, so they have several security features:</p><ul><li>To create or update a signer-linked ClusterTrustBundle, you must be permitted to <strong>attest</strong> on the signer (custom authorization verb <code>attest</code>, API group <code>certificates.k8s.io</code>; resource path <code>signers</code>). You can configure authorization for the specific resource name <code><signerNameDomain>/<signerNamePath></code> or match a pattern such as <code><signerNameDomain>/*</code>.</li><li>Signer-linked ClusterTrustBundles <strong>must</strong> be named with a prefix derived from their <code>spec.signerName</code> field. Slashes (<code>/</code>) are replaced with colons (<code>:</code>), and a final colon is appended. This is followed by an arbitrary name. For example, the signer <code>example.com/mysigner</code> can be linked to a ClusterTrustBundle <code>example.com:mysigner:<arbitrary-name></code>.</li></ul><p>Signer-linked ClusterTrustBundles will typically be consumed in workloads by a combination of a <a href=/docs/concepts/overview/working-with-objects/field-selectors/>field selector</a> on the signer name, and a separate <a href=/docs/concepts/overview/working-with-objects/labels/#label-selectors>label selector</a>.</p><h3 id=ctb-signer-unlinked>Signer-unlinked ClusterTrustBundles</h3><p>Signer-unlinked ClusterTrustBundles have an empty <code>spec.signerName</code> field, like this:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>certificates.k8s.io/v1alpha1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ClusterTrustBundle<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>foo<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>spec</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># no signerName specified, so the field is blank</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>trustBundle</span>:<span style=color:#bbb> </span><span style=color:#b44>"<... PEM data ...>"</span><span style=color:#bbb> </span></span></span></code></pre></div><p>They are primarily intended for cluster configuration use cases. Each signer-unlinked ClusterTrustBundle is an independent object, in contrast to the customary grouping behavior of signer-linked ClusterTrustBundles.</p><p>Signer-unlinked ClusterTrustBundles have no <code>attest</code> verb requirement. Instead, you control access to them directly using the usual mechanisms, such as role-based access control.</p><p>To distinguish them from signer-linked ClusterTrustBundles, the names of signer-unlinked ClusterTrustBundles <strong>must not</strong> contain a colon (<code>:</code>).</p><h3 id=ctb-projection>Accessing ClusterTrustBundles from pods</h3><div class="feature-state-notice feature-alpha"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.29 [alpha]</code></div><p>The contents of ClusterTrustBundles can be injected into the container filesystem, similar to ConfigMaps and Secrets. See the <a href=/docs/concepts/storage/projected-volumes/#clustertrustbundle>clusterTrustBundle projected volume source</a> for more details.</p><h2 id=normal-user>How to issue a certificate for a user</h2><p>A few steps are required in order to get a normal user to be able to authenticate and invoke an API. First, this user must have a certificate issued by the Kubernetes cluster, and then present that certificate to the Kubernetes API.</p><h3 id=create-private-key>Create private key</h3><p>The following scripts show how to generate PKI private key and CSR. It is important to set CN and O attribute of the CSR. CN is the name of the user and O is the group that this user will belong to. You can refer to <a href=/docs/reference/access-authn-authz/rbac/>RBAC</a> for standard groups.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>openssl genrsa -out myuser.key <span style=color:#666>2048</span> </span></span><span style=display:flex><span>openssl req -new -key myuser.key -out myuser.csr -subj <span style=color:#b44>"/CN=myuser"</span> </span></span></code></pre></div><h3 id=create-certificatessigningrequest>Create a CertificateSigningRequest</h3><p>Create a <a href=/docs/reference/kubernetes-api/authentication-resources/certificate-signing-request-v1/>CertificateSigningRequest</a> and submit it to a Kubernetes Cluster via kubectl. Below is a script to generate the CertificateSigningRequest.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>cat <span style=color:#b44><<EOF | kubectl apply -f - </span></span></span><span style=display:flex><span><span style=color:#b44>apiVersion: certificates.k8s.io/v1 </span></span></span><span style=display:flex><span><span style=color:#b44>kind: CertificateSigningRequest </span></span></span><span style=display:flex><span><span style=color:#b44>metadata: </span></span></span><span style=display:flex><span><span style=color:#b44> name: myuser </span></span></span><span style=display:flex><span><span style=color:#b44>spec: </span></span></span><span style=display:flex><span><span style=color:#b44> request: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ1ZqQ0NBVDRDQVFBd0VURVBNQTBHQTFVRUF3d0dZVzVuWld4aE1JSUJJakFOQmdrcWhraUc5dzBCQVFFRgpBQU9DQVE4QU1JSUJDZ0tDQVFFQTByczhJTHRHdTYxakx2dHhWTTJSVlRWMDNHWlJTWWw0dWluVWo4RElaWjBOCnR2MUZtRVFSd3VoaUZsOFEzcWl0Qm0wMUFSMkNJVXBGd2ZzSjZ4MXF3ckJzVkhZbGlBNVhwRVpZM3ExcGswSDQKM3Z3aGJlK1o2MVNrVHF5SVBYUUwrTWM5T1Nsbm0xb0R2N0NtSkZNMUlMRVI3QTVGZnZKOEdFRjJ6dHBoaUlFMwpub1dtdHNZb3JuT2wzc2lHQ2ZGZzR4Zmd4eW8ybmlneFNVekl1bXNnVm9PM2ttT0x1RVF6cXpkakJ3TFJXbWlECklmMXBMWnoyalVnald4UkhCM1gyWnVVV1d1T09PZnpXM01LaE8ybHEvZi9DdS8wYk83c0x0MCt3U2ZMSU91TFcKcW90blZtRmxMMytqTy82WDNDKzBERHk5aUtwbXJjVDBnWGZLemE1dHJRSURBUUFCb0FBd0RRWUpLb1pJaHZjTgpBUUVMQlFBRGdnRUJBR05WdmVIOGR4ZzNvK21VeVRkbmFjVmQ1N24zSkExdnZEU1JWREkyQTZ1eXN3ZFp1L1BVCkkwZXpZWFV0RVNnSk1IRmQycVVNMjNuNVJsSXJ3R0xuUXFISUh5VStWWHhsdnZsRnpNOVpEWllSTmU3QlJvYXgKQVlEdUI5STZXT3FYbkFvczFqRmxNUG5NbFpqdU5kSGxpT1BjTU1oNndLaTZzZFhpVStHYTJ2RUVLY01jSVUyRgpvU2djUWdMYTk0aEpacGk3ZnNMdm1OQUxoT045UHdNMGM1dVJVejV4T0dGMUtCbWRSeEgvbUNOS2JKYjFRQm1HCkkwYitEUEdaTktXTU0xMzhIQXdoV0tkNjVoVHdYOWl4V3ZHMkh4TG1WQzg0L1BHT0tWQW9FNkpsYWFHdTlQVmkKdjlOSjVaZlZrcXdCd0hKbzZXdk9xVlA3SVFjZmg3d0drWm89Ci0tLS0tRU5EIENFUlRJRklDQVRFIFJFUVVFU1QtLS0tLQo= </span></span></span><span style=display:flex><span><span style=color:#b44> signerName: kubernetes.io/kube-apiserver-client </span></span></span><span style=display:flex><span><span style=color:#b44> expirationSeconds: 86400 # one day </span></span></span><span style=display:flex><span><span style=color:#b44> usages: </span></span></span><span style=display:flex><span><span style=color:#b44> - client auth </span></span></span><span style=display:flex><span><span style=color:#b44>EOF</span> </span></span></code></pre></div><p>Some points to note:</p><ul><li><p><code>usages</code> has to be '<code>client auth</code>'</p></li><li><p><code>expirationSeconds</code> could be made longer (i.e. <code>864000</code> for ten days) or shorter (i.e. <code>3600</code> for one hour)</p></li><li><p><code>request</code> is the base64 encoded value of the CSR file content. You can get the content using this command:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>cat myuser.csr | base64 | tr -d <span style=color:#b44>"\n"</span> </span></span></code></pre></div></li></ul><h3 id=approve-certificate-signing-request>Approve the CertificateSigningRequest</h3><p>Use kubectl to create a CSR and approve it.</p><p>Get the list of CSRs:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl get csr </span></span></code></pre></div><p>Approve the CSR:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl certificate approve myuser </span></span></code></pre></div><h3 id=get-the-certificate>Get the certificate</h3><p>Retrieve the certificate from the CSR:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl get csr/myuser -o yaml </span></span></code></pre></div><p>The certificate value is in Base64-encoded format under <code>status.certificate</code>.</p><p>Export the issued certificate from the CertificateSigningRequest.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl get csr myuser -o <span style=color:#b8860b>jsonpath</span><span style=color:#666>=</span><span style=color:#b44>'{.status.certificate}'</span>| base64 -d > myuser.crt </span></span></code></pre></div><h3 id=create-role-and-rolebinding>Create Role and RoleBinding</h3><p>With the certificate created it is time to define the Role and RoleBinding for this user to access Kubernetes cluster resources.</p><p>This is a sample command to create a Role for this new user:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create role developer --verb<span style=color:#666>=</span>create --verb<span style=color:#666>=</span>get --verb<span style=color:#666>=</span>list --verb<span style=color:#666>=</span>update --verb<span style=color:#666>=</span>delete --resource<span style=color:#666>=</span>pods </span></span></code></pre></div><p>This is a sample command to create a RoleBinding for this new user:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create rolebinding developer-binding-myuser --role<span style=color:#666>=</span>developer --user<span style=color:#666>=</span>myuser </span></span></code></pre></div><h3 id=add-to-kubeconfig>Add to kubeconfig</h3><p>The last step is to add this user into the kubeconfig file.</p><p>First, you need to add new credentials:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl config set-credentials myuser --client-key<span style=color:#666>=</span>myuser.key --client-certificate<span style=color:#666>=</span>myuser.crt --embed-certs<span style=color:#666>=</span><span style=color:#a2f>true</span> </span></span></code></pre></div><p>Then, you need to add the context:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl config set-context myuser --cluster<span style=color:#666>=</span>kubernetes --user<span style=color:#666>=</span>myuser </span></span></code></pre></div><p>To test it, change the context to <code>myuser</code>:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl config use-context myuser </span></span></code></pre></div><h2 id=what-s-next>What's next</h2><ul><li>Read <a href=/docs/tasks/tls/managing-tls-in-a-cluster/>Manage TLS Certificates in a Cluster</a></li><li>View the source code for the kube-controller-manager built in <a href=https://github.com/kubernetes/kubernetes/blob/32ec6c212ec9415f604ffc1f4c1f29b782968ff1/pkg/controller/certificates/signer/cfssl_signer.go>signer</a></li><li>View the source code for the kube-controller-manager built in <a href=https://github.com/kubernetes/kubernetes/blob/32ec6c212ec9415f604ffc1f4c1f29b782968ff1/pkg/controller/certificates/approver/sarapprove.go>approver</a></li><li>For details of X.509 itself, refer to <a href=https://tools.ietf.org/html/rfc5280#section-3.1>RFC 5280</a> section 3.1</li><li>For information on the syntax of PKCS#10 certificate signing requests, refer to <a href=https://tools.ietf.org/html/rfc2986>RFC 2986</a></li><li>Read about the ClusterTrustBundle API:<ul><li><a class=api-reference-page-link href=https://kubernetes.io/docs/reference/kubernetes-api/authentication-resources/cluster-trust-bundle-v1alpha1/>ClusterTrustBundle API reference</a></li></ul></li></ul></div><div class=td-content style=page-break-before:always><h1 id=pg-643e4cec52a8577e9454649bdaac84d0>12 - Mapping PodSecurityPolicies to Pod Security Standards</h1><p>The tables below enumerate the configuration parameters on <code>PodSecurityPolicy</code> objects, whether the field mutates and/or validates pods, and how the configuration values map to the <a href=/docs/concepts/security/pod-security-standards/>Pod Security Standards</a>.</p><p>For each applicable parameter, the allowed values for the <a href=/docs/concepts/security/pod-security-standards/#baseline>Baseline</a> and <a href=/docs/concepts/security/pod-security-standards/#restricted>Restricted</a> profiles are listed. Anything outside the allowed values for those profiles would fall under the <a href=/docs/concepts/security/pod-security-standards/#privileged>Privileged</a> profile. "No opinion" means all values are allowed under all Pod Security Standards.</p><p>For a step-by-step migration guide, see <a href=/docs/tasks/configure-pod-container/migrate-from-psp/>Migrate from PodSecurityPolicy to the Built-In PodSecurity Admission Controller</a>.</p><h2 id=podsecuritypolicy-spec>PodSecurityPolicy Spec</h2><p>The fields enumerated in this table are part of the <code>PodSecurityPolicySpec</code>, which is specified under the <code>.spec</code> field path.</p><table class=no-word-break><caption style=display:none>Mapping PodSecurityPolicySpec fields to Pod Security Standards</caption><tbody><tr><th><code>PodSecurityPolicySpec</code></th><th>Type</th><th>Pod Security Standards Equivalent</th></tr><tr><td><code>privileged</code></td><td>Validating</td><td><b>Baseline & Restricted</b>: <code>false</code> / undefined / nil</td></tr><tr><td><code>defaultAddCapabilities</code></td><td>Mutating & Validating</td><td>Requirements match <code>allowedCapabilities</code> below.</td></tr><tr><td><code>allowedCapabilities</code></td><td>Validating</td><td><p><b>Baseline</b>: subset of</p><ul><li><code>AUDIT_WRITE</code></li><li><code>CHOWN</code></li><li><code>DAC_OVERRIDE</code></li><li><code>FOWNER</code></li><li><code>FSETID</code></li><li><code>KILL</code></li><li><code>MKNOD</code></li><li><code>NET_BIND_SERVICE</code></li><li><code>SETFCAP</code></li><li><code>SETGID</code></li><li><code>SETPCAP</code></li><li><code>SETUID</code></li><li><code>SYS_CHROOT</code></li></ul><p><b>Restricted</b>: empty / undefined / nil OR a list containing <i>only</i> <code>NET_BIND_SERVICE</code></td></tr><tr><td><code>requiredDropCapabilities</code></td><td>Mutating & Validating</td><td><p><b>Baseline</b>: no opinion</p><p><b>Restricted</b>: must include <code>ALL</code></p></td></tr><tr><td><code>volumes</code></td><td>Validating</td><td><p><b>Baseline</b>: anything except</p><ul><li><code>hostPath</code></li><li><code>*</code></li></ul><p><b>Restricted</b>: subset of</p><ul><li><code>configMap</code></li><li><code>csi</code></li><li><code>downwardAPI</code></li><li><code>emptyDir</code></li><li><code>ephemeral</code></li><li><code>persistentVolumeClaim</code></li><li><code>projected</code></li><li><code>secret</code></li></ul></td></tr><tr><td><code>hostNetwork</code></td><td>Validating</td><td><b>Baseline & Restricted</b>: <code>false</code> / undefined / nil</td></tr><tr><td><code>hostPorts</code></td><td>Validating</td><td><b>Baseline & Restricted</b>: undefined / nil / empty</td></tr><tr><td><code>hostPID</code></td><td>Validating</td><td><b>Baseline & Restricted</b>: <code>false</code> / undefined / nil</td></tr><tr><td><code>hostIPC</code></td><td>Validating</td><td><b>Baseline & Restricted</b>: <code>false</code> / undefined / nil</td></tr><tr><td><code>seLinux</code></td><td>Mutating & Validating</td><td><p><b>Baseline & Restricted</b>: <code>seLinux.rule</code> is <code>MustRunAs</code>, with the following <code>options</code></p><ul><li><code>user</code> is unset (<code>""</code> / undefined / nil)</li><li><code>role</code> is unset (<code>""</code> / undefined / nil)</li><li><code>type</code> is unset or one of: <code>container_t, container_init_t, container_kvm_t, container_engine_t</code></li><li><code>level</code> is anything</li></ul></td></tr><tr><td><code>runAsUser</code></td><td>Mutating & Validating</td><td><p><b>Baseline</b>: Anything</p><p><b>Restricted</b>: <code>rule</code> is <code>MustRunAsNonRoot</code></p></td></tr><tr><td><code>runAsGroup</code></td><td>Mutating (MustRunAs) & Validating</td><td><i>No opinion</i></td></tr><tr><td><code>supplementalGroups</code></td><td>Mutating & Validating</td><td><i>No opinion</i></td></tr><tr><td><code>fsGroup</code></td><td>Mutating & Validating</td><td><i>No opinion</i></td></tr><tr><td><code>readOnlyRootFilesystem</code></td><td>Mutating & Validating</td><td><i>No opinion</i></td></tr><tr><td><code>defaultAllowPrivilegeEscalation</code></td><td>Mutating</td><td><i>No opinion (non-validating)</i></td></tr><tr><td><code>allowPrivilegeEscalation</code></td><td>Mutating & Validating</td><td><p><i>Only mutating if set to <code>false</code></i></p><p><b>Baseline</b>: No opinion</p><p><b>Restricted</b>: <code>false</code></p></td></tr><tr><td><code>allowedHostPaths</code></td><td>Validating</td><td><i>No opinion (volumes takes precedence)</i></td></tr><tr><td><code>allowedFlexVolumes</code></td><td>Validating</td><td><i>No opinion (volumes takes precedence)</i></td></tr><tr><td><code>allowedCSIDrivers</code></td><td>Validating</td><td><i>No opinion (volumes takes precedence)</i></td></tr><tr><td><code>allowedUnsafeSysctls</code></td><td>Validating</td><td><b>Baseline & Restricted</b>: undefined / nil / empty</td></tr><tr><td><code>forbiddenSysctls</code></td><td>Validating</td><td><i>No opinion</i></td></tr><tr><td><code>allowedProcMountTypes</code><br><i>(alpha feature)</i></td><td>Validating</td><td><b>Baseline & Restricted</b>: <code>["Default"]</code> OR undefined / nil / empty</td></tr><tr><td><code>runtimeClass</code><br><code> .defaultRuntimeClassName</code></td><td>Mutating</td><td><i>No opinion</i></td></tr><tr><td><code>runtimeClass</code><br><code> .allowedRuntimeClassNames</code></td><td>Validating</td><td><i>No opinion</i></td></tr></tbody></table><h2 id=podsecuritypolicy-annotations>PodSecurityPolicy annotations</h2><p>The <a href=/docs/concepts/overview/working-with-objects/annotations/>annotations</a> enumerated in this table can be specified under <code>.metadata.annotations</code> on the PodSecurityPolicy object.</p><table class=no-word-break><caption style=display:none>Mapping PodSecurityPolicy annotations to Pod Security Standards</caption><tbody><tr><th><code>PSP Annotation</code></th><th>Type</th><th>Pod Security Standards Equivalent</th></tr><tr><td><code>seccomp.security.alpha.kubernetes.io</code><br><code>/defaultProfileName</code></td><td>Mutating</td><td><i>No opinion</i></td></tr><tr><td><code>seccomp.security.alpha.kubernetes.io</code><br><code>/allowedProfileNames</code></td><td>Validating</td><td><p><b>Baseline</b>: <code>"runtime/default,"</code> <i>(Trailing comma to allow unset)</i></p><p><b>Restricted</b>: <code>"runtime/default"</code> <i>(No trailing comma)</i></p><p><i><code>localhost/*</code> values are also permitted for both Baseline & Restricted.</i></p></td></tr><tr><td><code>apparmor.security.beta.kubernetes.io</code><br><code>/defaultProfileName</code></td><td>Mutating</td><td><i>No opinion</i></td></tr><tr><td><code>apparmor.security.beta.kubernetes.io</code><br><code>/allowedProfileNames</code></td><td>Validating</td><td><p><b>Baseline</b>: <code>"runtime/default,"</code> <i>(Trailing comma to allow unset)</i></p><p><b>Restricted</b>: <code>"runtime/default"</code> <i>(No trailing comma)</i></p><p><i><code>localhost/*</code> values are also permitted for both Baseline & Restricted.</i></p></td></tr></tbody></table></div><div class=td-content style=page-break-before:always><h1 id=pg-36e1423f0b5caa8eafeb6f53c175d13c>13 - Kubelet authentication/authorization</h1><h2 id=overview>Overview</h2><p>A kubelet's HTTPS endpoint exposes APIs which give access to data of varying sensitivity, and allow you to perform operations with varying levels of power on the node and within containers.</p><p>This document describes how to authenticate and authorize access to the kubelet's HTTPS endpoint.</p><h2 id=kubelet-authentication>Kubelet authentication</h2><p>By default, requests to the kubelet's HTTPS endpoint that are not rejected by other configured authentication methods are treated as anonymous requests, and given a username of <code>system:anonymous</code> and a group of <code>system:unauthenticated</code>.</p><p>To disable anonymous access and send <code>401 Unauthorized</code> responses to unauthenticated requests:</p><ul><li>start the kubelet with the <code>--anonymous-auth=false</code> flag</li></ul><p>To enable X509 client certificate authentication to the kubelet's HTTPS endpoint:</p><ul><li>start the kubelet with the <code>--client-ca-file</code> flag, providing a CA bundle to verify client certificates with</li><li>start the apiserver with <code>--kubelet-client-certificate</code> and <code>--kubelet-client-key</code> flags</li><li>see the <a href=/docs/reference/access-authn-authz/authentication/#x509-client-certificates>apiserver authentication documentation</a> for more details</li></ul><p>To enable API bearer tokens (including service account tokens) to be used to authenticate to the kubelet's HTTPS endpoint:</p><ul><li>ensure the <code>authentication.k8s.io/v1beta1</code> API group is enabled in the API server</li><li>start the kubelet with the <code>--authentication-token-webhook</code> and <code>--kubeconfig</code> flags</li><li>the kubelet calls the <code>TokenReview</code> API on the configured API server to determine user information from bearer tokens</li></ul><h2 id=kubelet-authorization>Kubelet authorization</h2><p>Any request that is successfully authenticated (including an anonymous request) is then authorized. The default authorization mode is <code>AlwaysAllow</code>, which allows all requests.</p><p>There are many possible reasons to subdivide access to the kubelet API:</p><ul><li>anonymous auth is enabled, but anonymous users' ability to call the kubelet API should be limited</li><li>bearer token auth is enabled, but arbitrary API users' (like service accounts) ability to call the kubelet API should be limited</li><li>client certificate auth is enabled, but only some of the client certificates signed by the configured CA should be allowed to use the kubelet API</li></ul><p>To subdivide access to the kubelet API, delegate authorization to the API server:</p><ul><li>ensure the <code>authorization.k8s.io/v1beta1</code> API group is enabled in the API server</li><li>start the kubelet with the <code>--authorization-mode=Webhook</code> and the <code>--kubeconfig</code> flags</li><li>the kubelet calls the <code>SubjectAccessReview</code> API on the configured API server to determine whether each request is authorized</li></ul><p>The kubelet authorizes API requests using the same <a href=/docs/reference/access-authn-authz/authorization/#review-your-request-attributes>request attributes</a> approach as the apiserver.</p><p>The verb is determined from the incoming request's HTTP verb:</p><table><thead><tr><th>HTTP verb</th><th>request verb</th></tr></thead><tbody><tr><td>POST</td><td>create</td></tr><tr><td>GET, HEAD</td><td>get</td></tr><tr><td>PUT</td><td>update</td></tr><tr><td>PATCH</td><td>patch</td></tr><tr><td>DELETE</td><td>delete</td></tr></tbody></table><p>The resource and subresource is determined from the incoming request's path:</p><table><thead><tr><th>Kubelet API</th><th>resource</th><th>subresource</th></tr></thead><tbody><tr><td>/stats/*</td><td>nodes</td><td>stats</td></tr><tr><td>/metrics/*</td><td>nodes</td><td>metrics</td></tr><tr><td>/logs/*</td><td>nodes</td><td>log</td></tr><tr><td>/spec/*</td><td>nodes</td><td>spec</td></tr><tr><td>/checkpoint/*</td><td>nodes</td><td>checkpoint</td></tr><tr><td><em>all others</em></td><td>nodes</td><td>proxy</td></tr></tbody></table><p>The namespace and API group attributes are always an empty string, and the resource name is always the name of the kubelet's <code>Node</code> API object.</p><p>When running in this mode, ensure the user identified by the <code>--kubelet-client-certificate</code> and <code>--kubelet-client-key</code> flags passed to the apiserver is authorized for the following attributes:</p><ul><li>verb=*, resource=nodes, subresource=proxy</li><li>verb=*, resource=nodes, subresource=stats</li><li>verb=*, resource=nodes, subresource=log</li><li>verb=*, resource=nodes, subresource=spec</li><li>verb=*, resource=nodes, subresource=metrics</li></ul></div><div class=td-content style=page-break-before:always><h1 id=pg-d17c42b1760f6d5c333fc91ca9b453f4>14 - TLS bootstrapping</h1><p>In a Kubernetes cluster, the components on the worker nodes - kubelet and kube-proxy - need to communicate with Kubernetes control plane components, specifically kube-apiserver. In order to ensure that communication is kept private, not interfered with, and ensure that each component of the cluster is talking to another trusted component, we strongly recommend using client TLS certificates on nodes.</p><p>The normal process of bootstrapping these components, especially worker nodes that need certificates so they can communicate safely with kube-apiserver, can be a challenging process as it is often outside of the scope of Kubernetes and requires significant additional work. This in turn, can make it challenging to initialize or scale a cluster.</p><p>In order to simplify the process, beginning in version 1.4, Kubernetes introduced a certificate request and signing API. The proposal can be found <a href=https://github.com/kubernetes/kubernetes/pull/20439>here</a>.</p><p>This document describes the process of node initialization, how to set up TLS client certificate bootstrapping for kubelets, and how it works.</p><h2 id=initialization-process>Initialization process</h2><p>When a worker node starts up, the kubelet does the following:</p><ol><li>Look for its <code>kubeconfig</code> file</li><li>Retrieve the URL of the API server and credentials, normally a TLS key and signed certificate from the <code>kubeconfig</code> file</li><li>Attempt to communicate with the API server using the credentials.</li></ol><p>Assuming that the kube-apiserver successfully validates the kubelet's credentials, it will treat the kubelet as a valid node, and begin to assign pods to it.</p><p>Note that the above process depends upon:</p><ul><li>Existence of a key and certificate on the local host in the <code>kubeconfig</code></li><li>The certificate having been signed by a Certificate Authority (CA) trusted by the kube-apiserver</li></ul><p>All of the following are responsibilities of whoever sets up and manages the cluster:</p><ol><li>Creating the CA key and certificate</li><li>Distributing the CA certificate to the control plane nodes, where kube-apiserver is running</li><li>Creating a key and certificate for each kubelet; strongly recommended to have a unique one, with a unique CN, for each kubelet</li><li>Signing the kubelet certificate using the CA key</li><li>Distributing the kubelet key and signed certificate to the specific node on which the kubelet is running</li></ol><p>The TLS Bootstrapping described in this document is intended to simplify, and partially or even completely automate, steps 3 onwards, as these are the most common when initializing or scaling a cluster.</p><h3 id=bootstrap-initialization>Bootstrap initialization</h3><p>In the bootstrap initialization process, the following occurs:</p><ol><li>kubelet begins</li><li>kubelet sees that it does <em>not</em> have a <code>kubeconfig</code> file</li><li>kubelet searches for and finds a <code>bootstrap-kubeconfig</code> file</li><li>kubelet reads its bootstrap file, retrieving the URL of the API server and a limited usage "token"</li><li>kubelet connects to the API server, authenticates using the token</li><li>kubelet now has limited credentials to create and retrieve a certificate signing request (CSR)</li><li>kubelet creates a CSR for itself with the signerName set to <code>kubernetes.io/kube-apiserver-client-kubelet</code></li><li>CSR is approved in one of two ways:<ul><li>If configured, kube-controller-manager automatically approves the CSR</li><li>If configured, an outside process, possibly a person, approves the CSR using the Kubernetes API or via <code>kubectl</code></li></ul></li><li>Certificate is created for the kubelet</li><li>Certificate is issued to the kubelet</li><li>kubelet retrieves the certificate</li><li>kubelet creates a proper <code>kubeconfig</code> with the key and signed certificate</li><li>kubelet begins normal operation</li><li>Optional: if configured, kubelet automatically requests renewal of the certificate when it is close to expiry</li><li>The renewed certificate is approved and issued, either automatically or manually, depending on configuration.</li></ol><p>The rest of this document describes the necessary steps to configure TLS Bootstrapping, and its limitations.</p><h2 id=configuration>Configuration</h2><p>To configure for TLS bootstrapping and optional automatic approval, you must configure options on the following components:</p><ul><li>kube-apiserver</li><li>kube-controller-manager</li><li>kubelet</li><li>in-cluster resources: <code>ClusterRoleBinding</code> and potentially <code>ClusterRole</code></li></ul><p>In addition, you need your Kubernetes Certificate Authority (CA).</p><h2 id=certificate-authority>Certificate Authority</h2><p>As without bootstrapping, you will need a Certificate Authority (CA) key and certificate. As without bootstrapping, these will be used to sign the kubelet certificate. As before, it is your responsibility to distribute them to control plane nodes.</p><p>For the purposes of this document, we will assume these have been distributed to control plane nodes at <code>/var/lib/kubernetes/ca.pem</code> (certificate) and <code>/var/lib/kubernetes/ca-key.pem</code> (key). We will refer to these as "Kubernetes CA certificate and key".</p><p>All Kubernetes components that use these certificates - kubelet, kube-apiserver, kube-controller-manager - assume the key and certificate to be PEM-encoded.</p><h2 id=kube-apiserver-configuration>kube-apiserver configuration</h2><p>The kube-apiserver has several requirements to enable TLS bootstrapping:</p><ul><li>Recognizing CA that signs the client certificate</li><li>Authenticating the bootstrapping kubelet to the <code>system:bootstrappers</code> group</li><li>Authorize the bootstrapping kubelet to create a certificate signing request (CSR)</li></ul><h3 id=recognizing-client-certificates>Recognizing client certificates</h3><p>This is normal for all client certificate authentication. If not already set, add the <code>--client-ca-file=FILENAME</code> flag to the kube-apiserver command to enable client certificate authentication, referencing a certificate authority bundle containing the signing certificate, for example <code>--client-ca-file=/var/lib/kubernetes/ca.pem</code>.</p><h3 id=initial-bootstrap-authentication>Initial bootstrap authentication</h3><p>In order for the bootstrapping kubelet to connect to kube-apiserver and request a certificate, it must first authenticate to the server. You can use any <a href=/docs/reference/access-authn-authz/authentication/>authenticator</a> that can authenticate the kubelet.</p><p>While any authentication strategy can be used for the kubelet's initial bootstrap credentials, the following two authenticators are recommended for ease of provisioning.</p><ol><li><a href=#bootstrap-tokens>Bootstrap Tokens</a></li><li><a href=#token-authentication-file>Token authentication file</a></li></ol><p>Using bootstrap tokens is a simpler and more easily managed method to authenticate kubelets, and does not require any additional flags when starting kube-apiserver.</p><p>Whichever method you choose, the requirement is that the kubelet be able to authenticate as a user with the rights to:</p><ol><li>create and retrieve CSRs</li><li>be automatically approved to request node client certificates, if automatic approval is enabled.</li></ol><p>A kubelet authenticating using bootstrap tokens is authenticated as a user in the group <code>system:bootstrappers</code>, which is the standard method to use.</p><p>As this feature matures, you should ensure tokens are bound to a Role Based Access Control (RBAC) policy which limits requests (using the <a href=/docs/reference/access-authn-authz/bootstrap-tokens/>bootstrap token</a>) strictly to client requests related to certificate provisioning. With RBAC in place, scoping the tokens to a group allows for great flexibility. For example, you could disable a particular bootstrap group's access when you are done provisioning the nodes.</p><h4 id=bootstrap-tokens>Bootstrap tokens</h4><p>Bootstrap tokens are described in detail <a href=/docs/reference/access-authn-authz/bootstrap-tokens/>here</a>. These are tokens that are stored as secrets in the Kubernetes cluster, and then issued to the individual kubelet. You can use a single token for an entire cluster, or issue one per worker node.</p><p>The process is two-fold:</p><ol><li>Create a Kubernetes secret with the token ID, secret and scope(s).</li><li>Issue the token to the kubelet</li></ol><p>From the kubelet's perspective, one token is like another and has no special meaning. From the kube-apiserver's perspective, however, the bootstrap token is special. Due to its <code>type</code>, <code>namespace</code> and <code>name</code>, kube-apiserver recognizes it as a special token, and grants anyone authenticating with that token special bootstrap rights, notably treating them as a member of the <code>system:bootstrappers</code> group. This fulfills a basic requirement for TLS bootstrapping.</p><p>The details for creating the secret are available <a href=/docs/reference/access-authn-authz/bootstrap-tokens/>here</a>.</p><p>If you want to use bootstrap tokens, you must enable it on kube-apiserver with the flag:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-console data-lang=console><span style=display:flex><span><span style=color:#888>--enable-bootstrap-token-auth=true </span></span></span></code></pre></div><h4 id=token-authentication-file>Token authentication file</h4><p>kube-apiserver has the ability to accept tokens as authentication. These tokens are arbitrary but should represent at least 128 bits of entropy derived from a secure random number generator (such as <code>/dev/urandom</code> on most modern Linux systems). There are multiple ways you can generate a token. For example:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>head -c <span style=color:#666>16</span> /dev/urandom | od -An -t x | tr -d <span style=color:#b44>' '</span> </span></span></code></pre></div><p>This will generate tokens that look like <code>02b50b05283e98dd0fd71db496ef01e8</code>.</p><p>The token file should look like the following example, where the first three values can be anything and the quoted group name should be as depicted:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-console data-lang=console><span style=display:flex><span><span style=color:#888>02b50b05283e98dd0fd71db496ef01e8,kubelet-bootstrap,10001,"system:bootstrappers" </span></span></span></code></pre></div><p>Add the <code>--token-auth-file=FILENAME</code> flag to the kube-apiserver command (in your systemd unit file perhaps) to enable the token file. See docs <a href=/docs/reference/access-authn-authz/authentication/#static-token-file>here</a> for further details.</p><h3 id=authorize-kubelet-to-create-csr>Authorize kubelet to create CSR</h3><p>Now that the bootstrapping node is <em>authenticated</em> as part of the <code>system:bootstrappers</code> group, it needs to be <em>authorized</em> to create a certificate signing request (CSR) as well as retrieve it when done. Fortunately, Kubernetes ships with a <code>ClusterRole</code> with precisely these (and only these) permissions, <code>system:node-bootstrapper</code>.</p><p>To do this, you only need to create a <code>ClusterRoleBinding</code> that binds the <code>system:bootstrappers</code> group to the cluster role <code>system:node-bootstrapper</code>.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:#080;font-style:italic># enable bootstrapping nodes to create CSR</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ClusterRoleBinding<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>create-csrs-for-bootstrapping<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>subjects</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Group<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>system:bootstrappers<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroup</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>roleRef</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ClusterRole<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>system:node-bootstrapper<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroup</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io<span style=color:#bbb> </span></span></span></code></pre></div><h2 id=kube-controller-manager-configuration>kube-controller-manager configuration</h2><p>While the apiserver receives the requests for certificates from the kubelet and authenticates those requests, the controller-manager is responsible for issuing actual signed certificates.</p><p>The controller-manager performs this function via a certificate-issuing control loop. This takes the form of a <a href=https://blog.cloudflare.com/introducing-cfssl/>cfssl</a> local signer using assets on disk. Currently, all certificates issued have one year validity and a default set of key usages.</p><p>In order for the controller-manager to sign certificates, it needs the following:</p><ul><li>access to the "Kubernetes CA key and certificate" that you created and distributed</li><li>enabling CSR signing</li></ul><h3 id=access-to-key-and-certificate>Access to key and certificate</h3><p>As described earlier, you need to create a Kubernetes CA key and certificate, and distribute it to the control plane nodes. These will be used by the controller-manager to sign the kubelet certificates.</p><p>Since these signed certificates will, in turn, be used by the kubelet to authenticate as a regular kubelet to kube-apiserver, it is important that the CA provided to the controller-manager at this stage also be trusted by kube-apiserver for authentication. This is provided to kube-apiserver with the flag <code>--client-ca-file=FILENAME</code> (for example, <code>--client-ca-file=/var/lib/kubernetes/ca.pem</code>), as described in the kube-apiserver configuration section.</p><p>To provide the Kubernetes CA key and certificate to kube-controller-manager, use the following flags:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>--cluster-signing-cert-file<span style=color:#666>=</span><span style=color:#b44>"/etc/path/to/kubernetes/ca/ca.crt"</span> --cluster-signing-key-file<span style=color:#666>=</span><span style=color:#b44>"/etc/path/to/kubernetes/ca/ca.key"</span> </span></span></code></pre></div><p>For example:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>--cluster-signing-cert-file<span style=color:#666>=</span><span style=color:#b44>"/var/lib/kubernetes/ca.pem"</span> --cluster-signing-key-file<span style=color:#666>=</span><span style=color:#b44>"/var/lib/kubernetes/ca-key.pem"</span> </span></span></code></pre></div><p>The validity duration of signed certificates can be configured with flag:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>--cluster-signing-duration </span></span></code></pre></div><h3 id=approval>Approval</h3><p>In order to approve CSRs, you need to tell the controller-manager that it is acceptable to approve them. This is done by granting RBAC permissions to the correct group.</p><p>There are two distinct sets of permissions:</p><ul><li><code>nodeclient</code>: If a node is creating a new certificate for a node, then it does not have a certificate yet. It is authenticating using one of the tokens listed above, and thus is part of the group <code>system:bootstrappers</code>.</li><li><code>selfnodeclient</code>: If a node is renewing its certificate, then it already has a certificate (by definition), which it uses continuously to authenticate as part of the group <code>system:nodes</code>.</li></ul><p>To enable the kubelet to request and receive a new certificate, create a <code>ClusterRoleBinding</code> that binds the group in which the bootstrapping node is a member <code>system:bootstrappers</code> to the <code>ClusterRole</code> that grants it permission, <code>system:certificates.k8s.io:certificatesigningrequests:nodeclient</code>:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:#080;font-style:italic># Approve all CSRs for the group "system:bootstrappers"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ClusterRoleBinding<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>auto-approve-csrs-for-group<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>subjects</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Group<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>system:bootstrappers<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroup</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>roleRef</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ClusterRole<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>system:certificates.k8s.io:certificatesigningrequests:nodeclient<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroup</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io<span style=color:#bbb> </span></span></span></code></pre></div><p>To enable the kubelet to renew its own client certificate, create a <code>ClusterRoleBinding</code> that binds the group in which the fully functioning node is a member <code>system:nodes</code> to the <code>ClusterRole</code> that grants it permission, <code>system:certificates.k8s.io:certificatesigningrequests:selfnodeclient</code>:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:#080;font-style:italic># Approve renewal CSRs for the group "system:nodes"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ClusterRoleBinding<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>auto-approve-renewals-for-nodes<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>subjects</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Group<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>system:nodes<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroup</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>roleRef</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ClusterRole<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>system:certificates.k8s.io:certificatesigningrequests:selfnodeclient<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiGroup</span>:<span style=color:#bbb> </span>rbac.authorization.k8s.io<span style=color:#bbb> </span></span></span></code></pre></div><p>The <code>csrapproving</code> controller that ships as part of <a href=/docs/reference/command-line-tools-reference/kube-controller-manager/>kube-controller-manager</a> and is enabled by default. The controller uses the <a href=/docs/reference/access-authn-authz/authorization/#checking-api-access><code>SubjectAccessReview</code> API</a> to determine if a given user is authorized to request a CSR, then approves based on the authorization outcome. To prevent conflicts with other approvers, the built-in approver doesn't explicitly deny CSRs. It only ignores unauthorized requests. The controller also prunes expired certificates as part of garbage collection.</p><h2 id=kubelet-configuration>kubelet configuration</h2><p>Finally, with the control plane nodes properly set up and all of the necessary authentication and authorization in place, we can configure the kubelet.</p><p>The kubelet requires the following configuration to bootstrap:</p><ul><li>A path to store the key and certificate it generates (optional, can use default)</li><li>A path to a <code>kubeconfig</code> file that does not yet exist; it will place the bootstrapped config file here</li><li>A path to a bootstrap <code>kubeconfig</code> file to provide the URL for the server and bootstrap credentials, e.g. a bootstrap token</li><li>Optional: instructions to rotate certificates</li></ul><p>The bootstrap <code>kubeconfig</code> should be in a path available to the kubelet, for example <code>/var/lib/kubelet/bootstrap-kubeconfig</code>.</p><p>Its format is identical to a normal <code>kubeconfig</code> file. A sample file might look as follows:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>Config<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>clusters</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>cluster</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>certificate-authority</span>:<span style=color:#bbb> </span>/var/lib/kubernetes/ca.pem<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>server</span>:<span style=color:#bbb> </span>https://my.server.example.com:6443<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>bootstrap<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>contexts</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>context</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>cluster</span>:<span style=color:#bbb> </span>bootstrap<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>user</span>:<span style=color:#bbb> </span>kubelet-bootstrap<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>bootstrap<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>current-context</span>:<span style=color:#bbb> </span>bootstrap<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>preferences</span>:<span style=color:#bbb> </span>{}<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>users</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>kubelet-bootstrap<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>user</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>token</span>:<span style=color:#bbb> </span>07401b.f395accd246ae52d<span style=color:#bbb> </span></span></span></code></pre></div><p>The important elements to note are:</p><ul><li><code>certificate-authority</code>: path to a CA file, used to validate the server certificate presented by kube-apiserver</li><li><code>server</code>: URL to kube-apiserver</li><li><code>token</code>: the token to use</li></ul><p>The format of the token does not matter, as long as it matches what kube-apiserver expects. In the above example, we used a bootstrap token. As stated earlier, <em>any</em> valid authentication method can be used, not only tokens.</p><p>Because the bootstrap <code>kubeconfig</code> <em>is</em> a standard <code>kubeconfig</code>, you can use <code>kubectl</code> to generate it. To create the above example file:</p><pre tabindex=0><code>kubectl config --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig set-cluster bootstrap --server='https://my.server.example.com:6443' --certificate-authority=/var/lib/kubernetes/ca.pem kubectl config --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig set-credentials kubelet-bootstrap --token=07401b.f395accd246ae52d kubectl config --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig set-context bootstrap --user=kubelet-bootstrap --cluster=bootstrap kubectl config --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig use-context bootstrap </code></pre><p>To indicate to the kubelet to use the bootstrap <code>kubeconfig</code>, use the following kubelet flag:</p><pre tabindex=0><code>--bootstrap-kubeconfig="/var/lib/kubelet/bootstrap-kubeconfig" --kubeconfig="/var/lib/kubelet/kubeconfig" </code></pre><p>When starting the kubelet, if the file specified via <code>--kubeconfig</code> does not exist, the bootstrap kubeconfig specified via <code>--bootstrap-kubeconfig</code> is used to request a client certificate from the API server. On approval of the certificate request and receipt back by the kubelet, a kubeconfig file referencing the generated key and obtained certificate is written to the path specified by <code>--kubeconfig</code>. The certificate and key file will be placed in the directory specified by <code>--cert-dir</code>.</p><h3 id=client-and-serving-certificates>Client and serving certificates</h3><p>All of the above relate to kubelet <em>client</em> certificates, specifically, the certificates a kubelet uses to authenticate to kube-apiserver.</p><p>A kubelet also can use <em>serving</em> certificates. The kubelet itself exposes an https endpoint for certain features. To secure these, the kubelet can do one of:</p><ul><li>use provided key and certificate, via the <code>--tls-private-key-file</code> and <code>--tls-cert-file</code> flags</li><li>create self-signed key and certificate, if a key and certificate are not provided</li><li>request serving certificates from the cluster server, via the CSR API</li></ul><p>The client certificate provided by TLS bootstrapping is signed, by default, for <code>client auth</code> only, and thus cannot be used as serving certificates, or <code>server auth</code>.</p><p>However, you <em>can</em> enable its server certificate, at least partially, via certificate rotation.</p><h3 id=certificate-rotation>Certificate rotation</h3><p>Kubernetes v1.8 and higher kubelet implements features for enabling rotation of its client and/or serving certificates. Note, rotation of serving certificate is a <strong>beta</strong> feature and requires the <code>RotateKubeletServerCertificate</code> feature flag on the kubelet (enabled by default).</p><p>You can configure the kubelet to rotate its client certificates by creating new CSRs as its existing credentials expire. To enable this feature, use the <code>rotateCertificates</code> field of <a href=/docs/tasks/administer-cluster/kubelet-config-file/>kubelet configuration file</a> or pass the following command line argument to the kubelet (deprecated):</p><pre tabindex=0><code>--rotate-certificates </code></pre><p>Enabling <code>RotateKubeletServerCertificate</code> causes the kubelet <strong>both</strong> to request a serving certificate after bootstrapping its client credentials <strong>and</strong> to rotate that certificate. To enable this behavior, use the field <code>serverTLSBootstrap</code> of the <a href=/docs/tasks/administer-cluster/kubelet-config-file/>kubelet configuration file</a> or pass the following command line argument to the kubelet (deprecated):</p><pre tabindex=0><code>--rotate-server-certificates </code></pre><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4><p>The CSR approving controllers implemented in core Kubernetes do not approve node <em>serving</em> certificates for <a href=https://github.com/kubernetes/community/pull/1982>security reasons</a>. To use <code>RotateKubeletServerCertificate</code> operators need to run a custom approving controller, or manually approve the serving certificate requests.</p><p>A deployment-specific approval process for kubelet serving certificates should typically only approve CSRs which:</p><ol><li>are requested by nodes (ensure the <code>spec.username</code> field is of the form <code>system:node:<nodeName></code> and <code>spec.groups</code> contains <code>system:nodes</code>)</li><li>request usages for a serving certificate (ensure <code>spec.usages</code> contains <code>server auth</code>, optionally contains <code>digital signature</code> and <code>key encipherment</code>, and contains no other usages)</li><li>only have IP and DNS subjectAltNames that belong to the requesting node, and have no URI and Email subjectAltNames (parse the x509 Certificate Signing Request in <code>spec.request</code> to verify <code>subjectAltNames</code>)</li></ol></div><h2 id=other-authenticating-components>Other authenticating components</h2><p>All of TLS bootstrapping described in this document relates to the kubelet. However, other components may need to communicate directly with kube-apiserver. Notable is kube-proxy, which is part of the Kubernetes node components and runs on every node, but may also include other components such as monitoring or networking.</p><p>Like the kubelet, these other components also require a method of authenticating to kube-apiserver. You have several options for generating these credentials:</p><ul><li>The old way: Create and distribute certificates the same way you did for kubelet before TLS bootstrapping</li><li>DaemonSet: Since the kubelet itself is loaded on each node, and is sufficient to start base services, you can run kube-proxy and other node-specific services not as a standalone process, but rather as a daemonset in the <code>kube-system</code> namespace. Since it will be in-cluster, you can give it a proper service account with appropriate permissions to perform its activities. This may be the simplest way to configure such services.</li></ul><h2 id=kubectl-approval>kubectl approval</h2><p>CSRs can be approved outside of the approval flows built into the controller manager.</p><p>The signing controller does not immediately sign all certificate requests. Instead, it waits until they have been flagged with an "Approved" status by an appropriately-privileged user. This flow is intended to allow for automated approval handled by an external approval controller or the approval controller implemented in the core controller-manager. However cluster administrators can also manually approve certificate requests using kubectl. An administrator can list CSRs with <code>kubectl get csr</code> and describe one in detail with <code>kubectl describe csr <name></code>. An administrator can approve or deny a CSR with <code>kubectl certificate approve <name></code> and <code>kubectl certificate deny <name></code>.</p></div><div class=td-content style=page-break-before:always><h1 id=pg-7b9fccf8215aea0edc5c97e72f1f72e4>15 - Validating Admission Policy</h1><div class="feature-state-notice feature-stable"><span class=feature-state-name>FEATURE STATE:</span> <code>Kubernetes v1.30 [stable]</code></div><p>This page provides an overview of Validating Admission Policy.</p><h2 id=what-is-validating-admission-policy>What is Validating Admission Policy?</h2><p>Validating admission policies offer a declarative, in-process alternative to validating admission webhooks.</p><p>Validating admission policies use the Common Expression Language (CEL) to declare the validation rules of a policy. Validation admission policies are highly configurable, enabling policy authors to define policies that can be parameterized and scoped to resources as needed by cluster administrators.</p><h2 id=what-resources-make-a-policy>What Resources Make a Policy</h2><p>A policy is generally made up of three resources:</p><ul><li><p>The <code>ValidatingAdmissionPolicy</code> describes the abstract logic of a policy (think: "this policy makes sure a particular label is set to a particular value").</p></li><li><p>A <code>ValidatingAdmissionPolicyBinding</code> links the above resources together and provides scoping. If you only want to require an <code>owner</code> label to be set for <code>Pods</code>, the binding is where you would specify this restriction.</p></li><li><p>A parameter resource provides information to a ValidatingAdmissionPolicy to make it a concrete statement (think "the <code>owner</code> label must be set to something that ends in <code>.company.com</code>"). A native type such as ConfigMap or a CRD defines the schema of a parameter resource. <code>ValidatingAdmissionPolicy</code> objects specify what Kind they are expecting for their parameter resource.</p></li></ul><p>At least a <code>ValidatingAdmissionPolicy</code> and a corresponding <code>ValidatingAdmissionPolicyBinding</code> must be defined for a policy to have an effect.</p><p>If a <code>ValidatingAdmissionPolicy</code> does not need to be configured via parameters, simply leave <code>spec.paramKind</code> in <code>ValidatingAdmissionPolicy</code> not specified.</p><h2 id=getting-started-with-validating-admission-policy>Getting Started with Validating Admission Policy</h2><p>Validating Admission Policy is part of the cluster control-plane. You should write and deploy them with great caution. The following describes how to quickly experiment with Validating Admission Policy.</p><h3 id=creating-a-validatingadmissionpolicy>Creating a ValidatingAdmissionPolicy</h3><p>The following is an example of a ValidatingAdmissionPolicy.</p><div class="highlight code-sample"><div class=copy-code-icon><a href=https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/validatingadmissionpolicy/basic-example-policy.yaml download=validatingadmissionpolicy/basic-example-policy.yaml><code>validatingadmissionpolicy/basic-example-policy.yaml</code> </a><img src=/images/copycode.svg class=icon-copycode onclick='copyCode("validatingadmissionpolicy-basic-example-policy-yaml")' title="Copy validatingadmissionpolicy/basic-example-policy.yaml to clipboard"></img></div><div class=includecode id=validatingadmissionpolicy-basic-example-policy-yaml><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ValidatingAdmissionPolicy<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>"demo-policy.example.com"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>spec</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>failurePolicy</span>:<span style=color:#bbb> </span>Fail<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>matchConstraints</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resourceRules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"apps"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersions</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"v1"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>operations</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"CREATE"</span>,<span style=color:#bbb> </span><span style=color:#b44>"UPDATE"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"deployments"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>validations</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>"object.spec.replicas <= 5"</span></span></span></code></pre></div></div></div><p><code>spec.validations</code> contains CEL expressions which use the <a href=https://github.com/google/cel-spec>Common Expression Language (CEL)</a> to validate the request. If an expression evaluates to false, the validation check is enforced according to the <code>spec.failurePolicy</code> field.</p><div class="alert alert-info" role=alert><h4 class=alert-heading>Note:</h4>You can quickly test CEL expressions in <a href=https://playcel.undistro.io>CEL Playground</a>.</div><p>To configure a validating admission policy for use in a cluster, a binding is required. The following is an example of a ValidatingAdmissionPolicyBinding.:</p><div class="highlight code-sample"><div class=copy-code-icon><a href=https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/validatingadmissionpolicy/basic-example-binding.yaml download=validatingadmissionpolicy/basic-example-binding.yaml><code>validatingadmissionpolicy/basic-example-binding.yaml</code> </a><img src=/images/copycode.svg class=icon-copycode onclick='copyCode("validatingadmissionpolicy-basic-example-binding-yaml")' title="Copy validatingadmissionpolicy/basic-example-binding.yaml to clipboard"></img></div><div class=includecode id=validatingadmissionpolicy-basic-example-binding-yaml><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ValidatingAdmissionPolicyBinding<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>"demo-binding-test.example.com"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>spec</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>policyName</span>:<span style=color:#bbb> </span><span style=color:#b44>"demo-policy.example.com"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>validationActions</span>:<span style=color:#bbb> </span>[Deny]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>matchResources</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespaceSelector</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>matchLabels</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>environment</span>:<span style=color:#bbb> </span>test<span style=color:#bbb> </span></span></span></code></pre></div></div></div><p>When trying to create a deployment with replicas set not satisfying the validation expression, an error will return containing message:</p><pre tabindex=0><code class=language-none data-lang=none>ValidatingAdmissionPolicy 'demo-policy.example.com' with binding 'demo-binding-test.example.com' denied request: failed expression: object.spec.replicas <= 5 </code></pre><p>The above provides a simple example of using ValidatingAdmissionPolicy without a parameter configured.</p><h4 id=validation-actions>Validation actions</h4><p>Each <code>ValidatingAdmissionPolicyBinding</code> must specify one or more <code>validationActions</code> to declare how <code>validations</code> of a policy are enforced.</p><p>The supported <code>validationActions</code> are:</p><ul><li><code>Deny</code>: Validation failure results in a denied request.</li><li><code>Warn</code>: Validation failure is reported to the request client as a <a href=/blog/2020/09/03/warnings/>warning</a>.</li><li><code>Audit</code>: Validation failure is included in the audit event for the API request.</li></ul><p>For example, to both warn clients about a validation failure and to audit the validation failures, use:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>validationActions</span>:<span style=color:#bbb> </span>[Warn, Audit]<span style=color:#bbb> </span></span></span></code></pre></div><p><code>Deny</code> and <code>Warn</code> may not be used together since this combination needlessly duplicates the validation failure both in the API response body and the HTTP warning headers.</p><p>A <code>validation</code> that evaluates to false is always enforced according to these actions. Failures defined by the <code>failurePolicy</code> are enforced according to these actions only if the <code>failurePolicy</code> is set to <code>Fail</code> (or not specified), otherwise the failures are ignored.</p><p>See <a href=/docs/reference/labels-annotations-taints/audit-annotations/#validation-policy-admission-k8s-io-validation-failure>Audit Annotations: validation failures</a> for more details about the validation failure audit annotation.</p><h3 id=parameter-resources>Parameter resources</h3><p>Parameter resources allow a policy configuration to be separate from its definition. A policy can define paramKind, which outlines GVK of the parameter resource, and then a policy binding ties a policy by name (via policyName) to a particular parameter resource via paramRef.</p><p>If parameter configuration is needed, the following is an example of a ValidatingAdmissionPolicy with parameter configuration.</p><div class="highlight code-sample"><div class=copy-code-icon><a href=https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/validatingadmissionpolicy/policy-with-param.yaml download=validatingadmissionpolicy/policy-with-param.yaml><code>validatingadmissionpolicy/policy-with-param.yaml</code> </a><img src=/images/copycode.svg class=icon-copycode onclick='copyCode("validatingadmissionpolicy-policy-with-param-yaml")' title="Copy validatingadmissionpolicy/policy-with-param.yaml to clipboard"></img></div><div class=includecode id=validatingadmissionpolicy-policy-with-param-yaml><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ValidatingAdmissionPolicy<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>"replicalimit-policy.example.com"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>spec</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>failurePolicy</span>:<span style=color:#bbb> </span>Fail<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>paramKind</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rules.example.com/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ReplicaLimit<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>matchConstraints</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resourceRules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"apps"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersions</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"v1"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>operations</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"CREATE"</span>,<span style=color:#bbb> </span><span style=color:#b44>"UPDATE"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"deployments"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>validations</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>"object.spec.replicas <= params.maxReplicas"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>reason</span>:<span style=color:#bbb> </span>Invalid</span></span></code></pre></div></div></div><p>The <code>spec.paramKind</code> field of the ValidatingAdmissionPolicy specifies the kind of resources used to parameterize this policy. For this example, it is configured by ReplicaLimit custom resources. Note in this example how the CEL expression references the parameters via the CEL params variable, e.g. <code>params.maxReplicas</code>. <code>spec.matchConstraints</code> specifies what resources this policy is designed to validate. Note that the native types such like <code>ConfigMap</code> could also be used as parameter reference.</p><p>The <code>spec.validations</code> fields contain CEL expressions. If an expression evaluates to false, the validation check is enforced according to the <code>spec.failurePolicy</code> field.</p><p>The validating admission policy author is responsible for providing the ReplicaLimit parameter CRD.</p><p>To configure an validating admission policy for use in a cluster, a binding and parameter resource are created. The following is an example of a ValidatingAdmissionPolicyBinding that uses a <strong>cluster-wide</strong> param - the same param will be used to validate every resource request that matches the binding:</p><div class="highlight code-sample"><div class=copy-code-icon><a href=https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/validatingadmissionpolicy/binding-with-param.yaml download=validatingadmissionpolicy/binding-with-param.yaml><code>validatingadmissionpolicy/binding-with-param.yaml</code> </a><img src=/images/copycode.svg class=icon-copycode onclick='copyCode("validatingadmissionpolicy-binding-with-param-yaml")' title="Copy validatingadmissionpolicy/binding-with-param.yaml to clipboard"></img></div><div class=includecode id=validatingadmissionpolicy-binding-with-param-yaml><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ValidatingAdmissionPolicyBinding<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>"replicalimit-binding-test.example.com"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>spec</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>policyName</span>:<span style=color:#bbb> </span><span style=color:#b44>"replicalimit-policy.example.com"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>validationActions</span>:<span style=color:#bbb> </span>[Deny]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>paramRef</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>"replica-limit-test.example.com"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespace</span>:<span style=color:#bbb> </span><span style=color:#b44>"default"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>matchResources</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespaceSelector</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>matchLabels</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>environment</span>:<span style=color:#bbb> </span>test</span></span></code></pre></div></div></div><p>Notice this binding applies a parameter to the policy for all resources which are in the <code>test</code> environment.</p><p>The parameter resource could be as following:</p><div class="highlight code-sample"><div class=copy-code-icon><a href=https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/validatingadmissionpolicy/replicalimit-param.yaml download=validatingadmissionpolicy/replicalimit-param.yaml><code>validatingadmissionpolicy/replicalimit-param.yaml</code> </a><img src=/images/copycode.svg class=icon-copycode onclick='copyCode("validatingadmissionpolicy-replicalimit-param-yaml")' title="Copy validatingadmissionpolicy/replicalimit-param.yaml to clipboard"></img></div><div class=includecode id=validatingadmissionpolicy-replicalimit-param-yaml><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rules.example.com/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ReplicaLimit<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>"replica-limit-test.example.com"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespace</span>:<span style=color:#bbb> </span><span style=color:#b44>"default"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>maxReplicas</span>:<span style=color:#bbb> </span><span style=color:#666>3</span><span style=color:#bbb> </span></span></span></code></pre></div></div></div><p>This policy parameter resource limits deployments to a max of 3 replicas.</p><p>An admission policy may have multiple bindings. To bind all other environments to have a maxReplicas limit of 100, create another ValidatingAdmissionPolicyBinding:</p><div class="highlight code-sample"><div class=copy-code-icon><a href=https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/validatingadmissionpolicy/binding-with-param-prod.yaml download=validatingadmissionpolicy/binding-with-param-prod.yaml><code>validatingadmissionpolicy/binding-with-param-prod.yaml</code> </a><img src=/images/copycode.svg class=icon-copycode onclick='copyCode("validatingadmissionpolicy-binding-with-param-prod-yaml")' title="Copy validatingadmissionpolicy/binding-with-param-prod.yaml to clipboard"></img></div><div class=includecode id=validatingadmissionpolicy-binding-with-param-prod-yaml><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ValidatingAdmissionPolicyBinding<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>"replicalimit-binding-nontest"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>spec</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>policyName</span>:<span style=color:#bbb> </span><span style=color:#b44>"replicalimit-policy.example.com"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>validationActions</span>:<span style=color:#bbb> </span>[Deny]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>paramRef</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>"replica-limit-prod.example.com"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespace</span>:<span style=color:#bbb> </span><span style=color:#b44>"default"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>matchResources</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>namespaceSelector</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>matchExpressions</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>key</span>:<span style=color:#bbb> </span>environment<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>operator</span>:<span style=color:#bbb> </span>NotIn<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>values</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- test</span></span></code></pre></div></div></div><p>Notice this binding applies a different parameter to resources which are not in the <code>test</code> environment.</p><p>And have a parameter resource:</p><div class="highlight code-sample"><div class=copy-code-icon><a href=https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/validatingadmissionpolicy/replicalimit-param-prod.yaml download=validatingadmissionpolicy/replicalimit-param-prod.yaml><code>validatingadmissionpolicy/replicalimit-param-prod.yaml</code> </a><img src=/images/copycode.svg class=icon-copycode onclick='copyCode("validatingadmissionpolicy-replicalimit-param-prod-yaml")' title="Copy validatingadmissionpolicy/replicalimit-param-prod.yaml to clipboard"></img></div><div class=includecode id=validatingadmissionpolicy-replicalimit-param-prod-yaml><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rules.example.com/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ReplicaLimit<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>"replica-limit-prod.example.com"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>maxReplicas</span>:<span style=color:#bbb> </span><span style=color:#666>100</span></span></span></code></pre></div></div></div><p>For each admission request, the API server evaluates CEL expressions of each (policy, binding, param) combination that match the request. For a request to be admitted it must pass <strong>all</strong> evaluations.</p><p>If multiple bindings match the request, the policy will be evaluated for each, and they must all pass evaluation for the policy to be considered passed.</p><p>If multiple parameters match a single binding, the policy rules will be evaluated for each param, and they too must all pass for the binding to be considered passed. Bindings can have overlapping match criteria. The policy is evaluated for each matching binding-parameter combination. A policy may even be evaluated multiple times if multiple bindings match it, or a single binding that matches multiple parameters.</p><p>The params object representing a parameter resource will not be set if a parameter resource has not been bound, so for policies requiring a parameter resource, it can be useful to add a check to ensure one has been bound. A parameter resource will not be bound and <code>params</code> will be null if <code>paramKind</code> of the policy, or <code>paramRef</code> of the binding are not specified.</p><p>For the use cases require parameter configuration, we recommend to add a param check in <code>spec.validations[0].expression</code>:</p><pre tabindex=0><code>- expression: "params != null" message: "params missing but required to bind to this policy" </code></pre><h4 id=optional-parameters>Optional parameters</h4><p>It can be convenient to be able to have optional parameters as part of a parameter resource, and only validate them if present. CEL provides <code>has()</code>, which checks if the key passed to it exists. CEL also implements Boolean short-circuiting. If the first half of a logical OR evaluates to true, it won’t evaluate the other half (since the result of the entire OR will be true regardless).</p><p>Combining the two, we can provide a way to validate optional parameters:</p><p><code>!has(params.optionalNumber) || (params.optionalNumber >= 5 && params.optionalNumber <= 10)</code></p><p>Here, we first check that the optional parameter is present with <code>!has(params.optionalNumber)</code>.</p><ul><li>If <code>optionalNumber</code> hasn’t been defined, then the expression short-circuits since <code>!has(params.optionalNumber)</code> will evaluate to true.</li><li>If <code>optionalNumber</code> has been defined, then the latter half of the CEL expression will be evaluated, and optionalNumber will be checked to ensure that it contains a value between 5 and 10 inclusive.</li></ul><h4 id=per-namespace-parameters>Per-namespace Parameters</h4><p>As the author of a ValidatingAdmissionPolicy and its ValidatingAdmissionPolicyBinding, you can choose to specify cluster-wide, or per-namespace parameters. If you specify a <code>namespace</code> for the binding's <code>paramRef</code>, the control plane only searches for parameters in that namespace.</p><p>However, if <code>namespace</code> is not specified in the ValidatingAdmissionPolicyBinding, the API server can search for relevant parameters in the namespace that a request is against. For example, if you make a request to modify a ConfigMap in the <code>default</code> namespace and there is a relevant ValidatingAdmissionPolicyBinding with no <code>namespace</code> set, then the API server looks for a parameter object in <code>default</code>. This design enables policy configuration that depends on the namespace of the resource being manipulated, for more fine-tuned control.</p><h4 id=parameter-selector>Parameter selector</h4><p>In addition to specify a parameter in a binding by <code>name</code>, you may choose instead to specify label selector, such that all resources of the policy's <code>paramKind</code>, and the param's <code>namespace</code> (if applicable) that match the label selector are selected for evaluation. See <a class=glossary-tooltip title='Allows users to filter a list of resources based on labels.' data-toggle=tooltip data-placement=top href=/docs/concepts/overview/working-with-objects/labels/ target=_blank aria-label=selector>selector</a> for more information on how label selectors match resources.</p><p>If multiple parameters are found to meet the condition, the policy's rules are evaluated for each parameter found and the results will be ANDed together.</p><p>If <code>namespace</code> is provided, only objects of the <code>paramKind</code> in the provided namespace are eligible for selection. Otherwise, when <code>namespace</code> is empty and <code>paramKind</code> is namespace-scoped, the <code>namespace</code> used in the request being admitted will be used.</p><h4 id=authorization-check>Authorization checks</h4><p>We introduced the authorization check for parameter resources. User is expected to have <code>read</code> access to the resources referenced by <code>paramKind</code> in <code>ValidatingAdmissionPolicy</code> and <code>paramRef</code> in <code>ValidatingAdmissionPolicyBinding</code>.</p><p>Note that if a resource in <code>paramKind</code> fails resolving via the restmapper, <code>read</code> access to all resources of groups is required.</p><h3 id=failure-policy>Failure Policy</h3><p><code>failurePolicy</code> defines how mis-configurations and CEL expressions evaluating to error from the admission policy are handled. Allowed values are <code>Ignore</code> or <code>Fail</code>.</p><ul><li><code>Ignore</code> means that an error calling the ValidatingAdmissionPolicy is ignored and the API request is allowed to continue.</li><li><code>Fail</code> means that an error calling the ValidatingAdmissionPolicy causes the admission to fail and the API request to be rejected.</li></ul><p>Note that the <code>failurePolicy</code> is defined inside <code>ValidatingAdmissionPolicy</code>:</p><div class="highlight code-sample"><div class=copy-code-icon><a href=https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/validatingadmissionpolicy/failure-policy-ignore.yaml download=validatingadmissionpolicy/failure-policy-ignore.yaml><code>validatingadmissionpolicy/failure-policy-ignore.yaml</code> </a><img src=/images/copycode.svg class=icon-copycode onclick='copyCode("validatingadmissionpolicy-failure-policy-ignore-yaml")' title="Copy validatingadmissionpolicy/failure-policy-ignore.yaml to clipboard"></img></div><div class=includecode id=validatingadmissionpolicy-failure-policy-ignore-yaml><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ValidatingAdmissionPolicy<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>spec</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#00f;font-weight:700>...</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>failurePolicy</span>:<span style=color:#bbb> </span>Ignore<span style=color:#bbb> </span><span style=color:#080;font-style:italic># The default is "Fail"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>validations</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span>- <span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>"object.spec.xyz == params.x"</span><span style=color:#bbb> </span></span></span></code></pre></div></div></div><h3 id=validation-expression>Validation Expression</h3><p><code>spec.validations[i].expression</code> represents the expression which will be evaluated by CEL. To learn more, see the <a href=https://github.com/google/cel-spec>CEL language specification</a> CEL expressions have access to the contents of the Admission request/response, organized into CEL variables as well as some other useful variables:</p><ul><li>'object' - The object from the incoming request. The value is null for DELETE requests.</li><li>'oldObject' - The existing object. The value is null for CREATE requests.</li><li>'request' - Attributes of the <a href=/docs/reference/config-api/apiserver-admission.v1/#admission-k8s-io-v1-AdmissionRequest>admission request</a>.</li><li>'params' - Parameter resource referred to by the policy binding being evaluated. The value is null if <code>ParamKind</code> is not specified.</li><li><code>namespaceObject</code> - The namespace, as a Kubernetes resource, that the incoming object belongs to. The value is null if the incoming object is cluster-scoped.</li><li><code>authorizer</code> - A CEL Authorizer. May be used to perform authorization checks for the principal (authenticated user) of the request. See <a href=https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#AuthzSelectors>AuthzSelectors</a> and <a href=https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz>Authz</a> in the Kubernetes CEL library documentation for more details.</li><li><code>authorizer.requestResource</code> - A shortcut for an authorization check configured with the request resource (group, resource, (subresource), namespace, name).</li></ul><p>The <code>apiVersion</code>, <code>kind</code>, <code>metadata.name</code> and <code>metadata.generateName</code> are always accessible from the root of the object. No other metadata properties are accessible.</p><p>Equality on arrays with list type of 'set' or 'map' ignores element order, i.e. [1, 2] == [2, 1]. Concatenation on arrays with x-kubernetes-list-type use the semantics of the list type:</p><ul><li>'set': <code>X + Y</code> performs a union where the array positions of all elements in <code>X</code> are preserved and non-intersecting elements in <code>Y</code> are appended, retaining their partial order.</li><li>'map': <code>X + Y</code> performs a merge where the array positions of all keys in <code>X</code> are preserved but the values are overwritten by values in <code>Y</code> when the key sets of <code>X</code> and <code>Y</code> intersect. Elements in <code>Y</code> with non-intersecting keys are appended, retaining their partial order.</li></ul><h4 id=validation-expression-examples>Validation expression examples</h4><table><thead><tr><th>Expression</th><th>Purpose</th></tr></thead><tbody><tr><td><code>object.minReplicas <= object.replicas && object.replicas <= object.maxReplicas</code></td><td>Validate that the three fields defining replicas are ordered appropriately</td></tr><tr><td><code>'Available' in object.stateCounts</code></td><td>Validate that an entry with the 'Available' key exists in a map</td></tr><tr><td><code>(size(object.list1) == 0) != (size(object.list2) == 0)</code></td><td>Validate that one of two lists is non-empty, but not both</td></tr><tr><td><code>!('MY_KEY' in object.map1) || object['MY_KEY'].matches('^[a-zA-Z]*$')</code></td><td>Validate the value of a map for a specific key, if it is in the map</td></tr><tr><td><code>object.envars.filter(e, e.name == 'MY_ENV').all(e, e.value.matches('^[a-zA-Z]*$')</code></td><td>Validate the 'value' field of a listMap entry where key field 'name' is 'MY_ENV'</td></tr><tr><td><code>has(object.expired) && object.created + object.ttl < object.expired</code></td><td>Validate that 'expired' date is after a 'create' date plus a 'ttl' duration</td></tr><tr><td><code>object.health.startsWith('ok')</code></td><td>Validate a 'health' string field has the prefix 'ok'</td></tr><tr><td><code>object.widgets.exists(w, w.key == 'x' && w.foo < 10)</code></td><td>Validate that the 'foo' property of a listMap item with a key 'x' is less than 10</td></tr><tr><td><code>type(object) == string ? object == '100%' : object == 1000</code></td><td>Validate an int-or-string field for both the int and string cases</td></tr><tr><td><code>object.metadata.name.startsWith(object.prefix)</code></td><td>Validate that an object's name has the prefix of another field value</td></tr><tr><td><code>object.set1.all(e, !(e in object.set2))</code></td><td>Validate that two listSets are disjoint</td></tr><tr><td><code>size(object.names) == size(object.details) && object.names.all(n, n in object.details)</code></td><td>Validate the 'details' map is keyed by the items in the 'names' listSet</td></tr><tr><td><code>size(object.clusters.filter(c, c.name == object.primary)) == 1</code></td><td>Validate that the 'primary' property has one and only one occurrence in the 'clusters' listMap</td></tr></tbody></table><p>Read <a href=https://github.com/google/cel-spec/blob/v0.6.0/doc/langdef.md#evaluation>Supported evaluation on CEL</a> for more information about CEL rules.</p><p><code>spec.validation[i].reason</code> represents a machine-readable description of why this validation failed. If this is the first validation in the list to fail, this reason, as well as the corresponding HTTP response code, are used in the HTTP response to the client. The currently supported reasons are: <code>Unauthorized</code>, <code>Forbidden</code>, <code>Invalid</code>, <code>RequestEntityTooLarge</code>. If not set, <code>StatusReasonInvalid</code> is used in the response to the client.</p><h3 id=matching-requests-matchconditions>Matching requests: <code>matchConditions</code></h3><p>You can define <em>match conditions</em> for a <code>ValidatingAdmissionPolicy</code> if you need fine-grained request filtering. These conditions are useful if you find that match rules, <code>objectSelectors</code> and <code>namespaceSelectors</code> still doesn't provide the filtering you want. Match conditions are <a href=/docs/reference/using-api/cel/>CEL expressions</a>. All match conditions must evaluate to true for the resource to be evaluated.</p><p>Here is an example illustrating a few different uses for match conditions:</p><div class="highlight code-sample"><div class=copy-code-icon><a href=https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/access/validating-admission-policy-match-conditions.yaml download=access/validating-admission-policy-match-conditions.yaml><code>access/validating-admission-policy-match-conditions.yaml</code> </a><img src=/images/copycode.svg class=icon-copycode onclick='copyCode("access-validating-admission-policy-match-conditions-yaml")' title="Copy access/validating-admission-policy-match-conditions.yaml to clipboard"></img></div><div class=includecode id=access-validating-admission-policy-match-conditions-yaml><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ValidatingAdmissionPolicy<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>"demo-policy.example.com"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>spec</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>failurePolicy</span>:<span style=color:#bbb> </span>Fail<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>matchConstraints</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resourceRules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"*"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersions</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"*"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>operations</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"CREATE"</span>,<span style=color:#bbb> </span><span style=color:#b44>"UPDATE"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"*"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>matchConditions</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>'exclude-leases'</span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Each match condition must have a unique name</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>'!(request.resource.group == "coordination.k8s.io" && request.resource.resource == "leases")'</span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Match non-lease resources.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>'exclude-kubelet-requests'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>'!("system:nodes" in request.userInfo.groups)'</span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Match requests made by non-node users.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>'rbac'</span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># Skip RBAC requests.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>'request.resource.group != "rbac.authorization.k8s.io"'</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>validations</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>"!object.metadata.name.contains('demo') || object.metadata.namespace == 'demo'"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span></code></pre></div></div></div><p>Match conditions have access to the same CEL variables as validation expressions.</p><p>In the event of an error evaluating a match condition the policy is not evaluated. Whether to reject the request is determined as follows:</p><ol><li>If <strong>any</strong> match condition evaluated to <code>false</code> (regardless of other errors), the API server skips the policy.</li><li>Otherwise:<ul><li>for <a href=#failure-policy><code>failurePolicy: Fail</code></a>, reject the request (without evaluating the policy).</li><li>for <a href=#failure-policy><code>failurePolicy: Ignore</code></a>, proceed with the request but skip the policy.</li></ul></li></ol><h3 id=audit-annotations>Audit annotations</h3><p><code>auditAnnotations</code> may be used to include audit annotations in the audit event of the API request.</p><p>For example, here is an admission policy with an audit annotation:</p><div class="highlight code-sample"><div class=copy-code-icon><a href=https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/access/validating-admission-policy-audit-annotation.yaml download=access/validating-admission-policy-audit-annotation.yaml><code>access/validating-admission-policy-audit-annotation.yaml</code> </a><img src=/images/copycode.svg class=icon-copycode onclick='copyCode("access-validating-admission-policy-audit-annotation-yaml")' title="Copy access/validating-admission-policy-audit-annotation.yaml to clipboard"></img></div><div class=includecode id=access-validating-admission-policy-audit-annotation-yaml><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ValidatingAdmissionPolicy<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>"demo-policy.example.com"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>spec</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>failurePolicy</span>:<span style=color:#bbb> </span>Fail<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>matchConstraints</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resourceRules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"apps"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersions</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"v1"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>operations</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"CREATE"</span>,<span style=color:#bbb> </span><span style=color:#b44>"UPDATE"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"deployments"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>validations</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>"object.spec.replicas > 50"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>messageExpression</span>:<span style=color:#bbb> </span><span style=color:#b44>"'Deployment spec.replicas set to ' + string(object.spec.replicas)"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>auditAnnotations</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>key</span>:<span style=color:#bbb> </span><span style=color:#b44>"high-replica-count"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>valueExpression</span>:<span style=color:#bbb> </span><span style=color:#b44>"'Deployment spec.replicas set to ' + string(object.spec.replicas)"</span><span style=color:#bbb> </span></span></span></code></pre></div></div></div><p>When an API request is validated with this admission policy, the resulting audit event will look like:</p><pre tabindex=0><code># the audit event recorded { "kind": "Event", "apiVersion": "audit.k8s.io/v1", "annotations": { "demo-policy.example.com/high-replica-count": "Deployment spec.replicas set to 128" # other annotations ... } # other fields ... } </code></pre><p>In this example the annotation will only be included if the <code>spec.replicas</code> of the Deployment is more than 50, otherwise the CEL expression evaluates to null and the annotation will not be included.</p><p>Note that audit annotation keys are prefixed by the name of the <code>ValidatingAdmissionWebhook</code> and a <code>/</code>. If another admission controller, such as an admission webhook, uses the exact same audit annotation key, the value of the first admission controller to include the audit annotation will be included in the audit event and all other values will be ignored.</p><h3 id=message-expression>Message expression</h3><p>To return a more friendly message when the policy rejects a request, we can use a CEL expression to composite a message with <code>spec.validations[i].messageExpression</code>. Similar to the validation expression, a message expression has access to <code>object</code>, <code>oldObject</code>, <code>request</code>, <code>params</code>, and <code>namespaceObject</code>. Unlike validations, message expression must evaluate to a string.</p><p>For example, to better inform the user of the reason of denial when the policy refers to a parameter, we can have the following validation:</p><div class="highlight code-sample"><div class=copy-code-icon><a href=https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/access/deployment-replicas-policy.yaml download=access/deployment-replicas-policy.yaml><code>access/deployment-replicas-policy.yaml</code> </a><img src=/images/copycode.svg class=icon-copycode onclick='copyCode("access-deployment-replicas-policy-yaml")' title="Copy access/deployment-replicas-policy.yaml to clipboard"></img></div><div class=includecode id=access-deployment-replicas-policy-yaml><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ValidatingAdmissionPolicy<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>"deploy-replica-policy.example.com"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>spec</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>paramKind</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>rules.example.com/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ReplicaLimit<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>matchConstraints</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resourceRules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"apps"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersions</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"v1"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>operations</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"CREATE"</span>,<span style=color:#bbb> </span><span style=color:#b44>"UPDATE"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"deployments"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>validations</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>"object.spec.replicas <= params.maxReplicas"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>messageExpression</span>:<span style=color:#bbb> </span><span style=color:#b44>"'object.spec.replicas must be no greater than ' + string(params.maxReplicas)"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>reason</span>:<span style=color:#bbb> </span>Invalid<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span></span></span></code></pre></div></div></div><p>After creating a params object that limits the replicas to 3 and setting up the binding, when we try to create a deployment with 5 replicas, we will receive the following message.</p><pre tabindex=0><code>$ kubectl create deploy --image=nginx nginx --replicas=5 error: failed to create deployment: deployments.apps "nginx" is forbidden: ValidatingAdmissionPolicy 'deploy-replica-policy.example.com' with binding 'demo-binding-test.example.com' denied request: object.spec.replicas must be no greater than 3 </code></pre><p>This is more informative than a static message of "too many replicas".</p><p>The message expression takes precedence over the static message defined in <code>spec.validations[i].message</code> if both are defined. However, if the message expression fails to evaluate, the static message will be used instead. Additionally, if the message expression evaluates to a multi-line string, the evaluation result will be discarded and the static message will be used if present. Note that static message is validated against multi-line strings.</p><h3 id=type-checking>Type checking</h3><p>When a policy definition is created or updated, the validation process parses the expressions it contains and reports any syntax errors, rejecting the definition if any errors are found. Afterward, the referred variables are checked for type errors, including missing fields and type confusion, against the matched types of <code>spec.matchConstraints</code>. The result of type checking can be retrieved from <code>status.typeChecking</code>. The presence of <code>status.typeChecking</code> indicates the completion of type checking, and an empty <code>status.typeChecking</code> means that no errors were detected.</p><p>For example, given the following policy definition:</p><div class="highlight code-sample"><div class=copy-code-icon><a href=https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/validatingadmissionpolicy/typechecking.yaml download=validatingadmissionpolicy/typechecking.yaml><code>validatingadmissionpolicy/typechecking.yaml</code> </a><img src=/images/copycode.svg class=icon-copycode onclick='copyCode("validatingadmissionpolicy-typechecking-yaml")' title="Copy validatingadmissionpolicy/typechecking.yaml to clipboard"></img></div><div class=includecode id=validatingadmissionpolicy-typechecking-yaml><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ValidatingAdmissionPolicy<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>"deploy-replica-policy.example.com"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>spec</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>matchConstraints</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resourceRules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"apps"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersions</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"v1"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>operations</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"CREATE"</span>,<span style=color:#bbb> </span><span style=color:#b44>"UPDATE"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"deployments"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>validations</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>"object.replicas > 1"</span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># should be "object.spec.replicas > 1"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>message</span>:<span style=color:#bbb> </span><span style=color:#b44>"must be replicated"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>reason</span>:<span style=color:#bbb> </span>Invalid<span style=color:#bbb> </span></span></span></code></pre></div></div></div><p>The status will yield the following information:</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>status</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>typeChecking</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expressionWarnings</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>fieldRef</span>:<span style=color:#bbb> </span>spec.validations[0].expression<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>warning</span>:<span style=color:#bbb> </span>|-<span style=color:#b44;font-style:italic> </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> apps/v1, Kind=Deployment: ERROR: <input>:1:7: undefined field 'replicas' </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> | object.replicas > 1 </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> | ......^</span><span style=color:#bbb> </span></span></span></code></pre></div><p>If multiple resources are matched in <code>spec.matchConstraints</code>, all of matched resources will be checked against. For example, the following policy definition</p><div class="highlight code-sample"><div class=copy-code-icon><a href=https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/validatingadmissionpolicy/typechecking-multiple-match.yaml download=validatingadmissionpolicy/typechecking-multiple-match.yaml><code>validatingadmissionpolicy/typechecking-multiple-match.yaml</code> </a><img src=/images/copycode.svg class=icon-copycode onclick='copyCode("validatingadmissionpolicy-typechecking-multiple-match-yaml")' title="Copy validatingadmissionpolicy/typechecking-multiple-match.yaml to clipboard"></img></div><div class=includecode id=validatingadmissionpolicy-typechecking-multiple-match-yaml><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ValidatingAdmissionPolicy<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>"replica-policy.example.com"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>spec</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>matchConstraints</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resourceRules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"apps"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersions</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"v1"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>operations</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"CREATE"</span>,<span style=color:#bbb> </span><span style=color:#b44>"UPDATE"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"deployments"</span>,<span style=color:#b44>"replicasets"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>validations</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>"object.replicas > 1"</span><span style=color:#bbb> </span><span style=color:#080;font-style:italic># should be "object.spec.replicas > 1"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>message</span>:<span style=color:#bbb> </span><span style=color:#b44>"must be replicated"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>reason</span>:<span style=color:#bbb> </span>Invalid</span></span></code></pre></div></div></div><p>will have multiple types and type checking result of each type in the warning message.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>status</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>typeChecking</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expressionWarnings</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>fieldRef</span>:<span style=color:#bbb> </span>spec.validations[0].expression<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>warning</span>:<span style=color:#bbb> </span>|-<span style=color:#b44;font-style:italic> </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> apps/v1, Kind=Deployment: ERROR: <input>:1:7: undefined field 'replicas' </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> | object.replicas > 1 </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> | ......^ </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> apps/v1, Kind=ReplicaSet: ERROR: <input>:1:7: undefined field 'replicas' </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> | object.replicas > 1 </span></span></span><span style=display:flex><span><span style=color:#b44;font-style:italic> | ......^</span><span style=color:#bbb> </span></span></span></code></pre></div><p>Type Checking has the following limitation:</p><ul><li>No wildcard matching. If <code>spec.matchConstraints.resourceRules</code> contains <code>"*"</code> in any of <code>apiGroups</code>, <code>apiVersions</code> or <code>resources</code>, the types that <code>"*"</code> matches will not be checked.</li><li>The number of matched types is limited to 10. This is to prevent a policy that manually specifying too many types. to consume excessive computing resources. In the order of ascending group, version, and then resource, 11th combination and beyond are ignored.</li><li>Type Checking does not affect the policy behavior in any way. Even if the type checking detects errors, the policy will continue to evaluate. If errors do occur during evaluate, the failure policy will decide its outcome.</li><li>Type Checking does not apply to CRDs, including matched CRD types and reference of paramKind. The support for CRDs will come in future release.</li></ul><h3 id=variable-composition>Variable composition</h3><p>If an expression grows too complicated, or part of the expression is reusable and computationally expensive to evaluate, you can extract some part of the expressions into variables. A variable is a named expression that can be referred later in <code>variables</code> in other expressions.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:green;font-weight:700>spec</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>variables</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>foo<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>"'foo' in object.spec.metadata.labels ? object.spec.metadata.labels['foo'] : 'default'"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>validations</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span>variables.foo == 'bar'<span style=color:#bbb> </span></span></span></code></pre></div><p>A variable is lazily evaluated when it is first referred. Any error that occurs during the evaluation will be reported during the evaluation of the referring expression. Both the result and potential error are memorized and count only once towards the runtime cost.</p><p>The order of variables are important because a variable can refer to other variables that are defined before it. This ordering prevents circular references.</p><p>The following is a more complex example of enforcing that image repo names match the environment defined in its namespace.</p><div class="highlight code-sample"><div class=copy-code-icon><a href=https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/access/image-matches-namespace-environment.policy.yaml download=access/image-matches-namespace-environment.policy.yaml><code>access/image-matches-namespace-environment.policy.yaml</code> </a><img src=/images/copycode.svg class=icon-copycode onclick='copyCode("access-image-matches-namespace-environment-policy-yaml")' title="Copy access/image-matches-namespace-environment.policy.yaml to clipboard"></img></div><div class=includecode id=access-image-matches-namespace-environment-policy-yaml><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:#080;font-style:italic># This policy enforces that all containers of a deployment has the image repo match the environment label of its namespace.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># Except for "exempt" deployments, or any containers that do not belong to the "example.com" organization (e.g. common sidecars).</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># For example, if the namespace has a label of {"environment": "staging"}, all container images must be either staging.example.com/*</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:#080;font-style:italic># or do not contain "example.com" at all, unless the deployment has {"exempt": "true"} label.</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>apiVersion</span>:<span style=color:#bbb> </span>admissionregistration.k8s.io/v1<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>kind</span>:<span style=color:#bbb> </span>ValidatingAdmissionPolicy<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>metadata</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span><span style=color:#b44>"image-matches-namespace-environment.policy.example.com"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb></span><span style=color:green;font-weight:700>spec</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>failurePolicy</span>:<span style=color:#bbb> </span>Fail<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>matchConstraints</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resourceRules</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>apiGroups</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"apps"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>apiVersions</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"v1"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>operations</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"CREATE"</span>,<span style=color:#bbb> </span><span style=color:#b44>"UPDATE"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>resources</span>:<span style=color:#bbb> </span>[<span style=color:#b44>"deployments"</span>]<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>variables</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>environment<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>"'environment' in namespaceObject.metadata.labels ? namespaceObject.metadata.labels['environment'] : 'prod'"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>exempt<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>"'exempt' in object.metadata.labels && object.metadata.labels['exempt'] == 'true'"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>containers<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>"object.spec.template.spec.containers"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>name</span>:<span style=color:#bbb> </span>containersToCheck<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>"variables.containers.filter(c, c.image.contains('example.com/'))"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>validations</span>:<span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span>- <span style=color:green;font-weight:700>expression</span>:<span style=color:#bbb> </span><span style=color:#b44>"variables.exempt || variables.containersToCheck.all(c, c.image.startsWith(variables.environment + '.'))"</span><span style=color:#bbb> </span></span></span><span style=display:flex><span><span style=color:#bbb> </span><span style=color:green;font-weight:700>messageExpression</span>:<span style=color:#bbb> </span><span style=color:#b44>"'only ' + variables.environment + ' images are allowed in namespace ' + namespaceObject.metadata.name"</span></span></span></code></pre></div></div></div><p>With the policy bound to the namespace <code>default</code>, which is labeled <code>environment: prod</code>, the following attempt to create a deployment would be rejected.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>kubectl create deploy --image<span style=color:#666>=</span>dev.example.com/nginx invalid </span></span></code></pre></div><p>The error message is similar to this.</p><div class=highlight><pre tabindex=0 style=background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-console data-lang=console><span style=display:flex><span><span style=color:#888>error: failed to create deployment: deployments.apps "invalid" is forbidden: ValidatingAdmissionPolicy 'image-matches-namespace-environment.policy.example.com' with binding 'demo-binding-test.example.com' denied request: only prod images are allowed in namespace default </span></span></span></code></pre></div></div></main></div></div><footer class="bg-dark py-5 row d-print-none"><div class="container-fluid mx-sm-5"><div class=row><div class="col-5 col-sm-7 text-center order-2 footer-main"><p><span class=copyright-notice>© 2024 The Kubernetes Authors | Documentation Distributed under <a href=https://git.k8s.io/website/LICENSE class=light-text>CC BY 4.0</a></span></p><p><span class=copyright-notice>© 2024 The Linux Foundation ®. All rights reserved. The Linux Foundation has registered trademarks and uses trademarks. For a list of trademarks of The Linux Foundation, please see our <a href=https://www.linuxfoundation.org/trademark-usage class=light-text>Trademark Usage page</a></span></p><p><span class=certification-notice>ICP license: 京ICP备17074266号-3</span></p></div><div class="col col-sm-2 text-xs-center order-1"><ul class="list-inline mb-0 footer-icons"><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title aria-label><a class=text-white target=_blank rel=noopener href=https://youtube.com/kubernetescommunity aria-label><i class="fab fa-youtube"></i></a></li><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title aria-label><a class=text-white target=_blank rel=noopener href=https://discuss.kubernetes.io aria-label><i class="fa fa-envelope"></i></a></li><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title aria-label><a class=text-white target=_blank rel=noopener href=https://serverfault.com/questions/tagged/kubernetes aria-label><i class="fab fa-stack-overflow"></i></a></li><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title aria-label><a class=text-white target=_blank rel=noopener href=https://twitter.com/kubernetesio aria-label><i class="fab fa-twitter"></i></a></li></ul></div><div class="col col-sm-2 text-right text-xs-center order-3"><ul class="list-inline mb-0 footer-icons"><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title aria-label><a class=text-white target=_blank rel=noopener href=https://k8s.dev/ aria-label><i class="fas fa-laptop-code"></i></a></li><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title aria-label><a class=text-white target=_blank rel=noopener href=https://github.com/kubernetes/kubernetes aria-label><i class="fab fa-github"></i></a></li><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title aria-label><a class=text-white target=_blank rel=noopener href=https://slack.k8s.io aria-label><i class="fab fa-slack"></i></a></li><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title aria-label><a class=text-white target=_blank rel=noopener href="https://calendar.google.com/calendar/embed?src=calendar%40kubernetes.io" aria-label><i class="fas fa-calendar-alt"></i></a></li></ul></div></div></div></footer></div><script src=/js/jquery-3.6.0.min.js integrity=sha384-vtXRMe3mGCbOeY7l30aIg8H9p3GdeSe4IFlP6G8JMa7o7lXvnz3GFKzPxzJdPfGK crossorigin=anonymous></script><script src=/js/popper-1.16.1.min.js intregrity=sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN crossorigin=anonymous></script><script src=/js/bootstrap-4.6.1.min.js integrity=sha384-VHvPCCyXqtD5DqJeNxl2dtTyhF78xXNXdkwX1CZeRusQfRKp+tA7hAShOK/B/fQ2 crossorigin=anonymous></script><script src=/js/script.js></script><script src=/js/main.min.b8b5957f4ba9b582bc3696c8cbed20dde33f789d0fe3255f10fdc7adecf38b7e.js integrity="sha256-uLWVf0uptYK8NpbIy+0g3eM/eJ0P4yVfEP3Hrezzi34=" crossorigin=anonymous></script><script>let splitInstance=null;function enableSplitter(e){e.matches?splitInstance||(splitInstance=Split(["#sidebarnav","#maindoc"],{sizes:[20,80],minSize:100})):splitInstance&&(splitInstance.destroy(),splitInstance=null)}const screenWidthMediaQuery=window.matchMedia("(min-width: 768px)"),eleNav=document.getElementById("sidebarnav");eleNav!==null&&(enableSplitter(screenWidthMediaQuery),screenWidthMediaQuery.addListener(enableSplitter))</script></body></html>