This doesn’t provide a solution, but it explains why chattr can’t make a symlink immutable.
On Linux, immutable attributes are part of a set of flags which are controlled using the FS_IOC_SETFLAGS ioctl. Historically this was implemented first in ext2, and chattr itself is still part of e2fsprogs. When it attempts to retrieve the flags, before it can set them, chattr explicitly checks that the file it’s handling is a regular file or a directory:
if (!lstat(name, &buf) &&
!S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode)) {
goto notsupp;
}
One might think that removing these checks, or changing them to allow symlinks too, would be a good first step towards allowing chattr to make a symlink immutable, but the next hurdle comes up immediately thereafter:
fd = open (name, OPEN_FLAGS);
if (fd == -1)
return -1;
r = ioctl (fd, EXT2_IOC_GETFLAGS, &f);
ioctl operates on file descriptors, which means the target has to be opened before its flags can be set. Symlinks can’t be opened for use with ioctl; while open supports O_NOFOLLOW and O_NOPATH on symlinks, the former on its own will fail with ELOOP, and the latter will return a file descriptor which can’t be used with ioctl.