I did a bit more digging. When starting snap-confine will prepare and capture the mount namespace of a snap.
In the process it does a recursive bind mount of /sys inside the new mount namespace of a snap (MS_BIND|MS_REC), and then recursively change the propagation to slave (MS_REC|MS_SLAVE). This should /sys/fs/cgroup/freezer appear later in the parent namespace, it should also appear in the snap mount namespace.
On 20.04 VM, findmnt -o +PROPAGATION indicates that /sys/fs/cgroup is a mount point of tmpfs, with propagation set to shared:
/sys/fs/cgroup tmpfs tmpfs ro,nosuid,nodev,noexec,mode=755 shared
Thus in the snap mount namespace it becomes:
/sys/fs/cgroup tmpfs tmpfs ro,nosuid,nodev,noexec,mode=755 private,slave
However, inside the LXD container, it is already private (so mount and unmount events do not propagate):
/sys/fs/cgroup tmpfs tmpfs ro,nosuid,nodev,noexec,mode=755,uid=1000000,gid=1000000 private
Inside the snap mount ns is then:
/sys/fs/cgroup tmpfs tmpfs ro,nosuid,nodev,noexec,mode=755,uid=1000000,gid=1000000 private
As trying to change the propagation of a private mount has no effect. So even if freezer is mounted later it will not become visible.
That state of mount namespace is captured and reused for subsequent attempts to start the snap. Since the freezer is unavailable, s-c cannot setup the cgroup and thus fails.
This code has not been changed since late 2018/early 2019, so some other change must have made this behavior visible just now. It still is unclear why the freezer is unavailable at the time the snap is started, perhaps the system is still booting (as much as containers can boot) but the shell is already acessible and it is possible to try to `snap install`.
I did a bit more digging. When starting snap-confine will prepare and capture the mount namespace of a snap.
In the process it does a recursive bind mount of /sys inside the new mount namespace of a snap (MS_BIND|MS_REC), and then recursively change the propagation to slave (MS_REC|MS_SLAVE). This should /sys/fs/ cgroup/ freezer appear later in the parent namespace, it should also appear in the snap mount namespace.
On 20.04 VM, findmnt -o +PROPAGATION indicates that /sys/fs/cgroup is a mount point of tmpfs, with propagation set to shared: nodev,noexec, mode=755 shared
/sys/fs/cgroup tmpfs tmpfs ro,nosuid,
Thus in the snap mount namespace it becomes: nodev,noexec, mode=755 private,slave
/sys/fs/cgroup tmpfs tmpfs ro,nosuid,
However, inside the LXD container, it is already private (so mount and unmount events do not propagate): nodev,noexec, mode=755, uid=1000000, gid=1000000 private
/sys/fs/cgroup tmpfs tmpfs ro,nosuid,
Inside the snap mount ns is then: nodev,noexec, mode=755, uid=1000000, gid=1000000 private
/sys/fs/cgroup tmpfs tmpfs ro,nosuid,
As trying to change the propagation of a private mount has no effect. So even if freezer is mounted later it will not become visible.
That state of mount namespace is captured and reused for subsequent attempts to start the snap. Since the freezer is unavailable, s-c cannot setup the cgroup and thus fails.
This code has not been changed since late 2018/early 2019, so some other change must have made this behavior visible just now. It still is unclear why the freezer is unavailable at the time the snap is started, perhaps the system is still booting (as much as containers can boot) but the shell is already acessible and it is possible to try to `snap install`.