Skip to content
This repository has been archived by the owner on Apr 17, 2019. It is now read-only.

Commit

Permalink
Merge pull request #1658 from timstclair/aa-loader
Browse files Browse the repository at this point in the history
AppArmor loader DaemonSet
  • Loading branch information
dchen1107 authored Sep 1, 2016
2 parents 376e7d8 + 0b172a3 commit 562efc7
Show file tree
Hide file tree
Showing 14 changed files with 2,115 additions and 0 deletions.
47 changes: 47 additions & 0 deletions apparmor/loader/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Copyright 2016 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

FROM alpine:3.4
MAINTAINER [email protected]

ENV GLIBC_VERSION "2.23-r3"

RUN apk --no-cache add \
binutils-libs \
ca-certificates \
libgcc \
wget

# Install the 3p glibc apk.
RUN wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://raw.githubusercontent.com/sgerrand/alpine-pkg-glibc/master/sgerrand.rsa.pub && \
wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/${GLIBC_VERSION}/glibc-${GLIBC_VERSION}.apk && \
wget https://github.com/andyshinn/alpine-pkg-glibc/releases/download/${GLIBC_VERSION}/glibc-bin-${GLIBC_VERSION}.apk && \
apk add glibc-${GLIBC_VERSION}.apk glibc-bin-${GLIBC_VERSION}.apk && \
rm -rf glibc{,-bin}-${GLIBC_VERSION}.apk

# The apparmor package includes a lot of unnecessary dependencies, so we manually copy the necessary
# files.
ADD libapparmor.so.1 /usr/lib/libapparmor.so.1
ADD apparmor_parser /sbin/apparmor_parser

# Reload the libraries.
RUN /usr/glibc-compat/sbin/ldconfig /lib /usr/glibc-compat/lib

# Main binary.
ADD loader /usr/bin/loader

ENTRYPOINT ["/usr/bin/loader", "-logtostderr", "-v=2"]

# Default directory to watch.
CMD ["/profiles"]
14 changes: 14 additions & 0 deletions apparmor/loader/Godeps/Godeps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions apparmor/loader/Godeps/Readme

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions apparmor/loader/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Copyright 2016 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

VERSION ?= dev

loader: loader.go
CGO_ENABLED=0 go build

.PHONY: docker
docker: loader
ifeq ("", "$(wildcard apparmor_parser)")
$(error Missing required apparmor_parser binary)
endif
ifeq ("", "$(wildcard libapparmor.so.1)")
$(error Missing required libapparmor.so.1 library)
endif
docker build -t google/apparmor-loader:$(VERSION) -f Dockerfile .

.PHONY: clean
clean:
rm -f loader
67 changes: 67 additions & 0 deletions apparmor/loader/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# AppArmor Profile Loader

This is a small proof-of-concept daemon to demonstrate how AppArmor profiles can be loaded onto
nodes of a Kubernetes cluster. It is not considered production ready, nor will it be supported as a
long-term solution.

## Running the AppArmor Profile Loader

The [example-daemon.yaml](example-daemon.yaml) provides an example manifest for running the loader
as a cluster DaemonSet. In this example, the loader runs in a DaemonSet pod on each node in the
cluster, and periodically (every 30 seconds) polls for new profiles in the `apparmor-profiles`
configmap ([example manifest](example-configmap.yaml)). It is recommended to run the Daemon and
ConfigMap in a separate, restricted namespace:

$ kubectl create -f example-namespace.yaml
$ kubectl create -f example-configmap.yaml # Includes the k8s-nginx profile
$ kubectl create -f example-daemon.yaml

Check that the profile was loaded:

$ POD=$(kubectl --namespace apparmor get pod -o jsonpath="{.items[0].metadata.name}")
$ kubectl --namespace apparmor logs $POD
I0829 22:48:24.917263 1 loader.go:139] Polling /profiles every 30s
I0829 22:48:24.954295 1 loader.go:196] Loading profiles from /profiles/k8s-nginx:
Addition succeeded for "k8s-nginx".
I0829 22:48:24.954328 1 loader.go:100] Successfully loaded profiles: [k8s-nginx]

Trying running a pod with the loaded profile (requires Kubernetes >= v1.4):

$ kubectl create -f example-pod.yaml
# Verify that it's running with the new profile:
$ kubectl exec nginx-apparmor cat /proc/1/attr/current
k8s-nginx (enforce)
$ kubectl exec nginx-apparmor touch /tmp/foo
touch: cannot touch '/tmp/foo': Permission denied
error: error executing remote command: command terminated with non-zero exit code: Error executing in Docker Container: 1


### Standalone

The loader go binary can also be run as a standalone binary on the host. It must be run with root
priveledges:

sudo loader -logtostderr /path/to/profile/dir

Alternatively, it can be run with the supplied loader docker image:

PROFILES_PATH=/path/to/profile/dir
sudo docker run \
--privileged \
--detach=true \
--volume=/sys:/sys:ro \
--volume=/etc/apparmor.d:/etc/apparmor.d:ro \
--volume=$PROFILES_PATH:/profiles:ro \
--name=aa-loader \
google/apparmor-loader:latest

## Build the loader

The loader binary is a simple go program, and can be built with `make loader`. Building the Docker
image requires you to supply an `apparmor_parser` binary and `libapparmor.so.1` library. These can
be built from the source at https://code.launchpad.net/apparmor, or copied from a prebuilt package.

## Limitations

The loader will not unload profiles that are removed, and will not update profiles that are changed.
This is by design, since there are nuanced issues with changing profiles that are in use.
76 changes: 76 additions & 0 deletions apparmor/loader/example-configmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# An example ConfigMap demonstrating how profiles can be stored as Kubernetes objects, and loaded by
# the apparmor-loader DaemonSet.

apiVersion: v1
kind: ConfigMap
metadata:
name: apparmor-profiles
namespace: apparmor
data:
# Filename k8s-nginx maps to the definition of the nginx profile.
k8s-nginx: |-
#include <tunables/global>
# From https://github.com/jfrazelle/bane/blob/master/docker-nginx-sample
profile k8s-nginx flags=(attach_disconnected,mediate_deleted) {
#include <abstractions/base>
network inet tcp,
network inet udp,
network inet icmp,
deny network raw,
deny network packet,
file,
umount,
deny /bin/** wl,
deny /boot/** wl,
deny /dev/** wl,
deny /etc/** wl,
deny /home/** wl,
deny /lib/** wl,
deny /lib64/** wl,
deny /media/** wl,
deny /mnt/** wl,
deny /opt/** wl,
deny /proc/** wl,
deny /root/** wl,
deny /sbin/** wl,
deny /srv/** wl,
deny /tmp/** wl,
deny /sys/** wl,
deny /usr/** wl,
audit /** w,
/var/run/nginx.pid w,
/usr/sbin/nginx ix,
deny /bin/dash mrwklx,
deny /bin/sh mrwklx,
deny /usr/bin/top mrwklx,
capability chown,
capability dac_override,
capability setuid,
capability setgid,
capability net_bind_service,
deny @{PROC}/{*,**^[0-9*],sys/kernel/shm*} wkx,
deny @{PROC}/sysrq-trigger rwklx,
deny @{PROC}/mem rwklx,
deny @{PROC}/kmem rwklx,
deny @{PROC}/kcore rwklx,
deny mount,
deny /sys/[^f]*/** wklx,
deny /sys/f[^s]*/** wklx,
deny /sys/fs/[^c]*/** wklx,
deny /sys/fs/c[^g]*/** wklx,
deny /sys/fs/cg[^r]*/** wklx,
deny /sys/firmware/efi/efivars/** rwklx,
deny /sys/kernel/security/** rwklx,
}
50 changes: 50 additions & 0 deletions apparmor/loader/example-daemon.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# The example DaemonSet demonstrating how the profile loader can be deployed onto a cluster to
# automatically load AppArmor profiles from a ConfigMap.

apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: apparmor-loader
# Namespace must match that of the ConfigMap.
namespace: apparmor
spec:
template:
metadata:
name: apparmor-loader
labels:
daemon: apparmor-loader
spec:
containers:
- name: apparmor-loader
image: google/apparmor-loader:latest
args:
# Tell the loader to pull the /profiles directory every 30 seconds.
- -poll
- 30s
- /profiles
securityContext:
# The loader requires root permissions to actually load the profiles.
privileged: true
volumeMounts:
- name: sys
mountPath: /sys
readOnly: true
- name: apparmor-includes
mountPath: /etc/apparmor.d
readOnly: true
- name: profiles
mountPath: /profiles
readOnly: true
volumes:
# The /sys directory must be mounted to interact with the AppArmor module.
- name: sys
hostPath:
path: /sys
# The /etc/apparmor.d directory is required for most apparmor include templates.
- name: apparmor-includes
hostPath:
path: /etc/apparmor.d
# Map in the profile data.
- name: profiles
configMap:
name: apparmor-profiles
6 changes: 6 additions & 0 deletions apparmor/loader/example-namespace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# The example Namespace used by other example objects.

apiVersion: v1
kind: Namespace
metadata:
name: apparmor
19 changes: 19 additions & 0 deletions apparmor/loader/example-pod.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# The example Pod utilizing the profile loaded by the sample daemon.

apiVersion: v1
kind: Pod
metadata:
name: nginx-apparmor
# Note that the Pod does not need to be in the same namespace as the loader.
labels:
app: nginx
annotations:
# Tell Kubernetes to apply the AppArmor profile "k8s-nginx".
# Note that this is ignored if the Kubernetes node is not running version 1.4 or greater.
container.apparmor.security.alpha.kubernetes.io/nginx: localhost/k8s-nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
Loading

0 comments on commit 562efc7

Please sign in to comment.