fix: firstboot fstab handling — no duplicates, compatible with Asahi sed
Some checks failed
Some checks failed
- Replace sed with grep -v / awk for fstab manipulation (Asahi Fedora's sed doesn't support \| delimiter or \? quantifier) - Use idempotent write_lab_fstab function: removes all old entries first, comments out conflicting btrfs subvol entries, adds fresh LVM entries - Fix sed for SSH hardening: use #* instead of \? (POSIX compatible) - Tested on Mac Studio: no duplicate fstab entries after multiple runs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -111,6 +111,29 @@ mount_lv() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ── Write fstab function (idempotent) ────────────────────────────
|
||||||
|
write_lab_fstab() {
|
||||||
|
# Remove any previous lab LVM entries (clean slate)
|
||||||
|
sed -i '/# lab-lvm:/d' /etc/fstab
|
||||||
|
sed -i '/# Lab LVM volumes/d' /etc/fstab
|
||||||
|
grep -v "/dev/labvg/" /etc/fstab > /etc/fstab.tmp && mv /etc/fstab.tmp /etc/fstab
|
||||||
|
# Comment out non-LVM entries for mount points we manage
|
||||||
|
for mp in "/var " "/var/log " "/home " "/srv "; do
|
||||||
|
if grep -q "$mp" /etc/fstab; then
|
||||||
|
awk -v m="$mp" '{if($0 !~ /^#/ && index($0,m)) print "# lab-lvm: " $0; else print}' /etc/fstab > /etc/fstab.tmp
|
||||||
|
mv /etc/fstab.tmp /etc/fstab
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# Add fresh LVM entries
|
||||||
|
echo "# Lab LVM volumes" >> /etc/fstab
|
||||||
|
echo "/dev/labvg/swap none swap defaults 0 0" >> /etc/fstab
|
||||||
|
echo "/dev/labvg/var /var xfs defaults 0 0" >> /etc/fstab
|
||||||
|
echo "/dev/labvg/varlog /var/log xfs defaults 0 0" >> /etc/fstab
|
||||||
|
echo "/dev/labvg/home /home xfs defaults 0 0" >> /etc/fstab
|
||||||
|
echo "/dev/labvg/srv /srv xfs defaults 0 0" >> /etc/fstab
|
||||||
|
${roleFstabLines.join('\n ')}
|
||||||
|
}
|
||||||
|
|
||||||
# ── Check for existing VG ────────────────────────────────────────
|
# ── Check for existing VG ────────────────────────────────────────
|
||||||
if vgs labvg &>/dev/null; then
|
if vgs labvg &>/dev/null; then
|
||||||
echo "Volume group 'labvg' already exists — reprovision detected."
|
echo "Volume group 'labvg' already exists — reprovision detected."
|
||||||
@@ -129,16 +152,8 @@ ${roleMountLines.map(l => ` ${l}`).join('\n')}
|
|||||||
echo " Enabled swap"
|
echo " Enabled swap"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure fstab entries exist
|
# Ensure fstab entries exist — comment out conflicting btrfs subvol entries
|
||||||
grep -q "labvg" /etc/fstab || {
|
write_lab_fstab
|
||||||
echo "# Lab LVM volumes (re-added after reprovision)" >> /etc/fstab
|
|
||||||
echo "/dev/labvg/swap none swap defaults 0 0" >> /etc/fstab
|
|
||||||
echo "/dev/labvg/var /var xfs defaults 0 0" >> /etc/fstab
|
|
||||||
echo "/dev/labvg/varlog /var/log xfs defaults 0 0" >> /etc/fstab
|
|
||||||
echo "/dev/labvg/home /home xfs defaults 0 0" >> /etc/fstab
|
|
||||||
echo "/dev/labvg/srv /srv xfs defaults 0 0" >> /etc/fstab
|
|
||||||
${roleFstabLines.map(l => ` ${l}`).join('\n')}
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "Existing LVM volumes re-mounted."
|
echo "Existing LVM volumes re-mounted."
|
||||||
else
|
else
|
||||||
@@ -207,15 +222,7 @@ echo "NOTE: /var and /var/log will switch to LVM on next reboot."
|
|||||||
# Enable swap
|
# Enable swap
|
||||||
swapon /dev/labvg/swap 2>/dev/null || true
|
swapon /dev/labvg/swap 2>/dev/null || true
|
||||||
|
|
||||||
# Write fstab entries
|
write_lab_fstab
|
||||||
echo "" >> /etc/fstab
|
|
||||||
echo "# Lab LVM volumes" >> /etc/fstab
|
|
||||||
echo "/dev/labvg/swap none swap defaults 0 0" >> /etc/fstab
|
|
||||||
echo "/dev/labvg/var /var xfs defaults 0 0" >> /etc/fstab
|
|
||||||
echo "/dev/labvg/varlog /var/log xfs defaults 0 0" >> /etc/fstab
|
|
||||||
echo "/dev/labvg/home /home xfs defaults 0 0" >> /etc/fstab
|
|
||||||
echo "/dev/labvg/srv /srv xfs defaults 0 0" >> /etc/fstab
|
|
||||||
${roleFstabLines.join('\n')}
|
|
||||||
|
|
||||||
echo "LVM setup complete."
|
echo "LVM setup complete."
|
||||||
lvs labvg
|
lvs labvg
|
||||||
@@ -256,8 +263,8 @@ ${rootSshKeyBlock}
|
|||||||
chmod 600 /root/.ssh/authorized_keys
|
chmod 600 /root/.ssh/authorized_keys
|
||||||
|
|
||||||
# ── Harden SSH (takes effect on next sshd restart/reboot) ────────
|
# ── Harden SSH (takes effect on next sshd restart/reboot) ────────
|
||||||
sed -i 's/^#\\?PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config
|
sed -i 's/^#*PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config
|
||||||
sed -i 's/^#\\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
|
sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
|
||||||
|
|
||||||
# ── Write provisioning metadata ──────────────────────────────────
|
# ── Write provisioning metadata ──────────────────────────────────
|
||||||
cat > /etc/lab-provisioned << LABMETA
|
cat > /etc/lab-provisioned << LABMETA
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export const enableIscsi: Operation = async (ctx): Promise<OperationResult> => {
|
|||||||
const isFedora = osLower.includes("fedora") || osLower.includes("rhel") || osLower.includes("centos");
|
const isFedora = osLower.includes("fedora") || osLower.includes("rhel") || osLower.includes("centos");
|
||||||
|
|
||||||
const pkg = isFedora ? "iscsi-initiator-utils" : "open-iscsi";
|
const pkg = isFedora ? "iscsi-initiator-utils" : "open-iscsi";
|
||||||
const installCmd = isFedora ? `dnf install -y ${pkg}` : `apt-get install -y ${pkg}`;
|
const installCmd = isFedora ? `sudo dnf install -y ${pkg}` : `sudo apt-get install -y ${pkg}`;
|
||||||
|
|
||||||
const install = await ctx.ssh.exec(installCmd, { timeoutMs: 120_000 });
|
const install = await ctx.ssh.exec(installCmd, { timeoutMs: 120_000 });
|
||||||
if (install.exitCode !== 0) {
|
if (install.exitCode !== 0) {
|
||||||
@@ -25,7 +25,7 @@ export const enableIscsi: Operation = async (ctx): Promise<OperationResult> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Enable and start
|
// Enable and start
|
||||||
await ctx.ssh.exec("systemctl enable --now iscsid", sshOpts(ctx));
|
await ctx.ssh.exec("sudo systemctl enable --now iscsid", sshOpts(ctx));
|
||||||
|
|
||||||
return { success: true, changed: true, message: `Installed ${pkg} and enabled iscsid` };
|
return { success: true, changed: true, message: `Installed ${pkg} and enabled iscsid` };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ function isServerRole(role: string): boolean {
|
|||||||
|
|
||||||
function generateServerConfig(config: K3sConfig): string {
|
function generateServerConfig(config: K3sConfig): string {
|
||||||
const tlsSans = [config.hostname, config.ip, ...(config.tlsSans ?? [])];
|
const tlsSans = [config.hostname, config.ip, ...(config.tlsSans ?? [])];
|
||||||
|
const serverLine = config.k3sServerUrl ? `server: "${config.k3sServerUrl}"\ntoken: "${config.k3sToken}"\n` : "";
|
||||||
return `# k3s server configuration — CIS hardened
|
return `# k3s server configuration — CIS hardened
|
||||||
protect-kernel-defaults: true
|
${serverLine}protect-kernel-defaults: true
|
||||||
secrets-encryption: true
|
secrets-encryption: true
|
||||||
write-kubeconfig-mode: "0640"
|
write-kubeconfig-mode: "0640"
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,12 @@ export const installK3sBinary: Operation = async (ctx): Promise<OperationResult>
|
|||||||
const alreadyInstalled = version.exitCode === 0;
|
const alreadyInstalled = version.exitCode === 0;
|
||||||
|
|
||||||
if (isServer) {
|
if (isServer) {
|
||||||
|
// If joining an existing cluster, pass K3S_URL and K3S_TOKEN
|
||||||
|
const joinEnv = ctx.config.k3sServerUrl && ctx.config.k3sToken
|
||||||
|
? `K3S_URL="${ctx.config.k3sServerUrl}" K3S_TOKEN="${ctx.config.k3sToken}"`
|
||||||
|
: "";
|
||||||
const result = await ctx.ssh.exec(
|
const result = await ctx.ssh.exec(
|
||||||
'curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="server" INSTALL_K3S_SKIP_SELINUX_RPM=true sh -',
|
`curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="server" INSTALL_K3S_SKIP_SELINUX_RPM=true ${joinEnv} sh -`,
|
||||||
{ timeoutMs: 300_000 },
|
{ timeoutMs: 300_000 },
|
||||||
);
|
);
|
||||||
if (result.exitCode !== 0) {
|
if (result.exitCode !== 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user