Sooner or later, the operation of any system raises the question of security: ensuring authentication, separation of rights, auditing and other tasks.
Many solutions have already been created for Kubernetes that can achieve compliance with standards even in very demanding environments ... The same material is devoted to the basic security aspects implemented within the framework of K8s built-in mechanisms. First of all, it will be useful for those who begin to get acquainted with Kubernetes - as a starting point for studying security issues.
Authentication
Kubernetes has two types of users:
- Service Accounts - accounts managed by the Kubernetes API;
- Users - βnormalβ users controlled by external, independent services.
The main difference between these types is that for Service Accounts there are special objects in the Kubernetes API (they are called
ServiceAccounts
) that are tied to the namespace and set of authorization data stored in the cluster in objects of type Secrets. Such users (Service Accounts) are intended primarily for managing access rights to the Kubernetes API processes running in a Kubernetes cluster.
Ordinary Users do not have entries in the Kubernetes API: they must be managed by external mechanisms. They are intended for people or processes living outside the cluster.
Each request to the API is bound either to the Service Account, or to the User, or is considered anonymous.
User authentication data includes:
- Username - username (case sensitive!);
- UID is a machine-readable user identification string that is βmore consistent and unique than the usernameβ;
- Groups - a list of groups to which the user belongs;
- Extra - additional fields that can be used by the authorization mechanism.
Kubernetes can use a large number of authentication mechanisms: X509 certificates, Bearer tokens, authentication proxies, HTTP Basic Auth. Using these mechanisms, a large number of authorization schemes can be implemented: from a static file with passwords to OpenID OAuth2.
Moreover, multiple authorization schemes are allowed at the same time. By default, the cluster uses:
- service account tokens - for Service Accounts;
- X509 - for Users.
The question about the management of ServiceAccounts is beyond the scope of this article, but I recommend starting to learn more about this issue from the
official documentation page . We will consider in more detail the issue of the operation of X509 certificates.
Certificates for users (X.509)
The classic way of working with certificates involves:
- key generation:
mkdir -p ~/mynewuser/.certs/ openssl genrsa -out ~/.certs/mynewuser.key 2048
- certificate request generation:
openssl req -new -key ~/.certs/mynewuser.key -out ~/.certs/mynewuser.csr -subj "/CN=mynewuser/O=company"
- processing a certificate request using Kubernetes cluster CA keys, obtaining a user certificate (to obtain a certificate, you need to use an account that has access to the Kubernetes cluster certificate authority key, which is located in
/etc/kubernetes/pki/ca.key
by default):
openssl x509 -req -in ~/.certs/mynewuser.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out ~/.certs/mynewuser.crt -days 500
- creating a configuration file:
- cluster description (specify the address and location of the CA certificate file of the particular cluster installation):
kubectl config set-cluster kubernetes --certificate-authority=/etc/kubernetes/pki/ca.crt --server=https://192.168.100.200:6443
- or - if not the recommended option - you can omit the root certificate (then kubectl will not check the correctness of the api-server cluster):
kubectl config set-cluster kubernetes --insecure-skip-tls-verify=true --server=https://192.168.100.200:6443
- adding a user to the configuration file:
kubectl config set-credentials mynewuser --client-certificate=.certs/mynewuser.crt --client-key=.certs/mynewuser.key
- adding context:
kubectl config set-context mynewuser-context --cluster=kubernetes --namespace=target-namespace --user=mynewuser
- default context assignment:
kubectl config use-context mynewuser-context
After the above manipulations, a config of the form will be created in the
.kube/config
file:
apiVersion: v1 clusters: - cluster: certificate-authority: /etc/kubernetes/pki/ca.crt server: https://192.168.100.200:6443 name: kubernetes contexts: - context: cluster: kubernetes namespace: target-namespace user: mynewuser name: mynewuser-context current-context: mynewuser-context kind: Config preferences: {} users: - name: mynewuser user: client-certificate: /home/mynewuser/.certs/mynewuser.crt client-key: /home/mynewuser/.certs/mynewuser.key
To facilitate the transfer of the config between accounts and servers, it is useful to edit the values ββof the following keys:
-
certificate-authority
-
client-certificate
-
client-key
To do this, you can encode the files indicated in them using base64 and register them in the config by adding the suffix
-data
to the name of the keys, i.e. getting
certificate-authority-data
, etc.
Certificates with kubeadm
With
Kubernetes 1.15, working with certificates has become much easier thanks to the alpha version of its support in
the kubeadm utility . For example, here is how the generation of a configuration file with user keys may now look:
kubeadm alpha kubeconfig user --client-name=mynewuser --apiserver-advertise-address 192.168.100.200
NB : The required advertise address can be viewed in the api-server config, which is located at /etc/kubernetes/manifests/kube-apiserver.yaml
by default.
The resulting config will be output to stdout. It must be saved in
~/.kube/config
user account or in the file specified in the
KUBECONFIG
environment
KUBECONFIG
.
Dig deeper
For those wishing to better understand the issues described:
Login
An authenticated account does not have permission to act in a cluster by default. Kubernetes has an authorization mechanism for granting permissions.
Prior to version 1.6, Kubernetes used an authentication type called
ABAC (Attribute-based access control). Details about it can be found in the
official documentation . This approach is currently considered legacy, but you can still use it at the same time as other types of authorization.
The actual (and more flexible) way of dividing cluster access rights is called
RBAC (
Role-based access control ). It has been declared stable since
Kubernetes 1.8 . RBAC implements a rights model that prohibits anything that is not explicitly permitted.
To enable RBAC , you need to run Kubernetes api-server with the
--authorization-mode=RBAC
option. Parameters are set in the manifest with the api-server configuration, which is by default located on the path
/etc/kubernetes/manifests/kube-apiserver.yaml
, in the
command
section. However, RBAC is already enabled by default, so you probably should not worry about this: you can verify this by the value of
authorization-mode
(in the already mentioned
kube-apiserver.yaml
). By the way, among its values ββthere may be other types of authorization (
node
,
webhook
,
always allow
), but we will leave them outside the scope of the material.
By the way, we have already published
an article with a fairly detailed story about the principles and features of working with RBAC, so I will further limit myself to a brief listing of the basics and examples.
The following API entities are used to control access to Kubernetes via RBAC:
-
Role
and ClusterRole
are roles that describe privileges: -
Role
allows you to describe rights within a namespace; -
ClusterRole
- within the cluster, including cluster-specific objects such as nodes, non-resources urls (i.e. not related to Kubernetes resources - for example, /version
, /logs
, /api*
); -
RoleBinding
and ClusterRoleBinding
- serves to bind Role
and ClusterRole
to a user, user group or ServiceAccount.
Entities Role and RoleBinding are limited by namespace, i.e. must be within the same namespace. However, RoleBinding can refer to ClusterRole, which allows you to create a set of standard permissions and control access using them.
Roles describe rights using rule sets containing:
- API groups - see the official documentation for apiGroups and the output of
kubectl api-resources
; - resources ( resources :
pod
, namespace
, deployment
, etc.); - verbs ( verbs :
set
, update
, etc.). - resource names (
resourceNames
) - for the case when you need to provide access to a specific resource, and not to all resources of this type.
A more detailed discussion of authorization in Kubernetes can be found on the
official documentation page. Instead, (or rather, in addition to this), I will give examples that illustrate its work.
RBAC Entity Examples
A simple
Role
that allows you to get the list and status of pods and monitor them in the
target-namespace
:
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: namespace: target-namespace name: pod-reader rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "watch", "list"]
An example of
ClusterRole
, which allows you to get a list and status of pods and monitor them throughout the cluster:
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: # "namespace" , ClusterRole name: pod-reader rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "watch", "list"]
An example of
RoleBinding
, which allows the user
mynewuser
"read" pods in the
my-namespace
:
apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: read-pods namespace: target-namespace subjects: - kind: User name: mynewuser # ! apiGroup: rbac.authorization.k8s.io roleRef: kind: Role # βRoleβ βClusterRoleβ name: pod-reader # Role, namespace, # ClusterRole, # apiGroup: rbac.authorization.k8s.io
Event Audit
Schematically, the Kubernetes architecture can be represented as follows:
The key component of Kubernetes, which is responsible for processing requests, is
api-server . All operations on the cluster pass through it. Read more about these internal mechanisms in the article β
What happens in Kubernetes when kubectl run starts? ".
System audit is an interesting feature in Kubernetes, which is turned off by default. It allows you to log all calls to the Kubernetes API. As you can easily guess, through this API all actions related to monitoring and changing the state of the cluster are performed. A good description of its features can (as usual) be found in the
official K8s
documentation . Next, I will try to present the topic in a simpler language.
So,
to enable auditing , we need to pass three required parameters to the container in the api-server, which are described in more detail below:
-
--audit-policy-file=/etc/kubernetes/policies/audit-policy.yaml
-
--audit-log-path=/var/log/kube-audit/audit.log
-
--audit-log-format=json
In addition to these three necessary parameters, there are many additional settings related to auditing: from log rotation to webhook descriptions. Example log rotation parameters:
-
--audit-log-maxbackup=10
-
--audit-log-maxsize=100
-
--audit-log-maxage=7
But we will not dwell on them in more detail - you can find all the details in the
documentation for kube-apiserver .
As already mentioned, all parameters are set in the manifest with the api-server configuration (by default
/etc/kubernetes/manifests/kube-apiserver.yaml
), in the
command
section. Let's go back to the 3 required parameters and analyze them:
-
audit-policy-file
- path to the YAML file with the description of the audit policy. We will return to its contents, but for now I note that the file should be accessible for reading by the api-server process. Therefore, it is necessary to mount it inside the container, for which you can add the following code to the appropriate sections of the config:
volumeMounts: - mountPath: /etc/kubernetes/policies name: policies readOnly: true volumes: - hostPath: path: /etc/kubernetes/policies type: DirectoryOrCreate name: policies
-
audit-log-path
- path to the log file. The path should also be accessible to the api-server process, therefore, we similarly describe its mounting:
volumeMounts: - mountPath: /var/log/kube-audit name: logs readOnly: false volumes: - hostPath: path: /var/log/kube-audit type: DirectoryOrCreate name: logs
-
audit-log-format
- format of the audit log. By default, this is json
, but the legacy text format is also available.
Audit policy
Now about the mentioned file with the description of the logging policy. The first concept of audit policy is
level
, the
level of logging . They are as follows:
-
None
- do not log; -
Metadata
- log request metadata: user, request time, target resource (pod, namespace, etc.), action type (verb), etc .; -
Request
- log metadata and request body; -
RequestResponse
- RequestResponse
metadata, request body and response body.
The last two levels (
Request
and
RequestResponse
) do not log requests that did not access resources (calls to so-called non-resources urls).
Also, all requests go through
several stages :
-
RequestReceived
- the stage when the request is received by the handler and has not yet been transferred further along the chain of handlers; -
ResponseStarted
- response headers sent but before sending the response body. Generated for long queries (e.g. watch
); -
ResponseComplete
- response body sent, more information will not be sent; -
Panic
- events are generated when an emergency is detected.
You can use
omitStages
to skip any stages.
In the policy file, we can describe several sections with different levels of logging. The first matching rule found in the policy description will apply.
The kubelet daemon monitors the manifest change with the api-server configuration, and if any is detected, it restarts the container with api-server. But there is an important detail:
they will ignore changes to the policy file . After making changes to the policy file, you will need to restart api-server manually. Since api-server is running as a
static pod , the
kubectl delete
command
kubectl delete
not restart it. You have to manually make
docker stop
on kube-masters where the audit policy has been changed:
docker stop $(docker ps | grep k8s_kube-apiserver | awk '{print $1}')
When you enable auditing, it is important to remember that
the load on kube-apiserver increases . In particular, memory consumption for storing the context of requests is increasing. Logging starts only after sending the response header. Also, the load depends on the configuration of the audit policy.
Policy Examples
Let's analyze the structure of policy files using examples.
Here is a simple
policy
file to log everything at the
Metadata
level:
apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata
You can specify a list of users (
Users
and
ServiceAccounts
) and user groups in the policy. For example, this is how we will ignore system users, but log everything else at the
Request
level:
apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: None userGroups: - "system:serviceaccounts" - "system:nodes" users: - "system:anonymous" - "system:apiserver" - "system:kube-controller-manager" - "system:kube-scheduler" - level: Request
It is also possible to describe the target:
-
namespaces
- verbs ( verbs :
get
, update
, delete
and others); - resources ( resources , namely:
pod
, configmaps
, etc.) and resource groups ( apiGroups
).
Note! Resources and resource groups (API groups, i.e. apiGroups), as well as their versions installed in the cluster, can be obtained using the commands:
kubectl api-resources kubectl api-versions
The following audit policy is provided as a demonstration of best practices in the
Alibaba Cloud documentation :
apiVersion: audit.k8s.io/v1beta1 kind: Policy # RequestReceived omitStages: - "RequestReceived" rules: # , : - level: None users: ["system:kube-proxy"] verbs: ["watch"] resources: - group: "" # api group , # Kubernetes, βcoreβ resources: ["endpoints", "services"] - level: None users: ["system:unsecured"] namespaces: ["kube-system"] verbs: ["get"] resources: - group: "" # core resources: ["configmaps"] - level: None users: ["kubelet"] verbs: ["get"] resources: - group: "" # core resources: ["nodes"] - level: None userGroups: ["system:nodes"] verbs: ["get"] resources: - group: "" # core resources: ["nodes"] - level: None users: - system:kube-controller-manager - system:kube-scheduler - system:serviceaccount:kube-system:endpoint-controller verbs: ["get", "update"] namespaces: ["kube-system"] resources: - group: "" # core resources: ["endpoints"] - level: None users: ["system:apiserver"] verbs: ["get"] resources: - group: "" # core resources: ["namespaces"] # read-only URLs: - level: None nonResourceURLs: - /healthz* - /version - /swagger* # , ββ: - level: None resources: - group: "" # core resources: ["events"] # Secret, ConfigMap TokenReview , # - level: Metadata resources: - group: "" # core resources: ["secrets", "configmaps"] - group: authentication.k8s.io resources: ["tokenreviews"] # get, list watch ; - level: Request verbs: ["get", "list", "watch"] resources: - group: "" # core - group: "admissionregistration.k8s.io" - group: "apps" - group: "authentication.k8s.io" - group: "authorization.k8s.io" - group: "autoscaling" - group: "batch" - group: "certificates.k8s.io" - group: "extensions" - group: "networking.k8s.io" - group: "policy" - group: "rbac.authorization.k8s.io" - group: "settings.k8s.io" - group: "storage.k8s.io" # API - level: RequestResponse resources: - group: "" # core - group: "admissionregistration.k8s.io" - group: "apps" - group: "authentication.k8s.io" - group: "authorization.k8s.io" - group: "autoscaling" - group: "batch" - group: "certificates.k8s.io" - group: "extensions" - group: "networking.k8s.io" - group: "policy" - group: "rbac.authorization.k8s.io" - group: "settings.k8s.io" - group: "storage.k8s.io" # - level: Metadata
Another good audit policy example is the
profile used in GCE .
For quick response to audit events, it is possible to
describe a webhook . This issue is disclosed in the
official documentation , I will leave it outside the scope of this article.
Summary
The article provides an overview of the basic security mechanisms in Kubernetes clusters that allow you to create personalized user accounts, share their rights, and register their actions. I hope it is useful to those who are faced with such questions in theory or already in practice. I also recommend that you read the list of other materials on the topic of security in Kubernetes, which is listed in the "PS" - perhaps among them you will find the necessary details on issues that are relevant to you.
PS
Read also in our blog: