Docker-in-LXC: security_opt in composes; revert LXC apparmor override; doc

This commit is contained in:
jtricerolph 2026-07-01 13:28:54 +00:00
parent 69db7d5426
commit 4e72d46bf9
3 changed files with 54 additions and 57 deletions

View file

@ -124,11 +124,8 @@ pct create "$LXC_ID" "$TEMPLATE" \
--unprivileged 0 \
--onboot 1 \
--tags "${POOL}" \
${POOL_OPT} &>/dev/null
# Docker-in-LXC: run AppArmor-unconfined so containers can start
grep -q '^lxc.apparmor.profile: unconfined' "/etc/pve/lxc/${LXC_ID}.conf" 2>/dev/null \
|| echo "lxc.apparmor.profile: unconfined" >> "/etc/pve/lxc/${LXC_ID}.conf"
pct start "$LXC_ID" &>/dev/null
${POOL_OPT} \
--start 1 &>/dev/null
sleep 5
msg_ok "LXC ${LXC_ID} created"

View file

@ -292,10 +292,18 @@ location /kitchen/health {
### 6. Docker Compose Template
> **Docker-in-LXC:** every service needs `security_opt: [apparmor=unconfined]`.
> The app LXCs run with `nesting=1` but are AppArmor-confined, so without this a
> container fails to start with *"docker-default profile could not be loaded …
> while confined"*. Do NOT set `lxc.apparmor.profile: unconfined` on the LXC —
> that cancels `nesting=1`.
```yaml
services:
backend:
build: ./backend
security_opt:
- apparmor=unconfined
environment:
- DATABASE_URL=postgresql://appname:${DB_PASS}@10.10.10.100:5432/appname_db
- CENTRAL_AUTH_SECRET=${CENTRAL_AUTH_SECRET}
@ -310,6 +318,8 @@ services:
frontend:
build: ./frontend
security_opt:
- apparmor=unconfined
ports:
- "3080:80" # this port is what NPM proxies to
depends_on:

View file

@ -335,68 +335,54 @@ ensure_pool() {
pvesh get "/pools/${POOL}" &>/dev/null && POOL_OPT="--pool ${POOL}"
}
# Docker-in-LXC needs the container to run AppArmor-unconfined, otherwise
# containers fail with "docker-default profile could not be loaded ... while
# confined". Idempotent: adds the line if missing, (re)starts only when needed.
apply_docker_lxc_conf() {
local id=$1 created=$2
local conf="/etc/pve/lxc/${id}.conf" changed=0
if ! grep -q '^lxc.apparmor.profile: unconfined' "$conf" 2>/dev/null; then
echo "lxc.apparmor.profile: unconfined" >> "$conf"
changed=1
fi
if [[ "$created" == 1 ]]; then
pct start "$id" &>/dev/null; sleep 5
elif [[ "$changed" == 1 ]]; then
pct stop "$id" &>/dev/null || true; pct start "$id" &>/dev/null; sleep 5
elif ! lxc_running "$id"; then
pct start "$id" &>/dev/null; sleep 5
fi
}
# Docker-in-LXC: keep the nesting=1 feature (needed by Docker) and let each
# container skip AppArmor via `security_opt: apparmor=unconfined` in its compose.
# (Overriding lxc.apparmor.profile would cancel nesting — don't do that.)
create_lxc() {
local id=$1 ip=$2 name=$3 mem=${4:-512} cores=${5:-1} created=0
local id=$1 ip=$2 name=$3 mem=${4:-512} cores=${5:-1}
if lxc_exists "$id"; then
msg_warn "LXC $id (hotel-manage-${name}) already exists — skipping creation"
else
local tmpl; tmpl=$(get_template)
pct create "$id" "$tmpl" \
--hostname "hotel-manage-${name}" \
--memory "$mem" \
--cores "$cores" \
--rootfs "${STORAGE}:8" \
--net0 "name=eth0,bridge=vmbr1,ip=${ip}/24,gw=10.10.10.1" \
--features nesting=1 \
--unprivileged 0 \
--onboot 1 \
--tags "${POOL}" \
${POOL_OPT} &>/dev/null
created=1
lxc_running "$id" || pct start "$id"
return
fi
apply_docker_lxc_conf "$id" "$created"
local tmpl; tmpl=$(get_template)
pct create "$id" "$tmpl" \
--hostname "hotel-manage-${name}" \
--memory "$mem" \
--cores "$cores" \
--rootfs "${STORAGE}:8" \
--net0 "name=eth0,bridge=vmbr1,ip=${ip}/24,gw=10.10.10.1" \
--features nesting=1 \
--unprivileged 0 \
--onboot 1 \
--tags "${POOL}" \
${POOL_OPT} \
--start 1 &>/dev/null
sleep 5
}
create_npm_lxc() {
local id=103 created=0
local id=103
if lxc_exists "$id"; then
msg_warn "LXC $id (hotel-manage-npm) already exists — skipping creation"
else
local tmpl; tmpl=$(get_template)
pct create "$id" "$tmpl" \
--hostname "hotel-manage-npm" \
--memory 512 \
--cores 1 \
--rootfs "${STORAGE}:8" \
--net0 "name=eth0,bridge=vmbr0,ip=${NPM_LAN_IP}/22,gw=${LAN_GW}" \
--net1 "name=eth1,bridge=vmbr1,ip=10.10.10.3/24" \
--features nesting=1 \
--unprivileged 0 \
--onboot 1 \
--tags "${POOL}" \
${POOL_OPT} &>/dev/null
created=1
lxc_running "$id" || pct start "$id"
return
fi
apply_docker_lxc_conf "$id" "$created"
local tmpl; tmpl=$(get_template)
pct create "$id" "$tmpl" \
--hostname "hotel-manage-npm" \
--memory 512 \
--cores 1 \
--rootfs "${STORAGE}:8" \
--net0 "name=eth0,bridge=vmbr0,ip=${NPM_LAN_IP}/22,gw=${LAN_GW}" \
--net1 "name=eth1,bridge=vmbr1,ip=10.10.10.3/24" \
--features nesting=1 \
--unprivileged 0 \
--onboot 1 \
--tags "${POOL}" \
${POOL_OPT} \
--start 1 &>/dev/null
sleep 5
}
install_docker() {
@ -521,6 +507,8 @@ services:
postgres:
container_name: hotel-manage-postgres
image: postgres:16-alpine
security_opt:
- apparmor=unconfined
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=${PG_SUPERPASS}
@ -662,6 +650,8 @@ services:
npm:
container_name: hotel-manage-npm
image: jc21/nginx-proxy-manager:latest
security_opt:
- apparmor=unconfined
ports:
- "80:80"
- "443:443"