diff --git a/add-app.sh b/add-app.sh index 86ac49a..f8595b9 100755 --- a/add-app.sh +++ b/add-app.sh @@ -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" diff --git a/docs/APP-INTEGRATION-GUIDE.md b/docs/APP-INTEGRATION-GUIDE.md index b9dd1b2..0067c52 100644 --- a/docs/APP-INTEGRATION-GUIDE.md +++ b/docs/APP-INTEGRATION-GUIDE.md @@ -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: diff --git a/install-stack.sh b/install-stack.sh index fea2933..2f90403 100755 --- a/install-stack.sh +++ b/install-stack.sh @@ -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"