Skip to content

Commit f6030dc

Browse files
Merge pull request #1 from pascalinthecloud/feature/initial-version
feat: add intiail version of Helm chart for Bookstack S3 backup
2 parents 7f023e2 + 0074f3d commit f6030dc

File tree

12 files changed

+350
-0
lines changed

12 files changed

+350
-0
lines changed

.github/workflows/release.yaml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: release
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
jobs:
9+
release:
10+
# depending on default permission settings for your org (contents being read-only or read-write for workloads), you will have to add permissions
11+
# see: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#modifying-the-permissions-for-the-github_token
12+
permissions:
13+
contents: write
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Checkout
17+
uses: actions/checkout@v3
18+
with:
19+
fetch-depth: 0
20+
21+
- name: Configure Git
22+
run: |
23+
git config user.name "$GITHUB_ACTOR"
24+
git config user.email "[email protected]"
25+
- name: Install Helm
26+
uses: azure/setup-helm@v3
27+
28+
- name: Run chart-releaser
29+
uses: helm/[email protected]
30+
env:
31+
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Helm-related files
2+
*.tgz
3+
*.lock
4+
.chart-releaser.yaml
5+
.release.yaml
6+
.local.values.yaml
7+
*.kubeconfig

README.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Helm Chart: Bookstack S3 Backup
2+
3+
This Helm chart deploys a CronJob to back up Bookstack documents to an S3-compatible storage with optional SSE-C encryption. The backup process overwrites existing files, so S3 bucket versioning should be enabled to maintain historical versions.
4+
5+
## Installation
6+
7+
To install with custom values:
8+
9+
```bash
10+
helm repo add bookstack-backup https://pascalinthecloud.github.io/helm-bookstack-s3-backup/
11+
12+
helm install bookstack-backup bookstack-backup bookstack-backup/helm-bookstack-s3-backup -f values.yaml -n <NAMESPACE>
13+
```
14+
15+
## Configuration
16+
17+
### Values
18+
19+
| Key | Default Value | Description |
20+
|--------------------|---------------------|-------------|
21+
| `cron` | `"0 4 * * 0"` | Cron schedule for backups (UTC) |
22+
| `image.repository` | `pascaaal/docker-awscli-kubectl-zip` | Backup container image repository |
23+
| `image.tag` | `v1.0.7` | Container image tag |
24+
| `s3.accessKey` | `""` | S3 Access Key ID |
25+
| `s3.secretKey` | `""` | S3 Secret Access Key |
26+
| `s3.bucket` | `""` | Target S3 bucket name |
27+
| `s3.endpoint` | `""` | S3 endpoint including `https://` |
28+
| `s3.region` | `""` | S3 region |
29+
| `s3.sseKey` | `""` | 32-byte hex key for server-side encryption (SSE-C) |
30+
| `bookstack.namespace` | `""` | Namespace of Bookstack deployment |
31+
| `bookstack.app` | `""` | Label of the Bookstack pod |
32+
| `bookstack.container_name` | `""` | Bookstack container name |
33+
34+
## Example `values.yaml`
35+
36+
```yaml
37+
cron: "0 3 * * *"
38+
39+
image:
40+
repository: pascaaal/docker-awscli-kubectl-zip
41+
tag: v1.0.7
42+
43+
s3:
44+
accessKey: "your-access-key"
45+
secretKey: "your-secret-key"
46+
bucket: "your-backup-bucket"
47+
endpoint: "https://s3.example.com"
48+
region: "us-east-1"
49+
sseKey: "your-32-byte-hex-key" # encryption is enabled, when key is set
50+
51+
bookstack:
52+
namespace: "bookstack"
53+
app: "bookstack"
54+
container_name: "bookstack-container"
55+
```
56+
57+
## Generate encryption key
58+
```bash
59+
openssl rand -hex 32
60+
```
61+
62+
## Decrypt backup
63+
```bash
64+
export S3_SSE_KEY=154cd74642087d8d8d36c9136b89fb10b42a745c12108092895de44ed03518c0
65+
echo $S3_SSE_KEY | xxd -r -p > .sse-c.key
66+
aws s3 sync s3://<BUCKET_NAME> . --endpoint-url https://<ENDPOINT_URL> --sse-c AES256 --sse-c-key fileb://.sse-c.key
67+
```
68+
69+
## View logs
70+
```bash
71+
kubectl logs -l app=bookstack-backup -n <NAMESPACE> -c bookstack-backup
72+
```
73+
## Uninstallation
74+
75+
To remove the deployment:
76+
77+
```bash
78+
helm uninstall bookstack-backup
79+
```
80+
81+
## Notes
82+
- Ensure that the S3 credentials have appropriate permissions to write to the bucket.
83+
- Treat your encryption key like your car keys—lose it, and you’re not going anywhere… especially not to your backups! 🚗🔑
84+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Patterns to ignore when building packages.
2+
# This supports shell glob matching, relative path matching, and
3+
# negation (prefixed with !). Only one pattern per line.
4+
.DS_Store
5+
# Common VCS dirs
6+
.git/
7+
.gitignore
8+
.bzr/
9+
.bzrignore
10+
.hg/
11+
.hgignore
12+
.svn/
13+
# Common backup files
14+
*.swp
15+
*.bak
16+
*.tmp
17+
*.orig
18+
*~
19+
# Various IDEs
20+
.project
21+
.idea/
22+
*.tmproj
23+
.vscode/
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
apiVersion: v2
2+
name: helm-bookstack-s3-backup
3+
description: A Helm chart for automating Bookstack backups to a S3 bucket in Kubernetes with optional encryption.
4+
type: application
5+
version: 0.1.0
6+
appVersion: "1.0.0"
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: {{ .Release.Name }}-config
5+
namespace: {{ .Release.Namespace }}
6+
data:
7+
backup.sh: |
8+
#!/bin/sh
9+
set -e
10+
11+
# Log functions
12+
log() {
13+
local level="$1"
14+
local message="$2"
15+
echo "[${level}] ${message}"
16+
}
17+
18+
info() { log "INFO" "$1"; }
19+
warn() { log "WARN" "$1"; }
20+
error() { log "ERROR" "$1"; }
21+
22+
# Validate environment variables
23+
: "${S3_BUCKET:?$(error 'Environment variable S3_BUCKET not set')}"
24+
: "${S3_ENDPOINT:?$(error 'Environment variable S3_ENDPOINT not set')}"
25+
: "${S3_REGION:?$(error 'Environment variable S3_REGION not set')}"
26+
27+
# Get the bookstack pod name & container name
28+
BOOKSTACK_POD=$(kubectl get pod -n $BOOKSTACK_NAMESPACE -l app=$BOOKSTACK_APP -o jsonpath="{.items[0].metadata.name}")
29+
30+
# Set the timestamp and file name
31+
TIMESTAMP=$(date +%Y-%m-%d_%H-%M-%S)
32+
FILE_NAME="bookstack-backup-$TIMESTAMP"
33+
34+
# Import the key if S3_SSE_KEY is set
35+
if [ -n "$S3_SSE_KEY" ]; then
36+
S3_SSE_C_PATH="/mnt/backup/.sse-c.key"
37+
echo "$S3_SSE_KEY" | xxd -r -p > "$S3_SSE_C_PATH"
38+
fi
39+
40+
41+
#sleep infinity
42+
43+
info "Creating backup with document exporter..."
44+
kubectl exec ${BOOKSTACK_POD} --container $BOOKSTACK_CONTAINER_NAME -- \
45+
sh -c 'mysqldump --skip-ssl -h $DB_HOST -u $DB_USERNAME -p$DB_PASSWORD $DB_DATABASE > /tmp/'${FILE_NAME}'.sql'
46+
47+
kubectl exec ${BOOKSTACK_POD} --container $BOOKSTACK_CONTAINER_NAME -- \
48+
sh -c 'cd /app/www/ && tar --dereference -czvf /tmp/bookstack-files-backup.tar.gz public/uploads storage/uploads themes'
49+
50+
info "Copying sql backup file to this pod..."
51+
kubectl cp \
52+
--container="${BOOKSTACK_CONTAINER_NAME}" \
53+
${BOOKSTACK_POD}:/tmp/${FILE_NAME}.sql \
54+
/mnt/backup/bookstack-backup-db.sql
55+
56+
info "Copying bookstack folder backup file to the host..."
57+
kubectl cp \
58+
--container="${BOOKSTACK_CONTAINER_NAME}" \
59+
${BOOKSTACK_POD}:/tmp/bookstack-files-backup.tar.gz \
60+
/mnt/backup/bookstack-files-backup.tar.gz
61+
62+
info "Unzipping backup file..."
63+
tar -xzvf /mnt/backup/bookstack-files-backup.tar.gz -C /mnt/backup/
64+
65+
info "Removing tar file local..."
66+
rm /mnt/backup/bookstack-files-backup.tar.gz
67+
68+
info "Cleaning up backup files remote..."
69+
kubectl exec ${BOOKSTACK_POD} --container ${BOOKSTACK_CONTAINER_NAME} -- \
70+
bash -c "rm /tmp/${FILE_NAME}.sql && \
71+
rm /tmp/bookstack-files-backup.tar.gz"
72+
73+
info "Uploading backup to S3..."
74+
CMD="aws s3 sync /mnt/backup s3://$S3_BUCKET --endpoint-url $S3_ENDPOINT --region $S3_REGION --exclude '.sse-c.key'"
75+
76+
if [ -n "$S3_SSE_KEY" ]; then
77+
CMD="$CMD --sse-c AES256 --sse-c-key fileb://$S3_SSE_C_PATH"
78+
info "Using server-side encryption with customer-provided key"
79+
fi
80+
81+
eval "$CMD"
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
apiVersion: batch/v1
2+
kind: CronJob
3+
metadata:
4+
name: {{ .Release.Name }}-cronjob
5+
namespace: {{ .Release.Namespace }}
6+
spec:
7+
schedule: "{{ .Values.cron }}"
8+
successfulJobsHistoryLimit: 2
9+
failedJobsHistoryLimit: 2
10+
jobTemplate:
11+
spec:
12+
backoffLimit: 2
13+
template:
14+
metadata:
15+
name: {{ .Release.Name }}
16+
labels:
17+
app: {{ .Release.Name }}
18+
spec:
19+
serviceAccountName: {{ .Release.Name }}
20+
restartPolicy: OnFailure
21+
22+
containers:
23+
- name: bookstack-backup
24+
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
25+
command: ["bash", "-c", "/opt/bookstack-s3-backup/backup.sh"]
26+
envFrom:
27+
- secretRef:
28+
name: {{ .Release.Name }}-secret
29+
securityContext:
30+
runAsUser: 34
31+
seccompProfile:
32+
type: RuntimeDefault
33+
allowPrivilegeEscalation: false
34+
runAsNonRoot: true
35+
capabilities:
36+
drop:
37+
- ALL
38+
volumeMounts:
39+
- name: backup-volume
40+
mountPath: /mnt/backup
41+
- name: backup-script
42+
mountPath: /opt/bookstack-s3-backup/backup.sh
43+
subPath: backup.sh
44+
45+
volumes:
46+
- name: backup-volume
47+
emptyDir: {}
48+
- name: backup-script
49+
configMap:
50+
name: {{ .Release.Name }}-config
51+
items:
52+
- key: backup.sh
53+
path: backup.sh
54+
defaultMode: 0555
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
apiVersion: rbac.authorization.k8s.io/v1
2+
kind: Role
3+
metadata:
4+
name: {{ .Release.Name }}
5+
namespace: {{ .Release.Namespace }}
6+
rules:
7+
- apiGroups: [""]
8+
resources: ["pods", "pods/exec"]
9+
verbs: ["get", "list", "create"]
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
apiVersion: rbac.authorization.k8s.io/v1
2+
kind: RoleBinding
3+
metadata:
4+
name: {{ .Release.Name }}
5+
namespace: {{ .Release.Namespace }}
6+
subjects:
7+
- kind: ServiceAccount
8+
name: {{ .Release.Name }}
9+
roleRef:
10+
kind: Role
11+
name: {{ .Release.Name }}
12+
apiGroup: rbac.authorization.k8s.io
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
apiVersion: v1
2+
kind: ServiceAccount
3+
metadata:
4+
name: {{ .Release.Name }}
5+
namespace: {{ .Release.Namespace }}

0 commit comments

Comments
 (0)