Since /project binds the whole project root into the workshop, the definition directory .workshop/ is writable by the workshop user — including the definition YAML and any in-project SDK setup-project hooks. A process inside the workshop can edit its own definition and the changes are applied by the next workshop refresh. For hooks this means arbitrary code execution as the workshop user on refresh, since setup-project runs from the (writable) .workshop/<sdk>/hooks/ as that user.
Quick repro (0.9.2, LXD 6.9):
$ workshop exec test -- sh -c 'touch /project/.workshop/canary; echo $?'
0
Declaring a read-only mount plug over the definition dir closes it — the definition becomes immutable from inside, while workshopd still manages it host-side:
plugs:
lockdown:
interface: mount
workshop-target: /project/.workshop
read-only: true
After that:
$ workshop exec test -- sh -c 'touch /project/.workshop/canary; echo $?'
touch: cannot touch '/project/.workshop/canary': Read-only file system
1
$ workshop exec test -- mount | grep '.workshop'
... on /project/.workshop type ext4 (ro,relatime)
workshop refresh still works, and the mountpoint can't be renamed or removed from the rw parent ("Device or resource busy"), so it's self-reinforcing — the container can't unmount its own lock.
My question: is mounting .workshop/ read-only over itself a supported pattern, or does it work only by nested-mount precedence over the core /project bind (i.e. could a future version break it)? If it's intended to work, having it documented — or the definition dir being read-only inside the container by default — would let people rely on it.
Since
/projectbinds the whole project root into the workshop, the definition directory.workshop/is writable by the workshop user — including the definition YAML and any in-project SDKsetup-projecthooks. A process inside the workshop can edit its own definition and the changes are applied by the nextworkshop refresh. For hooks this means arbitrary code execution as the workshop user on refresh, sincesetup-projectruns from the (writable).workshop/<sdk>/hooks/as that user.Quick repro (0.9.2, LXD 6.9):
Declaring a read-only mount plug over the definition dir closes it — the definition becomes immutable from inside, while
workshopdstill manages it host-side:After that:
workshop refreshstill works, and the mountpoint can't be renamed or removed from the rw parent ("Device or resource busy"), so it's self-reinforcing — the container can't unmount its own lock.My question: is mounting
.workshop/read-only over itself a supported pattern, or does it work only by nested-mount precedence over the core/projectbind (i.e. could a future version break it)? If it's intended to work, having it documented — or the definition dir being read-only inside the container by default — would let people rely on it.