+185
-155
@@ -12,20 +12,17 @@ redirect_from:
|
|||||||
{% assign for_k8s_version="v1.4" %}{% include feature-state-beta.md %}
|
{% assign for_k8s_version="v1.4" %}{% include feature-state-beta.md %}
|
||||||
|
|
||||||
|
|
||||||
AppArmor is a Linux kernel security module that can reduce the potential attack surface of an
|
AppArmor is a Linux kernel security module that supplements the standard Linux user and group based
|
||||||
application and provide greater defense in depth for Applications.
|
|
||||||
|
|
||||||
AppArmor supplements the standard Linux user and group based
|
|
||||||
permissions to confine programs to a limited set of resources. AppArmor can be configured for any
|
permissions to confine programs to a limited set of resources. AppArmor can be configured for any
|
||||||
application to reduce its potential attack surface and provide greater defense in depth. It is
|
application to reduce its potential attack surface and provide greater in-depth defense. It is
|
||||||
configured through profiles tuned to whitelist the access needed by a specific program or container,
|
configured through profiles tuned to whitelist the access needed by a specific program or container,
|
||||||
such as Linux capabilities, network access, file permissions, etc. Each profile can be run in either
|
such as Linux capabilities, network access, file permissions, etc. Each profile can be run in either
|
||||||
enforcing mode, which blocks access to disallowed resources, or complain mode, which only reports
|
*enforcing* mode, which blocks access to disallowed resources, or *complain* mode, which only reports
|
||||||
violations.
|
violations.
|
||||||
|
|
||||||
AppArmor can help you to run a more secure deployment by restricting what containers are allowed to
|
AppArmor can help you to run a more secure deployment by restricting what containers are allowed to
|
||||||
do, and /or providing better auditing through system logs. However, it is important to keep in mind
|
do, and/or provide better auditing through system logs. However, it is important to keep in mind
|
||||||
that AppArmor is not a silver bullet, and can only do so much to protect against exploits in your
|
that AppArmor is not a silver bullet and can only do so much to protect against exploits in your
|
||||||
application code. It is important to provide good, restrictive profiles, and harden your
|
application code. It is important to provide good, restrictive profiles, and harden your
|
||||||
applications and cluster from other angles as well.
|
applications and cluster from other angles as well.
|
||||||
|
|
||||||
@@ -33,11 +30,11 @@ applications and cluster from other angles as well.
|
|||||||
|
|
||||||
{% capture objectives %}
|
{% capture objectives %}
|
||||||
|
|
||||||
* One way to load a profile on a node
|
* See an example of how to load a profile on a node
|
||||||
* How to enforce the profile on a Pod
|
* Learn how to enforce the profile on a Pod
|
||||||
* How to check that the profile is loaded
|
* Learn how to check that the profile is loaded
|
||||||
* What happens when a profile is violated
|
* See what happens when a profile is violated
|
||||||
* What happens when a profile cannot be loaded
|
* See what happens when a profile cannot be loaded
|
||||||
|
|
||||||
{% endcapture %}
|
{% endcapture %}
|
||||||
|
|
||||||
@@ -45,53 +42,61 @@ applications and cluster from other angles as well.
|
|||||||
|
|
||||||
Make sure:
|
Make sure:
|
||||||
|
|
||||||
1. **Kubernetes version is at least v1.4**. Kubernetes support for AppArmor was added in
|
1. Kubernetes version is at least v1.4 -- Kubernetes support for AppArmor was added in
|
||||||
v1.4. Kubernetes components older than v1.4 are not aware of the new AppArmor annotations, and
|
v1.4. Kubernetes components older than v1.4 are not aware of the new AppArmor annotations, and
|
||||||
will **silently ignore** any AppArmor settings that are provided. To ensure that your Pods are
|
will **silently ignore** any AppArmor settings that are provided. To ensure that your Pods are
|
||||||
receiving the expected protections, it is important to verify the Kubelet version of your nodes:
|
receiving the expected protections, it is important to verify the Kubelet version of your nodes:
|
||||||
|
|
||||||
$ kubectl get nodes -o=jsonpath=$'{range .items[*]}{@.metadata.name}: {@.status.nodeInfo.kubeletVersion}\n{end}'
|
```
|
||||||
gke-test-default-pool-239f5d02-gyn2: v1.4.0
|
$ kubectl get nodes -o=jsonpath=$'{range .items[*]}{@.metadata.name}: {@.status.nodeInfo.kubeletVersion}\n{end}'
|
||||||
gke-test-default-pool-239f5d02-x1kf: v1.4.0
|
gke-test-default-pool-239f5d02-gyn2: v1.4.0
|
||||||
gke-test-default-pool-239f5d02-xwux: v1.4.0
|
gke-test-default-pool-239f5d02-x1kf: v1.4.0
|
||||||
|
gke-test-default-pool-239f5d02-xwux: v1.4.0
|
||||||
|
```
|
||||||
|
|
||||||
2. **AppArmor kernel module is enabled**. For the Linux kernel to enforce an AppArmor profile, the
|
2. AppArmor kernel module is enabled -- For the Linux kernel to enforce an AppArmor profile, the
|
||||||
AppArmor kernel module must be installed and enabled. Several distributions enable the module by
|
AppArmor kernel module must be installed and enabled. Several distributions enable the module by
|
||||||
default, such as Ubuntu and SUSE, and many others provide optional support. To check whether the
|
default, such as Ubuntu and SUSE, and many others provide optional support. To check whether the
|
||||||
module is enabled, check the `/sys/module/apparmor/parameters/enabled` file:
|
module is enabled, check the `/sys/module/apparmor/parameters/enabled` file:
|
||||||
|
|
||||||
$ cat /sys/module/apparmor/parameters/enabled
|
```
|
||||||
Y
|
$ cat /sys/module/apparmor/parameters/enabled
|
||||||
|
Y
|
||||||
|
```
|
||||||
|
|
||||||
If the Kubelet contains AppArmor support (>= v1.4), it will refuse to run a Pod with AppArmor
|
If the Kubelet contains AppArmor support (>= v1.4), it will refuse to run a Pod with AppArmor
|
||||||
options if the kernel module is not enabled.
|
options if the kernel module is not enabled.
|
||||||
|
|
||||||
*Note: Ubuntu carries many AppArmor patches that have not been merged into the upstream Linux
|
**Note:** Ubuntu carries many AppArmor patches that have not been merged into the upstream Linux
|
||||||
kernel, including patches that add additional hooks and features. Kubernetes has only been
|
kernel, including patches that add additional hooks and features. Kubernetes has only been
|
||||||
tested with the upstream version, and does not promise support for other features.*
|
tested with the upstream version, and does not promise support for other features.
|
||||||
|
|
||||||
3. **Container runtime is Docker**. Currently the only Kubernetes-supported container runtime that
|
3. Container runtime is Docker -- Currently the only Kubernetes-supported container runtime that
|
||||||
also supports AppArmor is Docker. As more runtimes add AppArmor support, the options will be
|
also supports AppArmor is Docker. As more runtimes add AppArmor support, the options will be
|
||||||
expanded. You can verify that your nodes are running docker with:
|
expanded. You can verify that your nodes are running docker with:
|
||||||
|
|
||||||
$ kubectl get nodes -o=jsonpath=$'{range .items[*]}{@.metadata.name}: {@.status.nodeInfo.containerRuntimeVersion}\n{end}'
|
```
|
||||||
gke-test-default-pool-239f5d02-gyn2: docker://1.11.2
|
$ kubectl get nodes -o=jsonpath=$'{range .items[*]}{@.metadata.name}: {@.status.nodeInfo.containerRuntimeVersion}\n{end}'
|
||||||
gke-test-default-pool-239f5d02-x1kf: docker://1.11.2
|
gke-test-default-pool-239f5d02-gyn2: docker://1.11.2
|
||||||
gke-test-default-pool-239f5d02-xwux: docker://1.11.2
|
gke-test-default-pool-239f5d02-x1kf: docker://1.11.2
|
||||||
|
gke-test-default-pool-239f5d02-xwux: docker://1.11.2
|
||||||
|
```
|
||||||
|
|
||||||
If the Kubelet contains AppArmor support (>= v1.4), it will refuse to run a Pod with AppArmor
|
If the Kubelet contains AppArmor support (>= v1.4), it will refuse to run a Pod with AppArmor
|
||||||
options if the runtime is not Docker.
|
options if the runtime is not Docker.
|
||||||
|
|
||||||
4. **Profile is loaded**. AppArmor is applied to a Pod by specifying an AppArmor profile that each
|
4. Profile is loaded -- AppArmor is applied to a Pod by specifying an AppArmor profile that each
|
||||||
container should be run with. If any of the specified profiles is not already loaded in the
|
container should be run with. If any of the specified profiles is not already loaded in the
|
||||||
kernel, the Kubelet (>= v1.4) will reject the Pod. You can view which profiles are loaded on a
|
kernel, the Kubelet (>= v1.4) will reject the Pod. You can view which profiles are loaded on a
|
||||||
node by checking the `/sys/kernel/security/apparmor/profiles` file. For example:
|
node by checking the `/sys/kernel/security/apparmor/profiles` file. For example:
|
||||||
|
|
||||||
$ ssh gke-test-default-pool-239f5d02-gyn2 "sudo cat /sys/kernel/security/apparmor/profiles | sort"
|
```
|
||||||
apparmor-test-deny-write (enforce)
|
$ ssh gke-test-default-pool-239f5d02-gyn2 "sudo cat /sys/kernel/security/apparmor/profiles | sort"
|
||||||
apparmor-test-audit-write (enforce)
|
apparmor-test-deny-write (enforce)
|
||||||
docker-default (enforce)
|
apparmor-test-audit-write (enforce)
|
||||||
k8s-nginx (enforce)
|
docker-default (enforce)
|
||||||
|
k8s-nginx (enforce)
|
||||||
|
```
|
||||||
|
|
||||||
For more details on loading profiles on nodes, see
|
For more details on loading profiles on nodes, see
|
||||||
[Setting up nodes with profiles](#setting-up-nodes-with-profiles).
|
[Setting up nodes with profiles](#setting-up-nodes-with-profiles).
|
||||||
@@ -101,10 +106,12 @@ with AppArmor options if any of the prerequisites are not met. You can also veri
|
|||||||
on nodes by checking the node ready condition message (though this is likely to be removed in a
|
on nodes by checking the node ready condition message (though this is likely to be removed in a
|
||||||
later release):
|
later release):
|
||||||
|
|
||||||
$ kubectl get nodes -o=jsonpath=$'{range .items[*]}{@.metadata.name}: {.status.conditions[?(@.reason=="KubeletReady")].message}\n{end}'
|
```shell
|
||||||
gke-test-default-pool-239f5d02-gyn2: kubelet is posting ready status. AppArmor enabled
|
$ kubectl get nodes -o=jsonpath=$'{range .items[*]}{@.metadata.name}: {.status.conditions[?(@.reason=="KubeletReady")].message}\n{end}'
|
||||||
gke-test-default-pool-239f5d02-x1kf: kubelet is posting ready status. AppArmor enabled
|
gke-test-default-pool-239f5d02-gyn2: kubelet is posting ready status. AppArmor enabled
|
||||||
gke-test-default-pool-239f5d02-xwux: kubelet is posting ready status. AppArmor enabled
|
gke-test-default-pool-239f5d02-x1kf: kubelet is posting ready status. AppArmor enabled
|
||||||
|
gke-test-default-pool-239f5d02-xwux: kubelet is posting ready status. AppArmor enabled
|
||||||
|
```
|
||||||
|
|
||||||
{% endcapture %}
|
{% endcapture %}
|
||||||
|
|
||||||
@@ -112,36 +119,42 @@ later release):
|
|||||||
|
|
||||||
## Securing a Pod
|
## Securing a Pod
|
||||||
|
|
||||||
*Note: AppArmor is currently in beta, so options are specified as annotations. Once support graduates to
|
**Note:** AppArmor is currently in beta, so options are specified as annotations. Once support graduates to
|
||||||
general availability, the annotations will be replaced with first-class fields (more details in
|
general availability, the annotations will be replaced with first-class fields (more details in
|
||||||
[Upgrade path to GA](#upgrade-path-to-general-availability)).*
|
[Upgrade path to GA](#upgrade-path-to-general-availability)).
|
||||||
|
|
||||||
AppArmor profiles are specified *per-container*. To specify the AppArmor profile to run a Pod
|
AppArmor profiles are specified *per-container*. To specify the AppArmor profile to run a Pod
|
||||||
container with, add an annotation to the Pod's metadata:
|
container with, add an annotation to the Pod's metadata:
|
||||||
|
|
||||||
container.apparmor.security.beta.kubernetes.io/<container_name>: <profile_ref>
|
```yaml
|
||||||
|
container.apparmor.security.beta.kubernetes.io/<container_name>: <profile_ref>
|
||||||
|
```
|
||||||
|
|
||||||
Where `<container_name>` is the name of the container to apply the profile to, and `<profile_ref>`
|
Where `<container_name>` is the name of the container to apply the profile to, and `<profile_ref>`
|
||||||
specifies the profile to apply. The `profile_ref` can be one of:
|
specifies the profile to apply. The `profile_ref` can be one of:
|
||||||
|
|
||||||
- `runtime/default` to apply the runtime's default profile.
|
* `runtime/default` to apply the runtime's default profile.
|
||||||
- `localhost/<profile_name>` to apply the profile loaded on the host with the name `<profile_name>`
|
* `localhost/<profile_name>` to apply the profile loaded on the host with the name `<profile_name>`
|
||||||
|
|
||||||
See the [API Reference](#api-reference) for the full details on the annotation and profile name formats.
|
See the [API Reference](#api-reference) for the full details on the annotation and profile name formats.
|
||||||
|
|
||||||
The Kubernetes AppArmor enforcement works by first checking that all the prerequisites have been
|
Kubernetes AppArmor enforcement works by first checking that all the prerequisites have been
|
||||||
met, and then forwarding the profile selection to the container runtime for enforcement. If the
|
met, and then forwarding the profile selection to the container runtime for enforcement. If the
|
||||||
prerequisites have not been met, the Pod will be rejected, and will not run.
|
prerequisites have not been met, the Pod will be rejected, and will not run.
|
||||||
|
|
||||||
To verify that the profile was applied, you can expect to see the AppArmor security option listed in the container created event:
|
To verify that the profile was applied, you can look for the AppArmor security option listed in the container created event:
|
||||||
|
|
||||||
$ kubectl get events | grep Created
|
```shell
|
||||||
22s 22s 1 hello-apparmor Pod spec.containers{hello} Normal Created {kubelet e2e-test-stclair-minion-group-31nt} Created container with docker id 269a53b202d3; Security:[seccomp=unconfined apparmor=k8s-apparmor-example-deny-write]
|
$ kubectl get events | grep Created
|
||||||
|
22s 22s 1 hello-apparmor Pod spec.containers{hello} Normal Created {kubelet e2e-test-stclair-minion-group-31nt} Created container with docker id 269a53b202d3; Security:[seccomp=unconfined apparmor=k8s-apparmor-example-deny-write]
|
||||||
|
```
|
||||||
|
|
||||||
You can also verify directly that the container's root process is running with the correct profile by checking its proc attr:
|
You can also verify directly that the container's root process is running with the correct profile by checking its proc attr:
|
||||||
|
|
||||||
$ kubectl exec <pod_name> cat /proc/1/attr/current
|
```shell
|
||||||
k8s-apparmor-example-deny-write (enforce)
|
$ kubectl exec <pod_name> cat /proc/1/attr/current
|
||||||
|
k8s-apparmor-example-deny-write (enforce)
|
||||||
|
```
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
@@ -150,121 +163,132 @@ You can also verify directly that the container's root process is running with t
|
|||||||
First, we need to load the profile we want to use onto our nodes. The profile we'll use simply
|
First, we need to load the profile we want to use onto our nodes. The profile we'll use simply
|
||||||
denies all file writes:
|
denies all file writes:
|
||||||
|
|
||||||
|
|
||||||
{% include code.html language="text" file="deny-write.profile" ghlink="/docs/tutorials/clusters/deny-write.profile" %}
|
{% include code.html language="text" file="deny-write.profile" ghlink="/docs/tutorials/clusters/deny-write.profile" %}
|
||||||
|
|
||||||
Since we don't know where the Pod will be scheduled, we'll need to load the profile on all our
|
Since we don't know where the Pod will be scheduled, we'll need to load the profile on all our
|
||||||
nodes. For this example we'll just use SSH to install the profiles, but other approaches are
|
nodes. For this example we'll just use SSH to install the profiles, but other approaches are
|
||||||
discussed in [Setting up nodes with profiles](#setting-up-nodes-with-profiles).
|
discussed in [Setting up nodes with profiles](#setting-up-nodes-with-profiles).
|
||||||
|
|
||||||
$ NODES=(
|
```shell
|
||||||
# The SSH-accessible domain names of your nodes
|
$ NODES=(
|
||||||
gke-test-default-pool-239f5d02-gyn2.us-central1-a.my-k8s
|
# The SSH-accessible domain names of your nodes
|
||||||
gke-test-default-pool-239f5d02-x1kf.us-central1-a.my-k8s
|
gke-test-default-pool-239f5d02-gyn2.us-central1-a.my-k8s
|
||||||
gke-test-default-pool-239f5d02-xwux.us-central1-a.my-k8s)
|
gke-test-default-pool-239f5d02-x1kf.us-central1-a.my-k8s
|
||||||
$ for NODE in ${NODES[*]}; do ssh $NODE 'sudo apparmor_parser -q <<EOF
|
gke-test-default-pool-239f5d02-xwux.us-central1-a.my-k8s)
|
||||||
#include <tunables/global>
|
$ for NODE in ${NODES[*]}; do ssh $NODE 'sudo apparmor_parser -q <<EOF
|
||||||
|
#include <tunables/global>
|
||||||
|
|
||||||
profile k8s-apparmor-example-deny-write flags=(attach_disconnected) {
|
profile k8s-apparmor-example-deny-write flags=(attach_disconnected) {
|
||||||
#include <abstractions/base>
|
#include <abstractions/base>
|
||||||
|
|
||||||
file,
|
file,
|
||||||
|
|
||||||
# Deny all file writes.
|
# Deny all file writes.
|
||||||
deny /** w,
|
deny /** w,
|
||||||
}
|
}
|
||||||
EOF'
|
EOF'
|
||||||
done
|
done
|
||||||
|
```
|
||||||
|
|
||||||
Next, we'll run a simple "Hello AppArmor" pod with the deny-write profile:
|
Next, we'll run a simple "Hello AppArmor" pod with the deny-write profile:
|
||||||
|
|
||||||
{% include code.html language="yaml" file="hello-apparmor-pod.yaml" ghlink="/docs/tutorials/clusters/hello-apparmor-pod.yaml" %}
|
{% include code.html language="yaml" file="hello-apparmor-pod.yaml" ghlink="/docs/tutorials/clusters/hello-apparmor-pod.yaml" %}
|
||||||
|
|
||||||
$ kubectl create -f /dev/stdin <<EOF
|
```shell
|
||||||
apiVersion: v1
|
$ kubectl create -f /dev/stdin <<EOF
|
||||||
kind: Pod
|
apiVersion: v1
|
||||||
metadata:
|
kind: Pod
|
||||||
name: hello-apparmor
|
metadata:
|
||||||
annotations:
|
name: hello-apparmor
|
||||||
container.apparmor.security.beta.kubernetes.io/hello: localhost/k8s-apparmor-example-deny-write
|
annotations:
|
||||||
spec:
|
container.apparmor.security.beta.kubernetes.io/hello: localhost/k8s-apparmor-example-deny-write
|
||||||
containers:
|
spec:
|
||||||
- name: hello
|
containers:
|
||||||
image: busybox
|
- name: hello
|
||||||
command: [ "sh", "-c", "echo 'Hello AppArmor!' && sleep 1h" ]
|
image: busybox
|
||||||
EOF
|
command: [ "sh", "-c", "echo 'Hello AppArmor!' && sleep 1h" ]
|
||||||
pod "hello-apparmor" created
|
EOF
|
||||||
|
pod "hello-apparmor" created
|
||||||
|
```
|
||||||
|
|
||||||
If we look at the pod events, we can see that the Pod container was created with the AppArmor
|
If we look at the pod events, we can see that the Pod container was created with the AppArmor
|
||||||
profile "k8s-apparmor-example-deny-write":
|
profile "k8s-apparmor-example-deny-write":
|
||||||
|
|
||||||
$ kubectl get events | grep hello-apparmor
|
```shell
|
||||||
14s 14s 1 hello-apparmor Pod Normal Scheduled {default-scheduler } Successfully assigned hello-apparmor to gke-test-default-pool-239f5d02-gyn2
|
$ kubectl get events | grep hello-apparmor
|
||||||
14s 14s 1 hello-apparmor Pod spec.containers{hello} Normal Pulling {kubelet gke-test-default-pool-239f5d02-gyn2} pulling image "busybox"
|
14s 14s 1 hello-apparmor Pod Normal Scheduled {default-scheduler } Successfully assigned hello-apparmor to gke-test-default-pool-239f5d02-gyn2
|
||||||
13s 13s 1 hello-apparmor Pod spec.containers{hello} Normal Pulled {kubelet gke-test-default-pool-239f5d02-gyn2} Successfully pulled image "busybox"
|
14s 14s 1 hello-apparmor Pod spec.containers{hello} Normal Pulling {kubelet gke-test-default-pool-239f5d02-gyn2} pulling image "busybox"
|
||||||
13s 13s 1 hello-apparmor Pod spec.containers{hello} Normal Created {kubelet gke-test-default-pool-239f5d02-gyn2} Created container with docker id 06b6cd1c0989; Security:[seccomp=unconfined apparmor=k8s-apparmor-example-deny-write]
|
13s 13s 1 hello-apparmor Pod spec.containers{hello} Normal Pulled {kubelet gke-test-default-pool-239f5d02-gyn2} Successfully pulled image "busybox"
|
||||||
13s 13s 1 hello-apparmor Pod spec.containers{hello} Normal Started {kubelet gke-test-default-pool-239f5d02-gyn2} Started container with docker id 06b6cd1c0989
|
13s 13s 1 hello-apparmor Pod spec.containers{hello} Normal Created {kubelet gke-test-default-pool-239f5d02-gyn2} Created container with docker id 06b6cd1c0989; Security:[seccomp=unconfined apparmor=k8s-apparmor-example-deny-write]
|
||||||
|
13s 13s 1 hello-apparmor Pod spec.containers{hello} Normal Started {kubelet gke-test-default-pool-239f5d02-gyn2} Started container with docker id 06b6cd1c0989
|
||||||
|
```
|
||||||
|
|
||||||
We can verify that the container is actually running with that profile by checking its proc attr:
|
We can verify that the container is actually running with that profile by checking its proc attr:
|
||||||
|
|
||||||
$ kubectl exec hello-apparmor cat /proc/1/attr/current
|
```shell
|
||||||
k8s-apparmor-example-deny-write (enforce)
|
$ kubectl exec hello-apparmor cat /proc/1/attr/current
|
||||||
|
k8s-apparmor-example-deny-write (enforce)
|
||||||
|
```
|
||||||
|
|
||||||
Finally, we can see what happens if we try to violate the profile by writing to a file:
|
Finally, we can see what happens if we try to violate the profile by writing to a file:
|
||||||
|
|
||||||
$ kubectl exec hello-apparmor touch /tmp/test
|
```shell
|
||||||
touch: /tmp/test: Permission denied
|
$ kubectl exec hello-apparmor touch /tmp/test
|
||||||
error: error executing remote command: command terminated with non-zero exit code: Error executing in Docker Container: 1
|
touch: /tmp/test: Permission denied
|
||||||
|
error: error executing remote command: command terminated with non-zero exit code: Error executing in Docker Container: 1
|
||||||
|
```
|
||||||
|
|
||||||
To wrap up, let's look at what happens if we try to specify a profile that hasn't been loaded:
|
To wrap up, let's look at what happens if we try to specify a profile that hasn't been loaded:
|
||||||
|
|
||||||
$ kubectl create -f /dev/stdin <<EOF
|
```shell
|
||||||
apiVersion: v1
|
$ kubectl create -f /dev/stdin <<EOF
|
||||||
kind: Pod
|
apiVersion: v1
|
||||||
metadata:
|
kind: Pod
|
||||||
name: hello-apparmor-2
|
metadata:
|
||||||
annotations:
|
name: hello-apparmor-2
|
||||||
container.apparmor.security.beta.kubernetes.io/hello: localhost/k8s-apparmor-example-allow-write
|
annotations:
|
||||||
spec:
|
container.apparmor.security.beta.kubernetes.io/hello: localhost/k8s-apparmor-example-allow-write
|
||||||
containers:
|
spec:
|
||||||
- name: hello
|
containers:
|
||||||
image: busybox
|
- name: hello
|
||||||
command: [ "sh", "-c", "echo 'Hello AppArmor!' && sleep 1h" ]
|
image: busybox
|
||||||
EOF
|
command: [ "sh", "-c", "echo 'Hello AppArmor!' && sleep 1h" ]
|
||||||
pod "hello-apparmor-2" created
|
EOF
|
||||||
|
pod "hello-apparmor-2" created
|
||||||
|
|
||||||
$ kubectl describe pod hello-apparmor-2
|
$ kubectl describe pod hello-apparmor-2
|
||||||
Name: hello-apparmor-2
|
Name: hello-apparmor-2
|
||||||
Namespace: default
|
Namespace: default
|
||||||
Node: gke-test-default-pool-239f5d02-x1kf/
|
Node: gke-test-default-pool-239f5d02-x1kf/
|
||||||
Start Time: Tue, 30 Aug 2016 17:58:56 -0700
|
Start Time: Tue, 30 Aug 2016 17:58:56 -0700
|
||||||
Labels: <none>
|
Labels: <none>
|
||||||
Status: Failed
|
Status: Failed
|
||||||
Reason: AppArmor
|
Reason: AppArmor
|
||||||
Message: Pod Cannot enforce AppArmor: profile "k8s-apparmor-example-allow-write" is not loaded
|
Message: Pod Cannot enforce AppArmor: profile "k8s-apparmor-example-allow-write" is not loaded
|
||||||
IP:
|
IP:
|
||||||
Controllers: <none>
|
Controllers: <none>
|
||||||
Containers:
|
Containers:
|
||||||
hello:
|
hello:
|
||||||
Image: busybox
|
Image: busybox
|
||||||
Port:
|
Port:
|
||||||
Command:
|
Command:
|
||||||
sh
|
sh
|
||||||
-c
|
-c
|
||||||
echo 'Hello AppArmor!' && sleep 1h
|
echo 'Hello AppArmor!' && sleep 1h
|
||||||
Requests:
|
Requests:
|
||||||
cpu: 100m
|
cpu: 100m
|
||||||
Environment Variables: <none>
|
Environment Variables: <none>
|
||||||
Volumes:
|
Volumes:
|
||||||
default-token-dnz7v:
|
default-token-dnz7v:
|
||||||
Type: Secret (a volume populated by a Secret)
|
Type: Secret (a volume populated by a Secret)
|
||||||
SecretName: default-token-dnz7v
|
SecretName: default-token-dnz7v
|
||||||
QoS Tier: Burstable
|
QoS Tier: Burstable
|
||||||
Events:
|
Events:
|
||||||
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
|
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
|
||||||
--------- -------- ----- ---- ------------- -------- ------ -------
|
--------- -------- ----- ---- ------------- -------- ------ -------
|
||||||
23s 23s 1 {default-scheduler } Normal Scheduled Successfully assigned hello-apparmor-2 to e2e-test-stclair-minion-group-t1f5
|
23s 23s 1 {default-scheduler } Normal Scheduled Successfully assigned hello-apparmor-2 to e2e-test-stclair-minion-group-t1f5
|
||||||
23s 23s 1 {kubelet e2e-test-stclair-minion-group-t1f5} Warning AppArmor Cannot enforce AppArmor: profile "k8s-apparmor-example-allow-write" is not loaded
|
23s 23s 1 {kubelet e2e-test-stclair-minion-group-t1f5} Warning AppArmor Cannot enforce AppArmor: profile "k8s-apparmor-example-allow-write" is not loaded
|
||||||
|
```
|
||||||
|
|
||||||
Note the pod status is Failed, with a helpful error message: `Pod Cannot enforce AppArmor: profile
|
Note the pod status is Failed, with a helpful error message: `Pod Cannot enforce AppArmor: profile
|
||||||
"k8s-apparmor-example-allow-write" is not loaded`. An event was also recorded with the same message.
|
"k8s-apparmor-example-allow-write" is not loaded`. An event was also recorded with the same message.
|
||||||
@@ -276,12 +300,12 @@ Note the pod status is Failed, with a helpful error message: `Pod Cannot enforce
|
|||||||
Kubernetes does not currently provide any native mechanisms for loading AppArmor profiles onto
|
Kubernetes does not currently provide any native mechanisms for loading AppArmor profiles onto
|
||||||
nodes. There are lots of ways to setup the profiles though, such as:
|
nodes. There are lots of ways to setup the profiles though, such as:
|
||||||
|
|
||||||
- Through a [DaemonSet](/docs/concepts/workloads/controllers/daemonset/) that runs a Pod on each node to
|
* Through a [DaemonSet](/docs/concepts/workloads/controllers/daemonset/) that runs a Pod on each node to
|
||||||
ensure the correct profiles are loaded. An example implementation can be found
|
ensure the correct profiles are loaded. An example implementation can be found
|
||||||
[here](https://github.com/kubernetes/contrib/tree/master/apparmor/loader).
|
[here](https://github.com/kubernetes/contrib/tree/master/apparmor/loader).
|
||||||
- At node initialization time, using your node initialization scripts (e.g. Salt, Ansible, etc.) or
|
* At node initialization time, using your node initialization scripts (e.g. Salt, Ansible, etc.) or
|
||||||
image.
|
image.
|
||||||
- By copying the profiles to each node and loading them through SSH, as demonstrated in the
|
* By copying the profiles to each node and loading them through SSH, as demonstrated in the
|
||||||
[Example](#example).
|
[Example](#example).
|
||||||
|
|
||||||
The scheduler is not aware of which profiles are loaded onto which node, so the full set of profiles
|
The scheduler is not aware of which profiles are loaded onto which node, so the full set of profiles
|
||||||
@@ -295,13 +319,17 @@ node with the required profile.
|
|||||||
If the PodSecurityPolicy extension is enabled, cluster-wide AppArmor restrictions can be applied. To
|
If the PodSecurityPolicy extension is enabled, cluster-wide AppArmor restrictions can be applied. To
|
||||||
enable the PodSecurityPolicy, two flags must be set on the `apiserver`:
|
enable the PodSecurityPolicy, two flags must be set on the `apiserver`:
|
||||||
|
|
||||||
--admission-control=PodSecurityPolicy[,others...]
|
```
|
||||||
--runtime-config=extensions/v1beta1/podsecuritypolicy[,others...]
|
--admission-control=PodSecurityPolicy[,others...]
|
||||||
|
--runtime-config=extensions/v1beta1/podsecuritypolicy[,others...]
|
||||||
|
```
|
||||||
|
|
||||||
With the extension enabled, the AppArmor options can be specified as annotations on the PodSecurityPolicy:
|
With the extension enabled, the AppArmor options can be specified as annotations on the PodSecurityPolicy:
|
||||||
|
|
||||||
apparmor.security.beta.kubernetes.io/defaultProfileName: <profile_ref>
|
```yaml
|
||||||
apparmor.security.beta.kubernetes.io/allowedProfileNames: <profile_ref>[,others...]
|
apparmor.security.beta.kubernetes.io/defaultProfileName: <profile_ref>
|
||||||
|
apparmor.security.beta.kubernetes.io/allowedProfileNames: <profile_ref>[,others...]
|
||||||
|
```
|
||||||
|
|
||||||
The default profile name option specifies the profile to apply to containers by default when none is
|
The default profile name option specifies the profile to apply to containers by default when none is
|
||||||
specified. The allowed profile names option specifies a list of profiles that Pod containers are
|
specified. The allowed profile names option specifies a list of profiles that Pod containers are
|
||||||
@@ -313,7 +341,9 @@ specification.
|
|||||||
|
|
||||||
If you do not want AppArmor to be available on your cluster, it can be disabled by a command-line flag:
|
If you do not want AppArmor to be available on your cluster, it can be disabled by a command-line flag:
|
||||||
|
|
||||||
--feature-gates=AppArmor=false
|
```
|
||||||
|
--feature-gates=AppArmor=false
|
||||||
|
```
|
||||||
|
|
||||||
When disabled, any Pod that includes an AppArmor profile will fail validation with a "Forbidden"
|
When disabled, any Pod that includes an AppArmor profile will fail validation with a "Forbidden"
|
||||||
error. Note that by default docker always enables the "docker-default" profile on non-privileged
|
error. Note that by default docker always enables the "docker-default" profile on non-privileged
|
||||||
@@ -343,10 +373,10 @@ explicitly reject the annotations for at least 2 releases after that.
|
|||||||
Getting AppArmor profiles specified correctly can be a tricky business. Fortunately there are some
|
Getting AppArmor profiles specified correctly can be a tricky business. Fortunately there are some
|
||||||
tools to help with that:
|
tools to help with that:
|
||||||
|
|
||||||
- `aa-genprof` and `aa-logprof` generate profile rules by monitoring an application's activity and
|
* `aa-genprof` and `aa-logprof` generate profile rules by monitoring an application's activity and
|
||||||
logs, and admitting the actions it takes. Further instructions are provided by the
|
logs, and admitting the actions it takes. Further instructions are provided by the
|
||||||
[AppArmor documentation](http://wiki.apparmor.net/index.php/Profiling_with_tools).
|
[AppArmor documentation](http://wiki.apparmor.net/index.php/Profiling_with_tools).
|
||||||
- [bane](https://github.com/jfrazelle/bane) is an AppArmor profile generator for Docker that uses a
|
* [bane](https://github.com/jfrazelle/bane) is an AppArmor profile generator for Docker that uses a
|
||||||
simplified profile language.
|
simplified profile language.
|
||||||
|
|
||||||
It is recommended to run your application through Docker on a development workstation to generate
|
It is recommended to run your application through Docker on a development workstation to generate
|
||||||
@@ -361,7 +391,7 @@ logs or through `journalctl`. More information is provided in
|
|||||||
|
|
||||||
## API Reference
|
## API Reference
|
||||||
|
|
||||||
**Pod Annotation**:
|
### Pod Annotation
|
||||||
|
|
||||||
Specifying the profile a container will run with:
|
Specifying the profile a container will run with:
|
||||||
|
|
||||||
@@ -370,7 +400,7 @@ Specifying the profile a container will run with:
|
|||||||
A separate profile can be specified for each container in the Pod.
|
A separate profile can be specified for each container in the Pod.
|
||||||
- **value**: a profile reference, described below
|
- **value**: a profile reference, described below
|
||||||
|
|
||||||
**Profile Reference**:
|
### Profile Reference
|
||||||
|
|
||||||
- `runtime/default`: Refers to the default runtime profile.
|
- `runtime/default`: Refers to the default runtime profile.
|
||||||
- Equivalent to not specifying a profile (without a PodSecurityPolicy default), except it still
|
- Equivalent to not specifying a profile (without a PodSecurityPolicy default), except it still
|
||||||
@@ -384,17 +414,17 @@ Specifying the profile a container will run with:
|
|||||||
|
|
||||||
Any other profile reference format is invalid.
|
Any other profile reference format is invalid.
|
||||||
|
|
||||||
**PodSecurityPolicy Annotations**
|
### PodSecurityPolicy Annotations
|
||||||
|
|
||||||
Specifying the default profile to apply to containers when none is provided:
|
Specifying the default profile to apply to containers when none is provided:
|
||||||
|
|
||||||
- **key**: `apparmor.security.beta.kubernetes.io/defaultProfileName`
|
* **key**: `apparmor.security.beta.kubernetes.io/defaultProfileName`
|
||||||
- **value**: a profile reference, described above
|
* **value**: a profile reference, described above
|
||||||
|
|
||||||
Specifying the list of profiles Pod containers is allowed to specify:
|
Specifying the list of profiles Pod containers is allowed to specify:
|
||||||
|
|
||||||
- **key**: `apparmor.security.beta.kubernetes.io/allowedProfileNames`
|
* **key**: `apparmor.security.beta.kubernetes.io/allowedProfileNames`
|
||||||
- **value**: a comma-separated list of profile references (described above)
|
* **value**: a comma-separated list of profile references (described above)
|
||||||
- Although an escaped comma is a legal character in a profile name, it cannot be explicitly
|
- Although an escaped comma is a legal character in a profile name, it cannot be explicitly
|
||||||
allowed here
|
allowed here
|
||||||
|
|
||||||
@@ -404,8 +434,8 @@ Specifying the list of profiles Pod containers is allowed to specify:
|
|||||||
|
|
||||||
Additional resources:
|
Additional resources:
|
||||||
|
|
||||||
- [Quick guide to the AppArmor profile language](http://wiki.apparmor.net/index.php/QuickProfileLanguage)
|
* [Quick guide to the AppArmor profile language](http://wiki.apparmor.net/index.php/QuickProfileLanguage)
|
||||||
- [AppArmor core policy reference](http://wiki.apparmor.net/index.php/ProfileLanguage)
|
* [AppArmor core policy reference](http://wiki.apparmor.net/index.php/ProfileLanguage)
|
||||||
|
|
||||||
{% endcapture %}
|
{% endcapture %}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user