diff --git a/Makefile b/Makefile index ff6a948e..4766f2af 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ install: install-create-container install-pull-config install-create-container: cd create-a-container && npm install --production cd create-a-container && npm run db:migrate - install -m644 -oroot -groot create-a-container/container-creator.service /etc/systemd/system/container-creator.service + install -m644 -oroot -groot create-a-container/systemd/container-creator.service /etc/systemd/system/container-creator.service systemctl daemon-reload || true systemctl enable container-creator.service systemctl start container-creator.service || true diff --git a/create-a-container/bin/hookscript.sh b/create-a-container/bin/hookscript.sh new file mode 100755 index 00000000..54751a1b --- /dev/null +++ b/create-a-container/bin/hookscript.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# /var/lib/vz/snippets/register_proxy_hook.sh + +echo "GUEST HOOK: $*" + +# First argument is the vmid +vmid="$1" + +# Second argument is the phase +phase="$2" + +case "$phase" in + 'pre-start') + # shellcheck disable=SC2016 + echo 'Setting AppArmor profile to `unconfined`' + conffile="/etc/pve/lxc/$vmid.conf" + if grep -qE '^lxc\.apparmor\.profile' "$conffile"; then + sed -i -E 's/^lxc\.apparmor\.profile: .*$/lxc.apparmor.profile: unconfined/' "$conffile" + else + echo 'lxc.apparmor.profile: unconfined' >>"$conffile" + fi + ;; + 'post-start') ;; + 'pre-stop') ;; + 'post-stop') ;; + *) + echo "got unknown phase '$phase'" >&2 + exit 255 + ;; +esac diff --git a/create-a-container/routers/containers.js b/create-a-container/routers/containers.js index 2017470f..6aa58641 100644 --- a/create-a-container/routers/containers.js +++ b/create-a-container/routers/containers.js @@ -227,11 +227,11 @@ router.post('/', async (req, res) => { }) }); const vmid = await client.nextId(); - const upid = await client.createLxc(node.name, { + const lxcData = { ostemplate, vmid, cores: 4, - features: 'nesting=1', // allow nested containers + features: 'nesting=1,keyctl=1,fuse=1', // allow nested containers with Docker/Podman hostname, memory: 4096, // 4GB RAM net0: 'name=eth0,ip=dhcp,bridge=vmbr0', @@ -242,7 +242,14 @@ router.post('/', async (req, res) => { start: 1, // start the container immediately after creation tags: req.session.user, unprivileged: 1 - }); + }; + // check if the hookscript is available + const availableSnippets = await client.listStorageContent(node.name, 'local', 'snippets'); + const hookscript = availableSnippets.find(s => s.volid === 'local:snippets/hookscript.sh')?.volid; + if (hookscript) { + lxcData.hookscript = hookscript; + } + const upid = await client.createLxc(node.name, lxcData); // wait for the task to complete while (true) { @@ -267,7 +274,7 @@ router.post('/', async (req, res) => { } } console.error('DNS lookup failed after maximum retries'); - return null + return null; })(); const container = await Container.create({ diff --git a/create-a-container/utils/proxmox-api.js b/create-a-container/utils/proxmox-api.js index 5a8c6690..8ff87b6d 100644 --- a/create-a-container/utils/proxmox-api.js +++ b/create-a-container/utils/proxmox-api.js @@ -256,6 +256,30 @@ class ProxmoxApi { return response.data; } + + /** + * List storage content + * @param {string} node - The cluster node name + * @param {string} storage - The storage identifier + * @param {string} [content] - Only list content of this type + * @param {number} [vmid] - Only list content for this VMID + * @returns {Promise} - The storage content list + */ + async listStorageContent(node, storage, content = null, vmid = null) { + const params = {}; + if (content) params.content = content; + if (vmid) params.vmid = vmid; + + const response = await axios.get( + `${this.baseUrl}/api2/json/nodes/${node}/storage/${storage}/content`, + { + params, + ...this.options + } + ); + + return response.data.data; + } } module.exports = ProxmoxApi;