Skip to content

Commit

Permalink
Merge a83629a into 849861d
Browse files Browse the repository at this point in the history
  • Loading branch information
tuxerrante authored Feb 1, 2023
2 parents 849861d + a83629a commit 82a45a3
Show file tree
Hide file tree
Showing 21 changed files with 744 additions and 248 deletions.
1 change: 0 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
**/*.dbmdl
**/*.jfm
**/bin
**/charts
**/docker-compose*
**/compose*
**/Dockerfile*
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/build-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: "1. Create app"

on:
push:
branches: [main,dev]
branches: [main,dev,feature/*]
paths:
- "go/src/app/**.go"
- Dockerfile
Expand Down Expand Up @@ -142,4 +142,4 @@ jobs:
env:
CR_TOKEN: "${{ env.GITHUB_TOKEN }}"
with:
config: ct.yaml
config: cr.yaml
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@
# Editor configs
.idea/
.vscode/
.errors/
70 changes: 70 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

1. Go unit tests
- [ ] Create a new profile
- [ ] Update an existing profile
- [ ] Remove an existing profile
- [ ] Remove a non existing profile
1. Remove kubernetes Service and DaemonSet exposed ports if useless
1. Evaluate an automatic changelog generation from commits like [googleapis/release-please](https://github.com/googleapis/release-please)
1. Add daemonset commands for checking readiness
1. Add tests for all the main functions
1. Add test for checking current confinement state of the app
1. Test on multiple nodes cluster


## [0.1.0]() - 2023-02-01
### Fixed
1. "Unable to replace profiles. Permission denied, app seems still confined." - Switched to ubuntu image
1. No need for SYS_ADMIN capabilities
1. Ignore hidden and system folders while scanning for profiles

### Added
1. Instructions to test the app in a virtual machine directly running the go app or in microk8s pushing the built container to the local registry


## 0.0.6 - 2023-01-26

### Added
Helm:
- Added SYS_ADMIN capabilities to the daemonset
- Mounted needed folders in the Dockerfile and in the daemonset
- Added POLL_TIME and profiles files as configurable options through configmaps

Go:
- Added first testing function
- Moved file operations functions to dedicated module
- Fixed POLL_TIME value passing from configmap

CI/CD:
- Explicit changelog to help users understanding the project features
- Automatic generation of release notes based on changelog file
- Configurable poll time and profiles directory in the helm values file

## [0.0.5](https://github.com/tuxerrante/kapparmor/releases/tag/kapparmor-0.0.5-alpha) - 2023-01-23

### Added

Helm:
- Helm Chart based mainly on a DaemonSet and a configmap. No operator needed.
- Load all AppArmor profiles in the configmap template

Go:
- Possibility to load continuously the security profiles from a configmap with a configurable poll time

CI/CD:
- Helm chart linting and testing before releasing
- Security vulnerability tests on Go dependencies and container file.
- Auto generation of [GitHub pages](https://tuxerrante.github.io/kapparmor/)
- Container image tag is set to current commit SHA for every release.

### Fixed

- Being still an alpha release I will add everything in the "Added" section
23 changes: 11 additions & 12 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,33 +1,32 @@
# --- build stage
FROM golang:1.19-alpine AS builder
RUN apk add --no-cache git
FROM golang:1.19 AS builder

WORKDIR /go/src/app
COPY . .
RUN go get -d -v ./go/src/app/
RUN go build -o /go/bin/app -v ./go/src/app/

# ---
FROM alpine:latest
FROM ubuntu:latest
LABEL Name=kapparmor Version=0.0.1
LABEL Author="Affinito Alessandro"

WORKDIR /app

RUN addgroup --system appgroup &&\
adduser --system appuser -G appgroup &&\
apk --no-cache update &&\
apk add apparmor

COPY --chown=appuser:appgroup --from=builder ./go/bin/app /app/
COPY --chown=appuser:appgroup ./charts/kapparmor/profiles /app/profiles
RUN apt-get update &&\
apt-get upgrade -y &&\
apt-get install --no-install-recommends --yes apparmor apparmor-utils &&\
rm -rf /var/lib/apt/lists/* &&\
mkdir --parent --verbose /etc/apparmor.d/custom

RUN chmod 550 app
COPY --from=builder /go/bin/app /app/
COPY ./charts/kapparmor/profiles /app/profiles/

ARG PROFILES_DIR
ARG POLL_TIME

ENV PROFILES_DIR=$PROFILES_DIR
ENV POLL_TIME=$POLL_TIME

USER appuser
USER root
CMD ./app
30 changes: 30 additions & 0 deletions Dockerfile.alpine
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# --- build stage
FROM golang:1.19-alpine AS builder
RUN apk add --no-cache git
WORKDIR /go/src/app
COPY . .
RUN go get -d -v ./go/src/app/
RUN go build -o /go/bin/app -v ./go/src/app/

# ---
FROM alpine:latest
LABEL Name=kapparmor Version=0.0.1
LABEL Author="Affinito Alessandro"

WORKDIR /app

RUN apk --no-cache update &&\
apk add apparmor libapparmor &&\
mkdir --parent --verbose /etc/apparmor.d/custom

COPY --from=builder ./go/bin/app /app/
COPY ./charts/kapparmor/profiles /app/profiles

ARG PROFILES_DIR
ARG POLL_TIME

ENV PROFILES_DIR=$PROFILES_DIR
ENV POLL_TIME=$POLL_TIME

USER root
CMD ./app
40 changes: 24 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@

# Kapparmor
- [Kapparmor](#kapparmor)
- [Prerequisites](#prerequisites)
- [How to initialize this project again](#how-to-initialize-this-project-again)
- [Testing](#testing)
- [How to initialize this project](#how-to-initialize-this-project)
- [Test the app locally](#test-the-app-locally)
- [TO-DO](#to-do)
- [External useful links](#external-useful-links)
- -----
Apparmor-loader project to deploy profiles through a kubernetes daemonset.

This work is inspired by [kubernetes/apparmor-loader](https://github.com/kubernetes/kubernetes/tree/master/test/images/apparmor-loader).

![architecture](./docs/kapparmor-architecture.png)

This app provide dynamic loading and unloading of AppArmor profiles to a Kubernetes cluster through a configmap.
The app doesn't need an operator and it will be managed by a DaemonSet filtering the linux nodes to schedule the app pod.
The custom profiles deployed in the configmap will be copied in a directory (`/etc/apparmor.d/custom` by default) since apparmor_parser needs the profiles definitions also to remove them. Once you will deploy a configmap with different profiles, Kapparmor will notice the missing ones and it will remove them from the apparmor cache and from the node directory.
If you modify only the content of a profile leaving the same name, Kapparmor should notice it anyway since a byte comparison is done when configmap profiles names and local profiles names match.

1. The CD pipeline will
- deploy a configmap in the security namespace containing all the profiles versioned in the current project
- it will apply a daemonset on the linux nodes
Expand All @@ -24,10 +27,14 @@ This work is inspired by [kubernetes/apparmor-loader](https://github.com/kuberne
- The name of the file should be the same as the name of the profile.
3. The configmap will be polled every POLL_TIME seconds to move them into PROFILES_DIR host path and then enable them.

## Prerequisites
You can view which profiles are loaded on a node by checking the /sys/kernel/security/apparmor/profiles, so its parent will need to be mounted in the pod.

This work was inspired by [kubernetes/apparmor-loader](https://github.com/kubernetes/kubernetes/tree/master/test/images/apparmor-loader).

## Testing
[Set up a Microk8s environment](./docs/microk8s.md).

### How to initialize this project again
### How to initialize this project
```sh
helm create kapparmor
sudo usermod -aG docker $USER
Expand All @@ -38,6 +45,8 @@ go mod init ./go/src/app/
```

### Test the app locally

Test Helm Chart creation
```sh
# --- Check the Helm chart
# https://github.com/helm/chart-testing/issues/464
Expand All @@ -49,27 +58,26 @@ docker run -it --network host --workdir=/data --volume ~/.kube/config:/root/.kub
--volume $(pwd):/data quay.io/helmpack/chart-testing:latest \
/bin/sh -c "git config --global --add safe.directory /data; ct lint --print-config --charts ./charts/kapparmor"

export GITHUB_SHA=42
# Replace here a commit id being part of an image tag
export GITHUB_SHA="sha-93d0dc4c597a8ae8a9febe1d68e674daf1fa919a"
helm install --dry-run --atomic --generate-name --timeout 30s --debug --set image.tag=$GITHUB_SHA charts/kapparmor/

```

Test the app inside a container:
```sh
# --- Build and run the container image
docker build --quiet -t test-kapparmor --build-arg POLL_TIME=60 --build-arg PROFILES_DIR=/app/profiles -f Dockerfile . &&\
echo &&\
docker run --rm -it --privileged \
--mount type=bind,source='/sys/kernel/security',target='/sys/kernel/security' \
--mount type=bind,source='/etc',target='/etc'\
test-kapparmor

--name kapparmor test-kapparmor

```
## TO-DO
1. Go unit tests
- [ ] Create a new profile
- [ ] Update an existing profile
- [ ] Remove an existing profile
- [ ] Remove a non existing profile
1. Remove kubernetes Service and DaemonSet exposed ports if useless

To test Helm chart installation in a MicroK8s cluster, follow docs/microk8s.md instructions if you don't have any local cluster.



# External useful links
Expand Down
14 changes: 2 additions & 12 deletions charts/kapparmor/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ type: application
home: https://artifacthub.io
kubeVersion: ">= 1.23.0-0"

version: "0.0.5-alpha"
appVersion: "0.0.1-alpha"
version: "0.0.6"
appVersion: "0.0.2"

keywords:
- kubernetes
Expand All @@ -17,13 +17,3 @@ keywords:
maintainers:
- name: tuxerrante
url: https://github.com/sponsors/tuxerrante

annotations:
artifacthub.io/containsSecurityUpdates: "false"
artifacthub.io/changes: |
- kind: added
description: Load new profiles in the configmap
- kind: added
description: Unload old profiles in the filesystem
- kind: added
description: Update profiles with same name and different content
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "kapparmor.fullname" . }}
name: kapparmor-profiles
data:
{{ (.Files.Glob "profiles/*").AsConfig | indent 2 }}
7 changes: 7 additions & 0 deletions charts/kapparmor/templates/cm-settings.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: kapparmor-settings
data:
PROFILES_DIR: "{{ .Values.app.profiles_dir }}"
POLL_TIME: "{{ .Values.app.poll_time }}"
58 changes: 38 additions & 20 deletions charts/kapparmor/templates/daemonset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ spec:
{{- include "kapparmor.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
container.apparmor.security.beta.kubernetes.io/kapparmor: unconfined
{{- with .Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}

labels:
{{- include "kapparmor.selectorLabels" . | nindent 8 }}

spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
Expand All @@ -24,36 +27,51 @@ spec:
serviceAccountName: {{ include "kapparmor.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}

containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 80
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http

resources:
{{- toYaml .Values.resources | nindent 12 }}
# mount a configmap as read only in the container's filesystem.

volumeMounts :
- name : {{ include "kapparmor.fullname" . }}
mountPath : /app/profiles
# Folder containing profiles files mounted from the configmap
- name : kapparmor-profiles
mountPath : {{ .Values.app.profiles_dir }}
readOnly : false
# Folder used by the kernel to store loaded profiles names
- name: profiles-kernel-path
mountPath: /sys/kernel/security
# Folder used by the app to store custom profiles definitions
- name: etc-apparmor
mountPath: /etc/apparmor.d/

env:
- name: PROFILES_DIR
valueFrom:
configMapKeyRef:
name: kapparmor-settings
key: PROFILES_DIR
- name: POLL_TIME
valueFrom:
configMapKeyRef:
name: kapparmor-settings
key: POLL_TIME

volumes:
- name: {{ include "kapparmor.fullname" . }}
- name: kapparmor-profiles
configMap:
name: {{ include "kapparmor.fullname" . }}
name: kapparmor-profiles
- name: profiles-kernel-path
hostPath:
path: /sys/kernel/security
- name: etc-apparmor
hostPath:
path: /etc/apparmor.d/

{{- with .Values.nodeSelector }}
nodeSelector:
Expand Down
Loading

0 comments on commit 82a45a3

Please sign in to comment.