#519: vphoned wrote jbroot/dev as the link text /rootfs/dev. The kernel resolves link text, not vroot paths, so it dangled: every shell failed on /dev/null and sshd never started. It is now /dev, an existing /rootfs/dev link is replaced, and rootfs -> / is created. #520: Irisin unpacks packages without RootHide dpkg's hook, so nothing linked .jbroot in deeper package directories, and sudo could not load libsudo_util from usr/libexec/sudo. - vphoned walks the bootstrap for directories holding Mach-O files and links each one, at install, at startup, and one second after the root's Library/dpkg changes. That pass also runs the base steps that waited for pwd_mkdb or ssh-keygen, so sshd has host keys once openssh is installed. - The spawn hooks follow the executable's LC_RPATH and LC_LOAD_DYLIB entries inside the root and link each dependency's directory, within a fixed bound. SystemHook does the same for TweakLoader before its dlopen. - launchd starts xpcproxy and bootstrap daemons through posix_spawnp, which the launchd hook now interposes. SystemHook is chain-loaded into every child whatever its environment; DISABLE_TWEAKS and safe mode only keep ElleKit out. The installer moves to Daemon/Bootstrap, with RootHide and rootless code in their own folders. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6.0 KiB
RootHide bootstrap base
Observation
Irisin's RootHide bootstrap unpacks packages but nothing builds the parts a
jailbreak's bootstrap installer normally creates. On the 26.4 vphone (jbroot
/var/containers/Bundle/Application/.jbroot-000114514191980C), none of them
existed:
- iGhostVT stayed on "Starting…". Its Ghostty config goes under
/tmp, andvar/tmp → ../tmppointed at nothing. libghostty only logs the failed write. - Tools under vroot could not open
/dev/null. id rootandid mobilefailed withno such user: Invalid argument, while group lookups worked.- OpenSSH reset every connection before its banner: there were no host keys,
and
ssh-keygencould not create any becausegetpwuid(0)failed.
Procursus tools link libiosexec, whose ie_getpw* replace libc's user lookups.
They read the Berkeley databases (/etc/pwd.db, and /etc/spwd.db for root)
rather than the text files; icli's account set-password edits spwd.db for
the same reason. This is inferred from the failure, not from libiosexec's
source. Group lookups still read /etc/group. Running
pwd_mkdb -p /etc/master.passwd under vroot fixed every user lookup.
A root/private/etc → ../etc link was tried and had no effect: libroothide
does not wrap the getpw* family.
Ownership
vphoned creates what a bootstrap installer creates; Irisin keeps what packages
and their maintainer scripts create (for example the update-alternatives
links, which Irisin's Bootstrap Install runs). None of the items below is a
package file, so reinstalling packages does not bring them back.
Implementation
GuestIrisinInstaller.ensureRootHideBase(root:) runs right after
ensureRootHideLinks in the RootHide install path and in
refreshBootstrapOnStartup. Paths are physical; root/x is /x under vroot.
| Item | Created as |
|---|---|
root/tmp |
directory 1777, 0:0 |
root/var, root/etc |
directory 0755, 0:0, when missing |
root/var/root |
directory 0700, 0:0 |
root/var/tmp |
link ../tmp |
root/dev |
link /dev; an existing link whose text is exactly /rootfs/dev is replaced |
root/rootfs |
link /, the vroot bridge to the real root |
root/etc/passwd, root/etc/group |
copies of /private/etc/…, 0644, 0:0 |
root/etc/master.passwd |
copy of /private/etc/master.passwd, created 0600, 0:0 |
root/etc/pwd.db, root/etc/spwd.db |
root/usr/sbin/pwd_mkdb -p /etc/master.passwd, then 0644 and 0600; checked with root/usr/bin/id root when present |
root/etc/ssh/ssh_host_*_key |
root/usr/bin/ssh-keygen -A, after the databases |
Link text is read by the kernel, not by vroot. vroot shows the real root as
/rootfs, but a link stored as /rootfs/dev resolves to the physical
/rootfs/dev, which does not exist. vphoned wrote exactly that before #519,
and nothing created root/rootfs either. Every shell then printed
/dev/null: Directory nonexistent and sshd never started. On 26.6.2 in
iGhostVT, a fresh tab still printed the error with dev → /rootfs/dev plus
rootfs → /, and stopped printing it with dev → /dev. Irisin writes
package links the same way: its linkText turns /rootfs/x into /x.
Rules:
- A missing item is created; an existing item is left alone whatever its type,
owner or mode. A
root/tmpmade by hand keeps its owner. The one exception is the danglingdev → /rootfs/devearlier vphoned wrote. - The databases are rebuilt only when either is missing or older than
master.passwd, so a password changed withpasswdsurvives a restart. - Host keys are generated only when
root/etc/sshexists (openssh is installed) and a default key is missing.-Anever replaces a key. - vphoned runs
pwd_mkdb,idandssh-keygenby physical path. Each loads libroothide through the.jbrootlinkensureRootHideLinksseeds beside it, so its arguments are vroot paths. - On a first install the bootstrap has no
pwd_mkdborssh-keygenyet. Those steps are reported underdeferredin the install result. They run one second after the next package operation rewrites the root'sLibrary/dpkg, or on the next vphoned start, so sshd has host keys as soon as openssh is installed. - Any other failure names the path. At install it rolls the install back like the other RootHide steps; at startup it is logged.
Open issues
- PAM. With accounts and keys in place, the bootstrap's sshd (OpenSSH 9.2,
UsePAM yes) failed withPAM: initialisation failed, althoughroot/etc/pam.d/sshdand every module it names exist; with-o UsePAM=no, password login worked. The modules inroot/usr/lib/pamload libroothide through@loader_path/.jbroot, and that directory had no link.ensureRootHideLinksnow seedsusr/lib/pam/.jbroot → ../../../.jbroot(#515). On an existing RootHide VM, startup recreated the link and root SSH worked with public-key and password authentication. A fresh restore with the package's unmodifiedsshd_confighas not been checked yet. vphoned never editssshd_config, the package's conffile. - Alternatives links such as
/usr/bin/pageronly exist when maintainer scripts ran. Check whether Irisin's Bootstrap Install creates them on a fresh vphone; if it does not, that is an Irisin bug.
Validation
On a freshly restored vphone, with Irisin installed through vphoned, after Irisin's Bootstrap Install and one restart, without manual changes:
# under vroot, in iGhostVT or over ssh
ls -ld /tmp /var/tmp /var/root # 1777 dir, link, 0700 dir
ls /dev /rootfs/private # both list the real root's entries
: > /dev/null && echo dev-ok
id root && id mobile # both resolve
ls -l /etc/pwd.db /etc/spwd.db /etc/ssh/ssh_host_*_key
- iGhostVT opens a shell.
root/usr/lib/pam/.jbrootresolves to the root, and from the Macssh mobile@<guest address>logs in with the package'ssshd_config(UsePAM yes).- Change a password with
passwd, restart, and confirm it still works and that the startup log reports nothing created.