penguins-eggs 25.12.7 → 25.12.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.oclif.manifest.json +1 -1
- package/README.md +74 -45
- package/README.pdf +13450 -14838
- package/addons/eggs/theme/livecd/simple.grub.main.cfg +3 -3
- package/conf/distros/buster/calamares/calamares-modules/cleanup/cleanup.sh +1 -1
- package/conf/distros/focal/calamares/calamares-modules/cleanup/cleanup.sh +1 -1
- package/conf/distros/noble/calamares/calamares-modules/cleanup/cleanup.sh +1 -1
- package/conf/distros/noble/calamares/libexec/calamares-l10n-helper.sh +2 -1
- package/conf/distros/noble/calamares/settings.yml +1 -0
- package/conf/distros/trixie/calamares/calamares-modules/cleanup/cleanup.sh +1 -1
- package/dist/classes/cli-autologin.d.ts +37 -4
- package/dist/classes/cli-autologin.js +125 -112
- package/dist/classes/daddy.js +4 -1
- package/dist/classes/incubation/fisherman-helper/initcpio.d.ts +3 -2
- package/dist/classes/incubation/fisherman-helper/initcpio.js +25 -20
- package/dist/classes/incubation/incubator.d/manjaro.js +1 -0
- package/dist/classes/ovary.d/edit-live-fs.d.ts +1 -1
- package/dist/classes/ovary.d/edit-live-fs.js +15 -122
- package/dist/classes/ovary.d/fertilization.js +1 -1
- package/dist/classes/ovary.d/luks-home.js +33 -19
- package/dist/classes/ovary.d/luks-root.d.ts +1 -2
- package/dist/classes/ovary.d/luks-root.js +46 -27
- package/dist/classes/ovary.d/luks-shrink.d.ts +14 -0
- package/dist/classes/ovary.d/luks-shrink.js +86 -0
- package/dist/classes/ovary.d/produce.js +63 -21
- package/dist/classes/ovary.d.ts +3 -1
- package/dist/classes/ovary.js +3 -1
- package/dist/classes/utils.js +1 -1
- package/dist/classes/yolk.js +1 -1
- package/dist/commands/produce.js +11 -7
- package/dist/interfaces/calamares/i-calamares-branding.d.ts +56 -38
- package/dist/interfaces/calamares/i-calamares-branding.js +10 -0
- package/dist/krill/classes/prepare.d/users.js +1 -1
- package/dist/krill/classes/sequence.d/fstab.js +1 -1
- package/dist/krill/classes/sequence.d/mkfs.js +1 -1
- package/dist/krill/classes/sequence.d/unpackfs.js +2 -2
- package/dist/krill/classes/sequence.d.ts +1 -5
- package/dist/krill/classes/sequence.js +26 -29
- package/dist/krill/components/finished.js +2 -2
- package/dist/krill/components/install.js +2 -2
- package/dist/krill/components/keyboard.js +2 -2
- package/dist/krill/components/location.js +2 -2
- package/dist/krill/components/network.js +2 -2
- package/dist/krill/components/partitions.js +2 -2
- package/dist/krill/components/summary.js +2 -2
- package/dist/krill/components/users.js +2 -2
- package/dist/krill/components/welcome.js +2 -2
- package/dist/lib/utils.d.ts +1 -0
- package/dist/lib/utils.js +46 -0
- package/manpages/doc/man/eggs.1.gz +0 -0
- package/manpages/doc/man/eggs.html +8 -8
- package/package.json +2 -2
- package/scripts/restore_homecrypt_krill.sh +93 -0
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* email: piero.proietti@gmail.com
|
|
6
6
|
* license: MIT
|
|
7
7
|
*/
|
|
8
|
-
// packages
|
|
9
8
|
import fs from 'fs';
|
|
10
9
|
import os from 'os';
|
|
11
10
|
import path from 'node:path';
|
|
@@ -16,11 +15,11 @@ import Systemctl from '../systemctl.js';
|
|
|
16
15
|
import { exec } from '../../lib/utils.js';
|
|
17
16
|
// _dirname
|
|
18
17
|
const __dirname = path.dirname(new URL(import.meta.url).pathname);
|
|
19
|
-
export async function editLiveFs(
|
|
18
|
+
export async function editLiveFs() {
|
|
20
19
|
if (this.verbose)
|
|
21
20
|
console.log('Ovary: editLiveFs');
|
|
22
21
|
const workDir = this.settings.work_dir.merged;
|
|
23
|
-
if (clone) {
|
|
22
|
+
if (this.clone || this.homecrypt || this.fullcrypt) {
|
|
24
23
|
await exec(`touch ${workDir}/etc/penguins-eggs.d/is_clone`, this.echo);
|
|
25
24
|
}
|
|
26
25
|
if (Pacman.packageIsInstalled('epoptes')) {
|
|
@@ -38,15 +37,6 @@ export async function editLiveFs(clone = false) {
|
|
|
38
37
|
await exec(cmd, this.echo);
|
|
39
38
|
cmd = `find ${workDir}/var/log/ -type f -exec truncate -s 0 {} \\;`;
|
|
40
39
|
await exec(cmd, this.echo);
|
|
41
|
-
// =========================================================================
|
|
42
|
-
// FIX DEFINITIVO PER DEVUAN/SYSVINIT (Init Script Hardening)
|
|
43
|
-
// =========================================================================
|
|
44
|
-
if (Utils.isSysvinit()) {
|
|
45
|
-
if (this.verbose)
|
|
46
|
-
console.log('SysVinit detected: Hardening init scripts...');
|
|
47
|
-
await patchInitScripts(workDir, this.verbose);
|
|
48
|
-
}
|
|
49
|
-
// =========================================================================
|
|
50
40
|
// Fix Symlinks /var/run e /var/lock
|
|
51
41
|
const varRun = `${workDir}/var/run`;
|
|
52
42
|
if (fs.existsSync(varRun) && !fs.lstatSync(varRun).isSymbolicLink()) {
|
|
@@ -85,16 +75,20 @@ export async function editLiveFs(clone = false) {
|
|
|
85
75
|
if (fs.existsSync(`${workDir}/etc/crypttab`)) {
|
|
86
76
|
await exec(`rm ${workDir}/etc/crypttab`, this.echo);
|
|
87
77
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
78
|
+
/**
|
|
79
|
+
* machine-id
|
|
80
|
+
*/
|
|
81
|
+
await exec(`rm -f ${workDir}/etc/machine-id`);
|
|
82
|
+
await exec(`rm -f ${workDir}/var/lib/dbus/machine-id`);
|
|
83
|
+
if (Utils.isSysvinit()) {
|
|
84
|
+
await exec(`chroot ${workDir} dbus-uuidgen --ensure=/etc/machine-id`);
|
|
85
|
+
await exec(`ln -sf /etc/machine-id ${workDir}/var/lib/dbus/machine-id`);
|
|
86
|
+
// const machineId = crypto.randomBytes(16).toString('hex')
|
|
87
|
+
// fs.writeFileSync(`${workDir}/etc/machine-id`, machineId + '\n')
|
|
88
|
+
// fs.writeFileSync(`${workDir}/var/lib/dbus/machine-id`, machineId + '\n')
|
|
94
89
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
await exec(`rm ${workDir}/var/lib/dbus/machine-id`, this.echo);
|
|
90
|
+
else if (Utils.isSystemd()) {
|
|
91
|
+
await exec(`touch ${workDir}/etc/machine-id`);
|
|
98
92
|
}
|
|
99
93
|
if (fs.existsSync(`${workDir}/boot/grub/fonts/unicode.pf2`)) {
|
|
100
94
|
shx.cp(`${workDir}/boot/grub/fonts/unicode.pf2`, `${workDir}/boot/grub/fonts/UbuntuMono16.pf2`);
|
|
@@ -166,104 +160,3 @@ export async function editLiveFs(clone = false) {
|
|
|
166
160
|
await exec(`chmod 1777 ${workDir}/tmp`, this.echo);
|
|
167
161
|
}
|
|
168
162
|
}
|
|
169
|
-
/**
|
|
170
|
-
* Patcha direttamente gli script di init in /etc/init.d/
|
|
171
|
-
*/
|
|
172
|
-
async function patchInitScripts(workDir, verbose) {
|
|
173
|
-
// 1. PATCH DBUS (Il più importante)
|
|
174
|
-
const dbusScript = `${workDir}/etc/init.d/dbus`;
|
|
175
|
-
if (fs.existsSync(dbusScript)) {
|
|
176
|
-
if (verbose)
|
|
177
|
-
console.log(`Patching ${dbusScript} to fix missing directories and RO fs...`);
|
|
178
|
-
let content = fs.readFileSync(dbusScript, 'utf8');
|
|
179
|
-
let modified = false;
|
|
180
|
-
// PATCH A: Header con gestione Ramdisk Fallback e Mount Proc
|
|
181
|
-
// Questa intestazione viene inserita all'inizio e prova a montare tmpfs se non può scrivere
|
|
182
|
-
const dbusFix = `
|
|
183
|
-
### EGGS-FIX-START
|
|
184
|
-
# 1. Assicuriamoci che /proc sia montato (necessario per leggere uuid kernel)
|
|
185
|
-
if ! mountpoint -q /proc/; then
|
|
186
|
-
mount -t proc proc /proc
|
|
187
|
-
fi
|
|
188
|
-
|
|
189
|
-
# 2. Gestione Filesystem Read-Only (RO)
|
|
190
|
-
# Se non possiamo scrivere in /var/lib/dbus, montiamo un tmpfs (RAM) sopra.
|
|
191
|
-
# Questo bypassa il blocco dell'overlayfs non ancora pronto tipico delle build AppImage.
|
|
192
|
-
if ! mkdir -p /var/lib/dbus 2>/dev/null || ! touch /var/lib/dbus/.rw_check 2>/dev/null; then
|
|
193
|
-
echo "EGGS-DEBUG: Filesystem Read-Only detected! Mounting tmpfs on /var/lib/dbus" > /dev/console
|
|
194
|
-
mount -t tmpfs -o size=1m tmpfs /var/lib/dbus
|
|
195
|
-
fi
|
|
196
|
-
rm -f /var/lib/dbus/.rw_check
|
|
197
|
-
|
|
198
|
-
# 3. Creazione Directory Runtime
|
|
199
|
-
mkdir -p /var/run/dbus /var/lib/dbus
|
|
200
|
-
chmod 755 /var/run/dbus /var/lib/dbus
|
|
201
|
-
|
|
202
|
-
# 4. Generazione Machine ID (Non-Blocking Strategy)
|
|
203
|
-
# Se il file non esiste (o è vuoto), lo creiamo.
|
|
204
|
-
if [ ! -s /var/lib/dbus/machine-id ]; then
|
|
205
|
-
# Prova A: Usa UUID del kernel (Istantaneo, non blocca)
|
|
206
|
-
if [ -f /proc/sys/kernel/random/uuid ]; then
|
|
207
|
-
cat /proc/sys/kernel/random/uuid | tr -d '-' > /var/lib/dbus/machine-id
|
|
208
|
-
# Prova B: Fallback statico
|
|
209
|
-
else
|
|
210
|
-
echo "00000000000000000000000000000001" > /var/lib/dbus/machine-id
|
|
211
|
-
fi
|
|
212
|
-
chmod 644 /var/lib/dbus/machine-id
|
|
213
|
-
fi
|
|
214
|
-
|
|
215
|
-
# 5. Sync /etc/machine-id (Best effort, ignora errori se /etc è RO)
|
|
216
|
-
if [ ! -s /etc/machine-id ] && [ -s /var/lib/dbus/machine-id ]; then
|
|
217
|
-
cp /var/lib/dbus/machine-id /etc/machine-id 2>/dev/null || true
|
|
218
|
-
fi
|
|
219
|
-
### EGGS-FIX-END
|
|
220
|
-
`;
|
|
221
|
-
if (!content.includes('EGGS-FIX-START')) {
|
|
222
|
-
content = content.replace('#!/bin/sh', `#!/bin/sh\n${dbusFix}`);
|
|
223
|
-
modified = true;
|
|
224
|
-
}
|
|
225
|
-
// PATCH B: SABOTAGE PREVENTION (Questa è quella che mancava!)
|
|
226
|
-
// Impediamo che create_machineid cancelli il file che abbiamo appena creato
|
|
227
|
-
// perché l'uptime è basso.
|
|
228
|
-
if (content.includes('rm -f "${MACHINEID}"')) {
|
|
229
|
-
if (verbose)
|
|
230
|
-
console.log('Patching dbus: disabling auto-delete of machine-id on boot');
|
|
231
|
-
content = content.replace('rm -f "${MACHINEID}"', ': # EGGS-PATCH: Prevent deletion of valid machine-id');
|
|
232
|
-
modified = true;
|
|
233
|
-
}
|
|
234
|
-
if (modified) {
|
|
235
|
-
fs.writeFileSync(dbusScript, content, 'utf8');
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
// 2. PATCH RSYSLOG
|
|
239
|
-
const rsyslogScript = `${workDir}/etc/init.d/rsyslog`;
|
|
240
|
-
if (fs.existsSync(rsyslogScript)) {
|
|
241
|
-
let content = fs.readFileSync(rsyslogScript, 'utf8');
|
|
242
|
-
const fix = `
|
|
243
|
-
### EGGS-FIX-START
|
|
244
|
-
mkdir -p /var/spool/rsyslog
|
|
245
|
-
chmod 755 /var/spool/rsyslog
|
|
246
|
-
### EGGS-FIX-END
|
|
247
|
-
`;
|
|
248
|
-
if (!content.includes('EGGS-FIX-START')) {
|
|
249
|
-
content = content.replace('#!/bin/sh', `#!/bin/sh\n${fix}`);
|
|
250
|
-
fs.writeFileSync(rsyslogScript, content, 'utf8');
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
// 3. PATCH CRON
|
|
254
|
-
const cronScript = `${workDir}/etc/init.d/cron`;
|
|
255
|
-
if (fs.existsSync(cronScript)) {
|
|
256
|
-
let content = fs.readFileSync(cronScript, 'utf8');
|
|
257
|
-
const fix = `
|
|
258
|
-
### EGGS-FIX-START
|
|
259
|
-
mkdir -p /var/spool/cron/crontabs
|
|
260
|
-
chmod 1730 /var/spool/cron/crontabs
|
|
261
|
-
chown root:crontab /var/spool/cron/crontabs 2>/dev/null || true
|
|
262
|
-
### EGGS-FIX-END
|
|
263
|
-
`;
|
|
264
|
-
if (!content.includes('EGGS-FIX-START')) {
|
|
265
|
-
content = content.replace('#!/bin/sh', `#!/bin/sh\n${fix}`);
|
|
266
|
-
fs.writeFileSync(cronScript, content, 'utf8');
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
@@ -21,7 +21,7 @@ export async function fertilization(snapshot_prefix = '', snapshot_basename = ''
|
|
|
21
21
|
this.familyId = distro.familyId;
|
|
22
22
|
this.distroId = distro.distroId;
|
|
23
23
|
this.distroLike = distro.distroLike;
|
|
24
|
-
this.
|
|
24
|
+
this.distroLiveMediumPath = distro.liveMediumPath;
|
|
25
25
|
this.settings = new Settings();
|
|
26
26
|
if (await this.settings.load()) {
|
|
27
27
|
await this.settings.loadRemix(this.theme);
|
|
@@ -27,7 +27,7 @@ export async function luksHome(clone = false, homecrypt = false) {
|
|
|
27
27
|
try {
|
|
28
28
|
/**
|
|
29
29
|
* this.luksMappedName = 'home.img';
|
|
30
|
-
* this.luksFile = `/tmp/${luksMappedName}`
|
|
30
|
+
* this.luksFile = `/var/tmp/${luksMappedName}`
|
|
31
31
|
* this.luksDevice = `/dev/mapper/${luksMappedName}`
|
|
32
32
|
* this.luksMountpoint = `/tmp/mnt/${luksMappedName}`
|
|
33
33
|
* this.luksPassword = '0'
|
|
@@ -42,15 +42,21 @@ export async function luksHome(clone = false, homecrypt = false) {
|
|
|
42
42
|
// Utils.warning('1. Calculation of space requirements...')
|
|
43
43
|
let sizeString = (await exec('du -sb --exclude=/home/eggs /home', { capture: true })).data.trim().split(/\s+/)[0];
|
|
44
44
|
let size = Number.parseInt(sizeString, 10);
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
45
|
+
const fsOverhead = Math.ceil(size * 0.05);
|
|
46
|
+
const luksHeader = 32 * 1024 * 1024;
|
|
47
|
+
const safetyBuffer = 100 * 1024 * 1024;
|
|
48
|
+
let calculatedSize = size + fsOverhead + luksHeader + safetyBuffer;
|
|
49
|
+
const minSize = 64 * 1024 * 1024;
|
|
50
|
+
if (calculatedSize < minSize)
|
|
51
|
+
calculatedSize = minSize;
|
|
52
|
+
const alignment = 4 * 1024 * 1024;
|
|
53
|
+
const luksSize = Math.ceil(calculatedSize / alignment) * alignment;
|
|
54
|
+
warning(`------------------------------------------`);
|
|
55
|
+
warning(`HOME CRYPT CALCULATION (Read-Only):`);
|
|
56
|
+
warning(` Data Payload: ${bytesToGB(size)}`);
|
|
57
|
+
warning(` Overhead+Buf: ${bytesToGB(luksSize - size)}`);
|
|
58
|
+
warning(` TOTAL SIZE: ${bytesToGB(luksSize)}`);
|
|
59
|
+
warning(`------------------------------------------`);
|
|
54
60
|
warning(`creating partition LUKS: ${this.luksFile}`);
|
|
55
61
|
await this.luksExecuteCommand('truncate', ['--size', `${luksSize}`, this.luksFile]);
|
|
56
62
|
warning(`formatting ${this.luksFile} as a LUKS volume...`);
|
|
@@ -59,7 +65,7 @@ export async function luksHome(clone = false, homecrypt = false) {
|
|
|
59
65
|
await this.luksExecuteCommand('cryptsetup', luksFormatArgs, `${this.luksPassword}\n`);
|
|
60
66
|
warning(`opening the LUKS volume. It will be mapped to ${this.luksDevice}`);
|
|
61
67
|
await this.luksExecuteCommand('cryptsetup', ['luksOpen', this.luksFile, this.luksMappedName], `${this.luksPassword}\n`);
|
|
62
|
-
warning(`formatting
|
|
68
|
+
warning(`formatting ext4 `);
|
|
63
69
|
await exec(`mkfs.ext4 -L live-home ${this.luksDevice}`, this.echo);
|
|
64
70
|
warning(`mounting ${this.luksDevice} on ${this.luksMountpoint}`);
|
|
65
71
|
if (fs.existsSync(this.luksMountpoint)) {
|
|
@@ -74,17 +80,21 @@ export async function luksHome(clone = false, homecrypt = false) {
|
|
|
74
80
|
await exec(`mount /dev/mapper/${this.luksMappedName} ${this.luksMountpoint}`, this.echo);
|
|
75
81
|
warning(`copying /home on ${this.luksMountpoint}`);
|
|
76
82
|
await exec(`rsync -ah --exclude='eggs' /home/ ${this.luksMountpoint}`, this.echo);
|
|
83
|
+
/**
|
|
84
|
+
* utenti e gruppi in .system-backup
|
|
85
|
+
*/
|
|
77
86
|
warning(`saving user accounts info...`);
|
|
78
|
-
// Crea directory per backup system files
|
|
79
87
|
await exec(`mkdir -p ${this.luksMountpoint}/.system-backup`, this.echo);
|
|
80
|
-
//
|
|
88
|
+
// passwd/shadow: solo utenti con UID >= 1000
|
|
81
89
|
await exec(`awk -F: '$3 >= 1000 {print}' /etc/passwd > ${this.luksMountpoint}/.system-backup/passwd`, this.echo);
|
|
82
90
|
await exec(`awk -F: '$3 >= 1000 {print}' /etc/shadow > ${this.luksMountpoint}/.system-backup/shadow`, this.echo);
|
|
83
|
-
//
|
|
91
|
+
// group/gshadow TUTTI
|
|
84
92
|
// Gli utenti possono appartenere a gruppi di sistema (sudo, audio, video, etc.)
|
|
85
93
|
await exec(`cp /etc/group ${this.luksMountpoint}/.system-backup/group`, this.echo);
|
|
86
94
|
await exec(`cp /etc/gshadow ${this.luksMountpoint}/.system-backup/gshadow`, this.echo);
|
|
87
|
-
|
|
95
|
+
/**
|
|
96
|
+
* saving display manager (autologin) configs...
|
|
97
|
+
*/
|
|
88
98
|
warning(`saving display manager configuration...`);
|
|
89
99
|
// GDM (gdm3 è comune su Debian/Ubuntu)
|
|
90
100
|
await exec(`[ -e /etc/gdm3 ] && cp -a /etc/gdm3 ${this.luksMountpoint}/.system-backup/`, this.echo);
|
|
@@ -95,10 +105,10 @@ export async function luksHome(clone = false, homecrypt = false) {
|
|
|
95
105
|
// SDDM (sia file .conf che directory .conf.d)
|
|
96
106
|
await exec(`[ -e /etc/sddm.conf ] && cp -a /etc/sddm.conf ${this.luksMountpoint}/.system-backup/`, this.echo);
|
|
97
107
|
await exec(`[ -e /etc/sddm.conf.d ] && cp -a /etc/sddm.conf.d ${this.luksMountpoint}/.system-backup/`, this.echo);
|
|
98
|
-
warning(`
|
|
99
|
-
await exec(
|
|
100
|
-
|
|
101
|
-
await this.
|
|
108
|
+
warning(`Syncing filesystem on ${this.luksMountpoint}...`);
|
|
109
|
+
await exec('sync', this.echo); // Forza scrittura dati su disco
|
|
110
|
+
// Shrink()
|
|
111
|
+
await this.luksShrink();
|
|
102
112
|
warning(`moving ${this.luksMappedName} to (ISO)/live/.`);
|
|
103
113
|
await exec(`mv ${this.luksFile} ${this.settings.iso_work}/live`, this.echo);
|
|
104
114
|
warning('encryption process successfully completed!');
|
|
@@ -117,6 +127,10 @@ export async function luksHome(clone = false, homecrypt = false) {
|
|
|
117
127
|
if (fs.existsSync(this.luksDevice)) {
|
|
118
128
|
await this.luksExecuteCommand('cryptsetup', ['close', this.luksMappedName]).catch(() => { });
|
|
119
129
|
}
|
|
130
|
+
if (fs.existsSync(this.luksFile)) {
|
|
131
|
+
Utils.warning(`Removing temporary container: ${this.luksFile}`);
|
|
132
|
+
fs.unlinkSync(this.luksFile);
|
|
133
|
+
}
|
|
120
134
|
await Utils.pressKeyToExit();
|
|
121
135
|
process.exit(1);
|
|
122
136
|
}
|
|
@@ -12,8 +12,7 @@ import { exec } from '../../lib/utils.js';
|
|
|
12
12
|
const noop = () => { };
|
|
13
13
|
/**
|
|
14
14
|
* luksRoot()
|
|
15
|
-
*
|
|
16
|
-
* create a container LUKS with the entire
|
|
15
|
+
* * create a container LUKS with the entire
|
|
17
16
|
* filesystem.squashfs
|
|
18
17
|
*/
|
|
19
18
|
export async function luksRoot() {
|
|
@@ -29,7 +28,7 @@ export async function luksRoot() {
|
|
|
29
28
|
try {
|
|
30
29
|
/**
|
|
31
30
|
* this.luksMappedName = 'root.img';
|
|
32
|
-
* this.luksFile = `/tmp/${luksMappedName}`
|
|
31
|
+
* this.luksFile = `/var/tmp/${luksMappedName}`
|
|
33
32
|
* this.luksDevice = `/dev/mapper/${luksMappedName}`
|
|
34
33
|
* this.luksMountpoint = `/tmp/mnt/${luksMappedName}`
|
|
35
34
|
* this.luksPassword = '0'
|
|
@@ -45,11 +44,29 @@ export async function luksRoot() {
|
|
|
45
44
|
throw new Error(`filesystem.squashfs not found at: ${live_fs}`);
|
|
46
45
|
}
|
|
47
46
|
const stats = fs.statSync(live_fs);
|
|
48
|
-
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
const size = stats.size; // Dimensione REALE del file in Byte
|
|
48
|
+
// -------------------------------------------------------------
|
|
49
|
+
// CALCOLO DIMENSIONE OTTIMIZZATA (No shrink, safe allocation)
|
|
50
|
+
// -------------------------------------------------------------
|
|
51
|
+
// 1. Overhead Ext4: Inode, bitmap, superblocchi. ~3-4% è standard senza journal.
|
|
52
|
+
const fsOverhead = Math.ceil(size * 0.04);
|
|
53
|
+
// 2. Header LUKS: Solitamente 16MB per LUKS2, usiamo 32MB per sicurezza.
|
|
54
|
+
const luksHeader = 32 * 1024 * 1024;
|
|
55
|
+
// 3. Margine di sicurezza (Buffer): 120MB fissi.
|
|
56
|
+
// Questo spazio evita errori di "Disk full" durante la copia dei metadati.
|
|
57
|
+
// Viene compensato dall'uso di "-m 0" nella formattazione.
|
|
58
|
+
const safetyBuffer = 120 * 1024 * 1024;
|
|
59
|
+
// 4. Somma totale
|
|
60
|
+
let calculatedSize = size + fsOverhead + luksHeader + safetyBuffer;
|
|
61
|
+
// 5. Allineamento a 4MB (Performance storage)
|
|
62
|
+
const alignment = 4 * 1024 * 1024;
|
|
63
|
+
const luksSize = Math.ceil(calculatedSize / alignment) * alignment;
|
|
64
|
+
warning(`------------------------------------------`);
|
|
65
|
+
warning(`SAFE SIZE CALCULATION (No Shrink):`);
|
|
66
|
+
warning(` Payload (SquashFS): ${bytesToGB(size)}`);
|
|
67
|
+
warning(` Calc. Overhead: ${bytesToGB(luksSize - size)}`);
|
|
68
|
+
warning(` TOTAL CONTAINER: ${bytesToGB(luksSize)}`);
|
|
69
|
+
warning(`------------------------------------------`);
|
|
53
70
|
warning(`creating partition LUKS: ${this.luksFile}`);
|
|
54
71
|
await this.luksExecuteCommand('truncate', ['--size', `${luksSize}`, this.luksFile]);
|
|
55
72
|
warning(`formatting ${this.luksFile} as a LUKS volume...`);
|
|
@@ -57,8 +74,13 @@ export async function luksRoot() {
|
|
|
57
74
|
await this.luksExecuteCommand('cryptsetup', luksFormatArgs, `${this.luksPassword}\n`);
|
|
58
75
|
warning(`opening the LUKS volume. It will be mapped to ${this.luksDevice}`);
|
|
59
76
|
await this.luksExecuteCommand('cryptsetup', ['luksOpen', this.luksFile, this.luksMappedName], `${this.luksPassword}\n`);
|
|
60
|
-
|
|
61
|
-
|
|
77
|
+
// -------------------------------------------------------------
|
|
78
|
+
// FORMATTAZIONE EXT4
|
|
79
|
+
// -m 0 : Imposta i blocchi riservati a 0% (recupera spazio)
|
|
80
|
+
// -O ^has_journal : Disabilita il journal (risparmia spazio e scritture)
|
|
81
|
+
// -------------------------------------------------------------
|
|
82
|
+
warning(`formatting ext4 (without journal, 0% reserved)...`);
|
|
83
|
+
await exec(`mkfs.ext4 -m 0 -O ^has_journal -L live-root ${this.luksDevice}`, this.echo);
|
|
62
84
|
warning(`mounting ${this.luksDevice} on ${this.luksMountpoint}`);
|
|
63
85
|
if (fs.existsSync(this.luksMountpoint)) {
|
|
64
86
|
if (!Utils.isMountpoint(this.luksMountpoint)) {
|
|
@@ -74,25 +96,18 @@ export async function luksRoot() {
|
|
|
74
96
|
await exec(`mv ${live_fs} ${this.luksMountpoint}/filesystem.squashfs`, this.echo);
|
|
75
97
|
warning(`Syncing filesystem on ${this.luksMountpoint}...`);
|
|
76
98
|
await exec('sync', this.echo); // Forza scrittura dati su disco
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
Utils.error(`Force unmount also failed: ${forceError}`);
|
|
87
|
-
// Considera se lanciare un errore qui per fermare il processo
|
|
88
|
-
});
|
|
89
|
-
// Lancia comunque l'errore originale per segnalare il problema
|
|
90
|
-
throw umountError;
|
|
91
|
-
}
|
|
92
|
-
warning(`closing LUKS volume ${this.luksFile}.`);
|
|
99
|
+
/**
|
|
100
|
+
* SHRINK DISABILITATO
|
|
101
|
+
* Non eseguiamo this.luksShrink() per evitare corruzione dati.
|
|
102
|
+
* Abbiamo calcolato la dimensione corretta all'inizio.
|
|
103
|
+
*/
|
|
104
|
+
// Procedura di chiusura manuale (sostituisce quella dentro shrink)
|
|
105
|
+
warning(`Unmounting ${this.luksMountpoint}...`);
|
|
106
|
+
await exec(`umount ${this.luksMountpoint}`, this.echo);
|
|
107
|
+
warning(`Closing LUKS volume ${this.luksMappedName}...`);
|
|
93
108
|
await this.luksExecuteCommand('cryptsetup', ['close', this.luksMappedName]);
|
|
94
109
|
warning(`moving ${this.luksMappedName} on (ISO)/live.`);
|
|
95
|
-
await exec(`mv ${this.luksFile} ${this.settings.iso_work}/live`, this.echo);
|
|
110
|
+
await exec(`mv ${this.luksFile} ${this.settings.iso_work}/live/root.img`, this.echo);
|
|
96
111
|
}
|
|
97
112
|
catch (error) {
|
|
98
113
|
if (error instanceof Error) {
|
|
@@ -108,6 +123,10 @@ export async function luksRoot() {
|
|
|
108
123
|
if (fs.existsSync(this.luksDevice)) {
|
|
109
124
|
await this.luksExecuteCommand('cryptsetup', ['close', this.luksMappedName]).catch(() => { });
|
|
110
125
|
}
|
|
126
|
+
if (fs.existsSync(this.luksFile)) {
|
|
127
|
+
Utils.warning(`Removing temporary container: ${this.luksFile}`);
|
|
128
|
+
fs.unlinkSync(this.luksFile);
|
|
129
|
+
}
|
|
111
130
|
await Utils.pressKeyToExit();
|
|
112
131
|
process.exit(1);
|
|
113
132
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ./src/classes/ovary.d/luks-shrink.ts
|
|
3
|
+
* penguins-eggs v.25.10.x / ecmascript 2020
|
|
4
|
+
* author: Piero Proietti
|
|
5
|
+
* email: piero.proietti@gmail.com
|
|
6
|
+
* license: MIT
|
|
7
|
+
*/
|
|
8
|
+
import Ovary from '../ovary.js';
|
|
9
|
+
/**
|
|
10
|
+
* luksShrink
|
|
11
|
+
* Riduce in sicurezza un volume LUKS (root o home) minimizzando lo spazio
|
|
12
|
+
* ma garantendo l'integrità dei dati e l'allineamento dei blocchi.
|
|
13
|
+
*/
|
|
14
|
+
export declare function luksShrink(this: Ovary): Promise<void>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ./src/classes/ovary.d/luks-shrink.ts
|
|
3
|
+
* penguins-eggs v.25.10.x / ecmascript 2020
|
|
4
|
+
* author: Piero Proietti
|
|
5
|
+
* email: piero.proietti@gmail.com
|
|
6
|
+
* license: MIT
|
|
7
|
+
*/
|
|
8
|
+
import Utils from '../utils.js';
|
|
9
|
+
import { exec } from '../../lib/utils.js';
|
|
10
|
+
const noop = () => { };
|
|
11
|
+
/**
|
|
12
|
+
* luksShrink
|
|
13
|
+
* Riduce in sicurezza un volume LUKS (root o home) minimizzando lo spazio
|
|
14
|
+
* ma garantendo l'integrità dei dati e l'allineamento dei blocchi.
|
|
15
|
+
*/
|
|
16
|
+
export async function luksShrink() {
|
|
17
|
+
const loggers = {
|
|
18
|
+
log: this.hidden ? noop : console.log,
|
|
19
|
+
warning: this.hidden ? noop : Utils.warning,
|
|
20
|
+
success: this.hidden ? noop : Utils.success,
|
|
21
|
+
info: this.hidden ? noop : Utils.info,
|
|
22
|
+
};
|
|
23
|
+
const { log, warning, success, info } = loggers;
|
|
24
|
+
warning(`Unmounting ${this.luksMountpoint} to perform shrinking...`);
|
|
25
|
+
// Usa -l (lazy) se necessario, ma meglio umount pulito
|
|
26
|
+
await exec(`umount ${this.luksMountpoint}`, this.echo);
|
|
27
|
+
// 1. Controllo integrità (CRUCIALE per homecrypt: ripara eventuali errori prima di ridimensionare)
|
|
28
|
+
warning(`Checking filesystem integrity...`);
|
|
29
|
+
await exec(`e2fsck -f -y ${this.luksDevice}`, this.echo);
|
|
30
|
+
// 2. Riduzione del filesystem al minimo
|
|
31
|
+
warning(`Shrinking filesystem to minimum size...`);
|
|
32
|
+
// resize2fs -M è sicuro se poi lasciamo margine nel contenitore
|
|
33
|
+
await exec(`resize2fs -M ${this.luksDevice}`, this.echo);
|
|
34
|
+
// 3. Calcolo della nuova dimensione del filesystem
|
|
35
|
+
warning(`Calculating new sizes...`);
|
|
36
|
+
const tuneOutput = (await exec(`tune2fs -l ${this.luksDevice}`, { capture: true })).data;
|
|
37
|
+
const blockSizeMatch = tuneOutput.match(/Block size:\s+(\d+)/);
|
|
38
|
+
const blockCountMatch = tuneOutput.match(/Block count:\s+(\d+)/);
|
|
39
|
+
if (!blockSizeMatch || !blockCountMatch) {
|
|
40
|
+
throw new Error("Could not determine filesystem size from tune2fs");
|
|
41
|
+
}
|
|
42
|
+
const blockSize = parseInt(blockSizeMatch[1], 10);
|
|
43
|
+
const blockCount = parseInt(blockCountMatch[1], 10);
|
|
44
|
+
const fsSizeBytes = blockSize * blockCount;
|
|
45
|
+
warning(`Actual Ext4 payload size: ${bytesToGB(fsSizeBytes)}`);
|
|
46
|
+
// 4. Calcolo dell'offset LUKS (Header size)
|
|
47
|
+
const statusOutput = (await exec(`cryptsetup status ${this.luksMappedName}`, { capture: true })).data;
|
|
48
|
+
const offsetMatch = statusOutput.match(/offset:\s+(\d+)\s+sectors/);
|
|
49
|
+
let luksHeaderBytes = 0;
|
|
50
|
+
if (offsetMatch && offsetMatch[1]) {
|
|
51
|
+
luksHeaderBytes = parseInt(offsetMatch[1], 10) * 512;
|
|
52
|
+
warning(`Detected LUKS header: ${bytesToGB(luksHeaderBytes)}`);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
luksHeaderBytes = 32 * 1024 * 1024; // Fallback 32MB
|
|
56
|
+
warning(`Could not detect LUKS offset, using safe fallback: 32MB`);
|
|
57
|
+
}
|
|
58
|
+
// 5. Calcolo dimensione finale SICURA
|
|
59
|
+
// Usiamo 200MB di margine. Su una ISO da 4GB è il 5%, un prezzo onesto per la stabilità.
|
|
60
|
+
// Questo spazio extra protegge sia lo squashfs (root) che i metadati sparsi (home).
|
|
61
|
+
const safetyMargin = 200 * 1024 * 1024;
|
|
62
|
+
let rawFinalSize = fsSizeBytes + luksHeaderBytes + safetyMargin;
|
|
63
|
+
// ALLINEAMENTO A 1MB: Fondamentale per evitare errori di I/O su device fisici o loop
|
|
64
|
+
const alignBlock = 1024 * 1024;
|
|
65
|
+
const finalFileSize = Math.ceil(rawFinalSize / alignBlock) * alignBlock;
|
|
66
|
+
warning(`------------------------------------------------`);
|
|
67
|
+
warning(`SAFE SHRINK CALCULATION:`);
|
|
68
|
+
warning(` FS Payload: ${bytesToGB(fsSizeBytes)}`);
|
|
69
|
+
warning(` LUKS Header: ${bytesToGB(luksHeaderBytes)}`);
|
|
70
|
+
warning(` SafetyMargin: ${bytesToGB(safetyMargin)}`);
|
|
71
|
+
warning(` Alignment: 1 MB`);
|
|
72
|
+
warning(` FINAL SIZE: ${bytesToGB(finalFileSize)}`);
|
|
73
|
+
warning(`------------------------------------------------`);
|
|
74
|
+
// 6. Chiusura volume
|
|
75
|
+
warning(`Closing LUKS volume ${this.luksMappedName}...`);
|
|
76
|
+
await this.luksExecuteCommand('cryptsetup', ['close', this.luksMappedName]);
|
|
77
|
+
// 7. Truncate finale
|
|
78
|
+
warning(`Truncating ${this.luksFile} to release unused space...`);
|
|
79
|
+
await exec(`truncate -s ${finalFileSize} ${this.luksFile}`, this.echo);
|
|
80
|
+
}
|
|
81
|
+
function bytesToGB(bytes) {
|
|
82
|
+
if (bytes === 0)
|
|
83
|
+
return '0.00 GB';
|
|
84
|
+
const gigabytes = bytes / (1024 * 1024 * 1024);
|
|
85
|
+
return gigabytes.toFixed(2) + ' GB';
|
|
86
|
+
}
|
|
@@ -58,7 +58,7 @@ export async function produce(kernel = '', clone = false, homecrypt = false, ful
|
|
|
58
58
|
else if (this.fullcrypt) {
|
|
59
59
|
this.luksMappedName = 'root.img';
|
|
60
60
|
}
|
|
61
|
-
this.luksFile = `/tmp/${this.luksMappedName}`;
|
|
61
|
+
this.luksFile = `/var/tmp/${this.luksMappedName}`;
|
|
62
62
|
this.luksMappedName = this.luksMappedName;
|
|
63
63
|
this.luksMountpoint = `/tmp/mnt/${this.luksMappedName}`;
|
|
64
64
|
this.luksDevice = `/dev/mapper/${this.luksMappedName}`;
|
|
@@ -131,8 +131,6 @@ export async function produce(kernel = '', clone = false, homecrypt = false, ful
|
|
|
131
131
|
* homecrypt/fullcrypt/clone/standard
|
|
132
132
|
*/
|
|
133
133
|
if (this.homecrypt) {
|
|
134
|
-
this.settings.config.user_opt = 'live'; // patch for humans
|
|
135
|
-
this.settings.config.user_opt_passwd = 'evolution';
|
|
136
134
|
Utils.warning("eggs will SAVE users and users' data ENCRYPTED on the live (ISO)/live/home.img");
|
|
137
135
|
}
|
|
138
136
|
else if (this.fullcrypt) {
|
|
@@ -195,10 +193,10 @@ export async function produce(kernel = '', clone = false, homecrypt = false, ful
|
|
|
195
193
|
/**
|
|
196
194
|
* installer
|
|
197
195
|
*/
|
|
198
|
-
this.incubator = new Incubator(this.settings.remix, this.settings.distro, this.settings.config.user_opt, this.theme, this.clone || this.fullcrypt, verbose);
|
|
196
|
+
this.incubator = new Incubator(this.settings.remix, this.settings.distro, this.settings.config.user_opt, this.theme, this.clone || this.fullcrypt || this.homecrypt, verbose);
|
|
199
197
|
await this.incubator.config(release);
|
|
200
|
-
//
|
|
201
|
-
await Utils.sleep(5000);
|
|
198
|
+
// 5 secondi di sleep
|
|
199
|
+
await Utils.sleep(5000);
|
|
202
200
|
await this.bindLiveFs();
|
|
203
201
|
await this.bindVfs();
|
|
204
202
|
/**
|
|
@@ -228,36 +226,80 @@ export async function produce(kernel = '', clone = false, homecrypt = false, ful
|
|
|
228
226
|
await this.initrdDracut();
|
|
229
227
|
}
|
|
230
228
|
}
|
|
231
|
-
// We
|
|
229
|
+
// We don't need more
|
|
232
230
|
await this.ubindVfs();
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
231
|
+
// mistero della Fede
|
|
232
|
+
await this.editLiveFs();
|
|
233
|
+
/**
|
|
234
|
+
* Autologin solse per non clone
|
|
235
|
+
*/
|
|
236
|
+
if (!(this.clone || this.homecrypt || this.fullcrypt)) {
|
|
238
237
|
await this.usersRemove();
|
|
239
238
|
await this.userCreateLive();
|
|
240
239
|
if (Pacman.isInstalledGui()) {
|
|
240
|
+
// add GUI autologin
|
|
241
241
|
await this.createXdgAutostart(this.settings.config.theme, myAddons, myLinks, noicons);
|
|
242
|
-
/**
|
|
243
|
-
* GUI installed but NOT Desktop Manager: just create motd and issue
|
|
244
|
-
*/
|
|
245
|
-
// if (displaymanager().length > 0) {
|
|
246
|
-
// this.cliAutologin.addIssue(this.settings.distro.distroId, this.settings.distro.codenameId, this.settings.config.user_opt, this.settings.config.user_opt_passwd, this.settings.config.root_passwd, this.settings.work_dir.merged)
|
|
247
|
-
// this.cliAutologin.addMotd(this.settings.distro.distroId, this.settings.distro.codenameId, this.settings.config.user_opt, this.settings.config.user_opt_passwd, this.settings.config.root_passwd, this.settings.work_dir.merged)
|
|
248
|
-
// }
|
|
249
242
|
}
|
|
250
243
|
else {
|
|
244
|
+
// add cli-autologin
|
|
251
245
|
this.cliAutologin.add(this.settings.distro.distroId, this.settings.distro.codenameId, this.settings.config.user_opt, this.settings.config.user_opt_passwd, this.settings.config.root_passwd, this.settings.work_dir.merged);
|
|
252
246
|
}
|
|
253
247
|
}
|
|
254
|
-
|
|
248
|
+
/**
|
|
249
|
+
* configurazione homecrytp
|
|
250
|
+
*/
|
|
255
251
|
if (this.homecrypt) {
|
|
252
|
+
// Occorre forzare il login CLI
|
|
253
|
+
if (Utils.isSystemd()) {
|
|
254
|
+
const systemdDir = `${this.settings.work_dir.merged}/etc/systemd/system`;
|
|
255
|
+
// remove eventuali autologin
|
|
256
|
+
if (fs.existsSync(`${systemdDir}/getty@tty1.service.d`)) {
|
|
257
|
+
await exec(`rm -rf ${systemdDir}/getty@tty1.service.d/*`);
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
await exec(`mkdir -p ${systemdDir}/getty@tty1.service.d`);
|
|
261
|
+
}
|
|
262
|
+
let content = ``;
|
|
263
|
+
content += `[Service]\n`;
|
|
264
|
+
content += `ExecStart=\n`;
|
|
265
|
+
content += `ExecStart=-/sbin/agetty -o '-p -- \\u' --noclear %I \$TERM\n`;
|
|
266
|
+
fs.writeFileSync(`${systemdDir}/getty@tty1.service.d/no-autologin.conf`, content);
|
|
267
|
+
}
|
|
268
|
+
else if (Utils.isSysvinit()) {
|
|
269
|
+
const inittabPath = `${this.settings.work_dir.merged}/etc/inittab`;
|
|
270
|
+
if (fs.existsSync(inittabPath)) {
|
|
271
|
+
// Non funziona per Devuan
|
|
272
|
+
let content = fs.readFileSync(inittabPath, 'utf-8');
|
|
273
|
+
// La riga standard per Devuan/Debian SysVinit.
|
|
274
|
+
// 1: ID
|
|
275
|
+
// 2345: Runlevels in cui attivarlo (Nota che il tuo boot è in runlevel 2)
|
|
276
|
+
// respawn: Riavvia getty se muore
|
|
277
|
+
// /sbin/getty 38400 tty1: Il comando
|
|
278
|
+
const tty1Line = '1:2345:respawn:/sbin/agetty --noclear tty1 linux';
|
|
279
|
+
// Rimuoviamo vecchie definizioni di tty1 (anche se commentate o diverse)
|
|
280
|
+
// Cerca righe che iniziano con "1:"
|
|
281
|
+
const regex = /^1:.*$/gm;
|
|
282
|
+
if (regex.test(content)) {
|
|
283
|
+
// Sostituisce la riga esistente
|
|
284
|
+
content = content.replace(regex, tty1Line);
|
|
285
|
+
console.log('Fixed tty1 in inittab (replaced)');
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
// Se non c'è, la aggiunge in fondo (dopo i commenti iniziali)
|
|
289
|
+
content += `\n${tty1Line}\n`;
|
|
290
|
+
console.log('Fixed tty1 in inittab (appended)');
|
|
291
|
+
}
|
|
292
|
+
// FIX CRITICO PER --cryptedhome / LIVE:
|
|
293
|
+
// A volte le live commentano tutte le tty per usare i propri hook.
|
|
294
|
+
// Assicurati che non ci siano altre righe strane che confliggono.
|
|
295
|
+
fs.writeFileSync(inittabPath, content);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
256
298
|
/**
|
|
257
299
|
* homecrypt: installa il supporto
|
|
258
300
|
*/
|
|
259
301
|
const squashfsRoot = this.settings.work_dir.merged;
|
|
260
|
-
const homeImgPath = this.
|
|
302
|
+
const homeImgPath = this.distroLiveMediumPath + 'live/home.img';
|
|
261
303
|
this.installHomecryptSupport(squashfsRoot, homeImgPath);
|
|
262
304
|
}
|
|
263
305
|
mksquashfsCmd = await this.makeSquashfs(scriptOnly, includeRootHome);
|