penguins-eggs 25.11.29 → 25.12.7
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 +72 -110
- package/README.pdf +11041 -10623
- package/conf/derivatives.yaml +2 -1
- package/conf/exclude.list.d/var.list +11 -6
- package/dist/appimage/dependency-manager.js +1 -1
- package/dist/classes/cli-autologin.js +77 -52
- package/dist/classes/compressors.d.ts +7 -10
- package/dist/classes/compressors.js +44 -31
- package/dist/classes/distro.js +2 -2
- package/dist/classes/diversions.js +2 -3
- package/dist/classes/incubation/fisherman-helper/initcpio.d.ts +2 -5
- package/dist/classes/incubation/fisherman-helper/initcpio.js +7 -4
- package/dist/classes/incubation/fisherman-helper/settings.js +1 -1
- package/dist/classes/incubation/fisherman.js +1 -1
- package/dist/classes/incubation/incubator.js +1 -1
- package/dist/classes/ovary.d/create-xdg-autostart.js +1 -1
- package/dist/classes/ovary.d/edit-live-fs.d.ts +1 -12
- package/dist/classes/ovary.d/edit-live-fs.js +129 -135
- package/dist/classes/ovary.d/make-dot-disk.js +1 -1
- package/dist/classes/ovary.d/produce.js +1 -1
- package/dist/classes/ovary.d/user-create-live.d.ts +4 -10
- package/dist/classes/ovary.d/user-create-live.js +82 -84
- package/dist/classes/ovary.d/users-remove.d.ts +5 -6
- package/dist/classes/ovary.d/users-remove.js +61 -31
- package/dist/classes/ovary.d.ts +2 -2
- package/dist/classes/ovary.js +2 -2
- package/dist/classes/pacman.d/alpine.js +2 -2
- package/dist/classes/pacman.d/archlinux.js +2 -2
- package/dist/classes/pacman.d/debian.js +2 -3
- package/dist/classes/pacman.d/fedora.js +2 -3
- package/dist/classes/pacman.d/openmamba.js +2 -3
- package/dist/classes/pacman.d/opensuse.js +2 -3
- package/dist/classes/pacman.d.ts +0 -5
- package/dist/classes/pacman.js +3 -16
- package/dist/classes/pve-live.js +1 -1
- package/dist/classes/settings.js +1 -1
- package/dist/classes/sys-users.d.ts +76 -0
- package/dist/classes/sys-users.js +206 -0
- package/dist/classes/utils.d/kernel.js +3 -3
- package/dist/classes/utils.d.ts +15 -6
- package/dist/classes/utils.js +79 -46
- package/dist/classes/xdg.js +1 -1
- package/dist/classes/yolk.js +2 -4
- package/dist/commands/export/appimage.js +3 -3
- package/dist/commands/export/pkg.js +3 -3
- package/dist/commands/export/tarballs.js +3 -3
- package/dist/commands/krill.js +1 -1
- package/dist/commands/produce.js +9 -4
- package/dist/commands/setup/install.js +1 -1
- package/dist/commands/setup/purge.js +1 -1
- package/dist/commands/tools/yolk.js +1 -1
- package/dist/commands/update.js +1 -2
- package/dist/interfaces/i-exec.d.ts +1 -0
- package/dist/krill/classes/prepare.d/location.js +1 -1
- package/dist/krill/classes/prepare.d/partitions.js +1 -1
- package/dist/krill/classes/prepare.d/users.js +2 -2
- package/dist/krill/classes/prepare.js +5 -5
- package/dist/krill/classes/sequence.d/add_user.d.ts +3 -15
- package/dist/krill/classes/sequence.d/add_user.js +87 -57
- package/dist/krill/classes/sequence.d/change_password.d.ts +5 -7
- package/dist/krill/classes/sequence.d/change_password.js +25 -10
- package/dist/krill/classes/sequence.d/del_live_user.d.ts +5 -7
- package/dist/krill/classes/sequence.d/del_live_user.js +39 -25
- package/dist/krill/classes/sequence.d/fstab.js +1 -1
- package/dist/krill/classes/sequence.d/grubcfg.d.ts +3 -7
- package/dist/krill/classes/sequence.d/grubcfg.js +33 -13
- package/dist/krill/classes/sequence.d/mkfs.js +1 -2
- package/dist/krill/classes/sequence.d/unpackfs.d.ts +2 -4
- package/dist/krill/classes/sequence.d/unpackfs.js +8 -5
- package/dist/krill/classes/sequence.js +2 -3
- package/dist/krill/components/title.js +2 -2
- package/dist/krill/lib/select_installation_device.js +1 -1
- package/dist/krill/lib/select_replaced_partition.js +1 -1
- package/dist/lib/utils.d.ts +51 -19
- package/dist/lib/utils.js +225 -20
- package/manpages/doc/man/eggs.1.gz +0 -0
- package/manpages/doc/man/eggs.html +8 -8
- package/package.json +9 -9
- package/perrisbrewery/template/dependencies.yaml +1 -0
- package/scripts/boot-encrypted-root.sh +220 -0
- package/scripts/mount-encrypted-home.sh +324 -0
- package/dracut/create-symlink +0 -71
- package/dracut/dracut-log.txt +0 -3
- package/dracut/export +0 -4
- package/dracut/export-dracut-analysis +0 -51
- package/dracut/export-dracut-log +0 -2
- package/dracut/mkisofs +0 -10
- package/dracut/renew-initramfs +0 -17
- package/dracut/sbin2bin +0 -10
- package/dracut/update-dracut-conf-d +0 -2
- package/dracut/update-dracut-modules +0 -62
|
@@ -1,38 +1,68 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* src/classes/ovary.d/users-remove.ts
|
|
3
3
|
* penguins-eggs v.25.7.x / ecmascript 2020
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* license: MIT
|
|
4
|
+
* * REFACTORED: Uses "The SysUser Master" class.
|
|
5
|
+
* Cleans up host users from the ISO filesystem safely.
|
|
7
6
|
*/
|
|
8
|
-
|
|
9
|
-
import path from '
|
|
10
|
-
// backup
|
|
11
|
-
// interfaces
|
|
12
|
-
// libraries
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
13
9
|
import { exec } from '../../lib/utils.js';
|
|
14
|
-
import
|
|
15
|
-
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
10
|
+
import SysUsers from '../sys-users.js';
|
|
11
|
+
export default async function usersRemove() {
|
|
12
|
+
// Il target corretto in Ovary è la directory "merged" dell'overlayfs
|
|
13
|
+
let target = this.settings.work_dir.merged;
|
|
14
|
+
// Assicuriamoci che il target esista per sicurezza
|
|
15
|
+
if (!target || !fs.existsSync(target)) {
|
|
16
|
+
console.error(`SysUsers Error: Merged target directory not found at: ${target}`);
|
|
17
|
+
return;
|
|
22
18
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
19
|
+
// Nota: verifica se in Ovary hai 'this.familyId' diretto o 'this.distro.familyId'.
|
|
20
|
+
// Solitamente è this.distro.familyId, ma se hai un getter va bene così.
|
|
21
|
+
const familyId = this.distro?.familyId || this.familyId;
|
|
22
|
+
console.log(`Cleaning host users from ISO snapshot at ${target} (Family: ${familyId})...`);
|
|
23
|
+
// 2. CARICAMENTO CONFIGURAZIONE
|
|
24
|
+
const sysUsers = new SysUsers(target, familyId);
|
|
25
|
+
sysUsers.load();
|
|
26
|
+
// 3. IDENTIFICAZIONE UTENTI DA RIMUOVERE
|
|
27
|
+
// Dobbiamo leggere il file passwd raw per decidere chi rimuovere
|
|
28
|
+
// (rimuoviamo UID >= 1000 tranne 'nobody' e 'root')
|
|
29
|
+
const usersToDelete = [];
|
|
30
|
+
const passwdPath = path.join(target, 'etc/passwd');
|
|
31
|
+
if (fs.existsSync(passwdPath)) {
|
|
32
|
+
const lines = fs.readFileSync(passwdPath, 'utf8').split('\n');
|
|
33
|
+
for (const line of lines) {
|
|
34
|
+
const parts = line.split(':');
|
|
35
|
+
if (parts.length > 2) {
|
|
36
|
+
const uid = parseInt(parts[2]);
|
|
37
|
+
const username = parts[0];
|
|
38
|
+
// Logica di rimozione standard di eggs
|
|
39
|
+
if (uid >= 1000 && username !== 'nobody') {
|
|
40
|
+
usersToDelete.push(username);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// 4. ESECUZIONE RIMOZIONE (IN MEMORIA)
|
|
46
|
+
for (const username of usersToDelete) {
|
|
47
|
+
console.log(`- Removing user: ${username}`);
|
|
48
|
+
sysUsers.removeUser(username);
|
|
49
|
+
// Pulizia File Fisici (Home, Mail) - Operazioni FS dirette
|
|
50
|
+
const homeDir = path.join(target, 'home', username);
|
|
51
|
+
if (fs.existsSync(homeDir)) {
|
|
52
|
+
await exec(`rm -rf ${homeDir}`, this.echo);
|
|
53
|
+
}
|
|
54
|
+
const mailFile = path.join(target, 'var/mail', username);
|
|
55
|
+
if (fs.existsSync(mailFile)) {
|
|
56
|
+
fs.unlinkSync(mailFile);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// 5. SALVATAGGIO ATOMICO SU DISCO
|
|
60
|
+
if (usersToDelete.length > 0) {
|
|
61
|
+
// Scrive passwd, shadow, group, gshadow, subuid... e ripara SELinux
|
|
62
|
+
await sysUsers.save();
|
|
63
|
+
console.log("User cleanup completed via SysUsers Master.");
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
console.log("No users needed to be removed.");
|
|
37
67
|
}
|
|
38
68
|
}
|
package/dist/classes/ovary.d.ts
CHANGED
|
@@ -21,9 +21,9 @@ import { createXdgAutostart } from './ovary.d/create-xdg-autostart.js';
|
|
|
21
21
|
import { copied, merged, mergedAndOverlay } from './ovary.d/merged.js';
|
|
22
22
|
import { makeIso } from './ovary.d/make-iso.js';
|
|
23
23
|
import { initrdAlpine, initrdArch, initrdDebian, initrdDracut } from './ovary.d/initrd.js';
|
|
24
|
-
import
|
|
24
|
+
import userCreateLive from './ovary.d/user-create-live.js';
|
|
25
25
|
import { syslinux } from './ovary.d/syslinux.js';
|
|
26
|
-
import
|
|
26
|
+
import usersRemove from './ovary.d/users-remove.js';
|
|
27
27
|
import { makeDotDisk } from './ovary.d/make-dot-disk.js';
|
|
28
28
|
import { kernelCopy } from './ovary.d/kernel-copy.js';
|
|
29
29
|
import { liveCreateStructure } from './ovary.d/live-create-structure.js';
|
package/dist/classes/ovary.js
CHANGED
|
@@ -22,9 +22,9 @@ import { createXdgAutostart } from './ovary.d/create-xdg-autostart.js';
|
|
|
22
22
|
import { copied, merged, mergedAndOverlay } from './ovary.d/merged.js';
|
|
23
23
|
import { makeIso } from './ovary.d/make-iso.js';
|
|
24
24
|
import { initrdAlpine, initrdArch, initrdDebian, initrdDracut } from './ovary.d/initrd.js';
|
|
25
|
-
import
|
|
25
|
+
import userCreateLive from './ovary.d/user-create-live.js';
|
|
26
26
|
import { syslinux } from './ovary.d/syslinux.js';
|
|
27
|
-
import
|
|
27
|
+
import usersRemove from './ovary.d/users-remove.js';
|
|
28
28
|
import { makeDotDisk } from './ovary.d/make-dot-disk.js';
|
|
29
29
|
import { kernelCopy } from './ovary.d/kernel-copy.js';
|
|
30
30
|
import { liveCreateStructure } from './ovary.d/live-create-structure.js';
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* email: piero.proietti@gmail.com
|
|
6
6
|
* license: MIT
|
|
7
7
|
*/
|
|
8
|
-
import shx from '
|
|
8
|
+
import { shx } from '../../lib/utils.js';
|
|
9
9
|
import { exec } from '../../lib/utils.js';
|
|
10
10
|
import Utils from '../utils.js';
|
|
11
11
|
/**
|
|
@@ -131,7 +131,7 @@ export default class Alpine {
|
|
|
131
131
|
*/
|
|
132
132
|
static async packageInstall(packageName) {
|
|
133
133
|
let retVal = false;
|
|
134
|
-
if (shx.exec(
|
|
134
|
+
if (shx.exec('/sbin/apk add ' + packageName, { silent: true }).code === 0) {
|
|
135
135
|
retVal = true;
|
|
136
136
|
}
|
|
137
137
|
return retVal;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* license: MIT
|
|
7
7
|
*/
|
|
8
8
|
import fs from 'node:fs';
|
|
9
|
-
import shx from '
|
|
9
|
+
import { shx } from '../../lib/utils.js';
|
|
10
10
|
import { exec } from '../../lib/utils.js';
|
|
11
11
|
import Utils from '../utils.js';
|
|
12
12
|
/**
|
|
@@ -77,7 +77,7 @@ export default class Archlinux {
|
|
|
77
77
|
*/
|
|
78
78
|
static async packageInstall(packageName) {
|
|
79
79
|
let retVal = false;
|
|
80
|
-
if (shx.exec(
|
|
80
|
+
if (shx.exec('/usr/bin/pacman -Si ' + packageName, { silent: true }).code === 0) {
|
|
81
81
|
retVal = true;
|
|
82
82
|
}
|
|
83
83
|
return retVal;
|
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
* license: MIT
|
|
7
7
|
*/
|
|
8
8
|
import fs from 'node:fs';
|
|
9
|
-
import shx from '
|
|
10
|
-
import { exec } from '../../lib/utils.js';
|
|
9
|
+
import { exec, shx } from '../../lib/utils.js';
|
|
11
10
|
import Utils from '../utils.js';
|
|
12
11
|
/**
|
|
13
12
|
* Debian
|
|
@@ -116,7 +115,7 @@ export default class Debian {
|
|
|
116
115
|
*/
|
|
117
116
|
static async packageInstall(packageName) {
|
|
118
117
|
let retVal = false;
|
|
119
|
-
if (shx.exec(
|
|
118
|
+
if (shx.exec('/usr/bin/apt-get install -y ' + packageName, { silent: true }).code === 0) {
|
|
120
119
|
retVal = true;
|
|
121
120
|
}
|
|
122
121
|
return retVal;
|
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
* license: MIT
|
|
7
7
|
*/
|
|
8
8
|
import fs from 'node:fs';
|
|
9
|
-
import shx from '
|
|
10
|
-
import { exec } from '../../lib/utils.js';
|
|
9
|
+
import { exec, shx } from '../../lib/utils.js';
|
|
11
10
|
import Utils from '../utils.js';
|
|
12
11
|
/**
|
|
13
12
|
* Utils: general porpourse utils
|
|
@@ -86,7 +85,7 @@ export default class Fedora {
|
|
|
86
85
|
*/
|
|
87
86
|
static async packageInstall(packageName) {
|
|
88
87
|
let retVal = false;
|
|
89
|
-
if (shx.exec(
|
|
88
|
+
if (shx.exec('/usr/bin/dnf install ' + packageName, { silent: true }).code === 0) {
|
|
90
89
|
retVal = true;
|
|
91
90
|
}
|
|
92
91
|
return retVal;
|
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
* license: MIT
|
|
7
7
|
*/
|
|
8
8
|
import fs from 'node:fs';
|
|
9
|
-
import shx from '
|
|
10
|
-
import { exec } from '../../lib/utils.js';
|
|
9
|
+
import { exec, shx } from '../../lib/utils.js';
|
|
11
10
|
import Utils from '../utils.js';
|
|
12
11
|
/**
|
|
13
12
|
* Utils: general porpourse utils
|
|
@@ -85,7 +84,7 @@ export default class Openmamba {
|
|
|
85
84
|
*/
|
|
86
85
|
static async packageInstall(packageName) {
|
|
87
86
|
let retVal = false;
|
|
88
|
-
if (shx.exec(
|
|
87
|
+
if (shx.exec('/usr/bin/dnf install ' + packageName, { silent: false }).code === 0) {
|
|
89
88
|
retVal = true;
|
|
90
89
|
}
|
|
91
90
|
return retVal;
|
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
* license: MIT
|
|
7
7
|
*/
|
|
8
8
|
import fs from 'node:fs';
|
|
9
|
-
import shx from '
|
|
10
|
-
import { exec } from '../../lib/utils.js';
|
|
9
|
+
import { exec, shx } from '../../lib/utils.js';
|
|
11
10
|
import Utils from '../utils.js';
|
|
12
11
|
/**
|
|
13
12
|
* Utils: general porpourse utils
|
|
@@ -88,7 +87,7 @@ export default class Opensuse {
|
|
|
88
87
|
*/
|
|
89
88
|
static async packageInstall(packageName) {
|
|
90
89
|
let retVal = false;
|
|
91
|
-
if (shx.exec(
|
|
90
|
+
if (shx.exec('/usr/bin/zypper install ' + packageName, { silent: true }).code === 0) {
|
|
92
91
|
retVal = true;
|
|
93
92
|
}
|
|
94
93
|
return retVal;
|
package/dist/classes/pacman.d.ts
CHANGED
|
@@ -40,11 +40,6 @@ export default class Pacman {
|
|
|
40
40
|
*
|
|
41
41
|
*/
|
|
42
42
|
static calamaresRemove(verbose?: boolean): Promise<boolean>;
|
|
43
|
-
/**
|
|
44
|
-
*
|
|
45
|
-
* @param cmd
|
|
46
|
-
*/
|
|
47
|
-
static commandIsInstalled(cmd: string): boolean;
|
|
48
43
|
/**
|
|
49
44
|
* Restituisce VERO se i file di configurazione SONO presenti
|
|
50
45
|
*/
|
package/dist/classes/pacman.js
CHANGED
|
@@ -5,11 +5,10 @@
|
|
|
5
5
|
* email: piero.proietti@gmail.com
|
|
6
6
|
* license: MIT
|
|
7
7
|
*/
|
|
8
|
-
import { execSync } from '
|
|
8
|
+
import { shx, execSync } from '../lib/utils.js';
|
|
9
9
|
import fs from 'node:fs';
|
|
10
10
|
// _dirname
|
|
11
11
|
import path from 'node:path';
|
|
12
|
-
import shx from 'shelljs';
|
|
13
12
|
import { exec } from '../lib/utils.js';
|
|
14
13
|
import Distro from './distro.js';
|
|
15
14
|
import Diversions from './diversions.js';
|
|
@@ -61,7 +60,7 @@ export default class Pacman {
|
|
|
61
60
|
* return true if calamares is installed
|
|
62
61
|
*/
|
|
63
62
|
static calamaresExists() {
|
|
64
|
-
return
|
|
63
|
+
return Utils.commandExists('calamares');
|
|
65
64
|
}
|
|
66
65
|
/**
|
|
67
66
|
*
|
|
@@ -144,17 +143,6 @@ export default class Pacman {
|
|
|
144
143
|
}
|
|
145
144
|
return retVal;
|
|
146
145
|
}
|
|
147
|
-
/**
|
|
148
|
-
*
|
|
149
|
-
* @param cmd
|
|
150
|
-
*/
|
|
151
|
-
static commandIsInstalled(cmd) {
|
|
152
|
-
let installed = false;
|
|
153
|
-
if (shx.exec(`command -V ${cmd} >/dev/null 2>&1`).code == 0) {
|
|
154
|
-
installed = true;
|
|
155
|
-
}
|
|
156
|
-
return installed;
|
|
157
|
-
}
|
|
158
146
|
/**
|
|
159
147
|
* Restituisce VERO se i file di configurazione SONO presenti
|
|
160
148
|
*/
|
|
@@ -195,7 +183,7 @@ export default class Pacman {
|
|
|
195
183
|
/**
|
|
196
184
|
* Salvo la configurazione di eggs.yaml
|
|
197
185
|
*/
|
|
198
|
-
config.machine_id = Utils.machineId()
|
|
186
|
+
config.machine_id = ''; //Utils.machineId()
|
|
199
187
|
config.vmlinuz = Utils.vmlinuz();
|
|
200
188
|
config.initrd_img = Utils.initrdImg();
|
|
201
189
|
const settings = new Settings();
|
|
@@ -231,7 +219,6 @@ export default class Pacman {
|
|
|
231
219
|
execSync(`rm -rf ${init}`);
|
|
232
220
|
}
|
|
233
221
|
execSync(`mkdir -p ${init}`);
|
|
234
|
-
// shx.ln('-s', path.resolve(__dirname, '../../addons'), addons)
|
|
235
222
|
shx.cp(path.resolve(__dirname, '../../conf/README.md'), confRoot);
|
|
236
223
|
shx.cp(path.resolve(__dirname, '../../conf/derivatives.yaml'), confRoot);
|
|
237
224
|
shx.cp(path.resolve(__dirname, '../../conf/derivatives_fedora.yaml'), confRoot);
|
package/dist/classes/pve-live.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* This will remove the symbolic link that indicated that the service should be started automatically.
|
|
15
15
|
*/
|
|
16
16
|
import path from 'node:path';
|
|
17
|
-
import shx from '
|
|
17
|
+
import { shx } from '../lib/utils.js';
|
|
18
18
|
import Systemctl from './systemctl.js';
|
|
19
19
|
// _dirname
|
|
20
20
|
const __dirname = path.dirname(new URL(import.meta.url).pathname);
|
package/dist/classes/settings.js
CHANGED
|
@@ -10,7 +10,7 @@ import yaml from 'js-yaml';
|
|
|
10
10
|
// packages
|
|
11
11
|
import fs from 'node:fs';
|
|
12
12
|
import os from 'node:os';
|
|
13
|
-
import shx from '
|
|
13
|
+
import { shx } from '../lib/utils.js';
|
|
14
14
|
// pjson
|
|
15
15
|
import { createRequire } from 'node:module';
|
|
16
16
|
const require = createRequire(import.meta.url);
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/classes/sys-users.ts
|
|
3
|
+
* penguins-eggs v.25.7.x / ecmascript 2020
|
|
4
|
+
* * "THE SYSUSER MASTER"
|
|
5
|
+
* Gestione pura Node.js per utenti e gruppi di sistema.
|
|
6
|
+
* Sostituisce i binari (useradd/usermod/deluser) per garantire operazioni atomiche
|
|
7
|
+
* e compatibilità SELinux (Fedora/RHEL) scrivendo file puliti.
|
|
8
|
+
*/
|
|
9
|
+
export interface IPasswdEntry {
|
|
10
|
+
username: string;
|
|
11
|
+
password: string;
|
|
12
|
+
uid: string;
|
|
13
|
+
gid: string;
|
|
14
|
+
gecos: string;
|
|
15
|
+
home: string;
|
|
16
|
+
shell: string;
|
|
17
|
+
}
|
|
18
|
+
export interface IShadowEntry {
|
|
19
|
+
username: string;
|
|
20
|
+
hash: string;
|
|
21
|
+
lastChange: string;
|
|
22
|
+
min: string;
|
|
23
|
+
max: string;
|
|
24
|
+
warn: string;
|
|
25
|
+
inactive: string;
|
|
26
|
+
expire: string;
|
|
27
|
+
}
|
|
28
|
+
export interface IGroupEntry {
|
|
29
|
+
groupName: string;
|
|
30
|
+
password: string;
|
|
31
|
+
gid: string;
|
|
32
|
+
members: string[];
|
|
33
|
+
}
|
|
34
|
+
export default class SysUsers {
|
|
35
|
+
private targetRoot;
|
|
36
|
+
private distroFamily;
|
|
37
|
+
private passwd;
|
|
38
|
+
private shadow;
|
|
39
|
+
private group;
|
|
40
|
+
private gshadowLines;
|
|
41
|
+
private subuidLines;
|
|
42
|
+
private subgidLines;
|
|
43
|
+
constructor(targetRoot: string, distroFamily: string);
|
|
44
|
+
/**
|
|
45
|
+
* Carica tutti i file di configurazione in memoria
|
|
46
|
+
*/
|
|
47
|
+
load(): void;
|
|
48
|
+
/**
|
|
49
|
+
* Salva lo stato della memoria su disco e applica SELinux fix
|
|
50
|
+
*/
|
|
51
|
+
save(): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Crea un nuovo utente completo
|
|
54
|
+
*/
|
|
55
|
+
addUser(user: IPasswdEntry, cleanPassword: string): void;
|
|
56
|
+
/**
|
|
57
|
+
* Rimuove completamente un utente
|
|
58
|
+
*/
|
|
59
|
+
removeUser(username: string): void;
|
|
60
|
+
/**
|
|
61
|
+
* Aggiunge utente a un gruppo supplementare
|
|
62
|
+
*/
|
|
63
|
+
addUserToGroup(username: string, groupName: string): void;
|
|
64
|
+
/**
|
|
65
|
+
* Cambia password utente
|
|
66
|
+
*/
|
|
67
|
+
setPassword(username: string, password: string): void;
|
|
68
|
+
private readFile;
|
|
69
|
+
private writeFile;
|
|
70
|
+
private parsePasswd;
|
|
71
|
+
private serializePasswd;
|
|
72
|
+
private parseShadow;
|
|
73
|
+
private serializeShadow;
|
|
74
|
+
private parseGroup;
|
|
75
|
+
private serializeGroup;
|
|
76
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/classes/sys-users.ts
|
|
3
|
+
* penguins-eggs v.25.7.x / ecmascript 2020
|
|
4
|
+
* * "THE SYSUSER MASTER"
|
|
5
|
+
* Gestione pura Node.js per utenti e gruppi di sistema.
|
|
6
|
+
* Sostituisce i binari (useradd/usermod/deluser) per garantire operazioni atomiche
|
|
7
|
+
* e compatibilità SELinux (Fedora/RHEL) scrivendo file puliti.
|
|
8
|
+
*/
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import * as bcrypt from 'bcryptjs';
|
|
12
|
+
import { exec } from '../lib/utils.js';
|
|
13
|
+
export default class SysUsers {
|
|
14
|
+
targetRoot;
|
|
15
|
+
distroFamily;
|
|
16
|
+
// Cache in memoria
|
|
17
|
+
passwd = [];
|
|
18
|
+
shadow = [];
|
|
19
|
+
group = [];
|
|
20
|
+
// File "minori" gestiti a righe raw per semplicità
|
|
21
|
+
gshadowLines = [];
|
|
22
|
+
subuidLines = [];
|
|
23
|
+
subgidLines = [];
|
|
24
|
+
constructor(targetRoot, distroFamily) {
|
|
25
|
+
this.targetRoot = targetRoot;
|
|
26
|
+
this.distroFamily = distroFamily;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Carica tutti i file di configurazione in memoria
|
|
30
|
+
*/
|
|
31
|
+
load() {
|
|
32
|
+
this.passwd = this.parsePasswd(this.readFile('etc/passwd'));
|
|
33
|
+
this.shadow = this.parseShadow(this.readFile('etc/shadow'));
|
|
34
|
+
this.group = this.parseGroup(this.readFile('etc/group'));
|
|
35
|
+
this.gshadowLines = this.readFile('etc/gshadow');
|
|
36
|
+
this.subuidLines = this.readFile('etc/subuid');
|
|
37
|
+
this.subgidLines = this.readFile('etc/subgid');
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Salva lo stato della memoria su disco e applica SELinux fix
|
|
41
|
+
*/
|
|
42
|
+
async save() {
|
|
43
|
+
// Serializzazione
|
|
44
|
+
const passwdContent = this.serializePasswd(this.passwd);
|
|
45
|
+
const shadowContent = this.serializeShadow(this.shadow);
|
|
46
|
+
const groupContent = this.serializeGroup(this.group);
|
|
47
|
+
// Scrittura Atomica + Fix SELinux
|
|
48
|
+
await this.writeFile('etc/passwd', passwdContent, 'passwd_file_t');
|
|
49
|
+
await this.writeFile('etc/shadow', shadowContent, 'shadow_t');
|
|
50
|
+
await this.writeFile('etc/group', groupContent, 'passwd_file_t');
|
|
51
|
+
// File raw
|
|
52
|
+
if (this.gshadowLines.length > 0)
|
|
53
|
+
await this.writeFile('etc/gshadow', this.gshadowLines.join('\n'), 'shadow_t');
|
|
54
|
+
if (this.subuidLines.length > 0)
|
|
55
|
+
await this.writeFile('etc/subuid', this.subuidLines.join('\n'), 'passwd_file_t');
|
|
56
|
+
if (this.subgidLines.length > 0)
|
|
57
|
+
await this.writeFile('etc/subgid', this.subgidLines.join('\n'), 'passwd_file_t');
|
|
58
|
+
}
|
|
59
|
+
// =========================================================================
|
|
60
|
+
// API PUBBLICA
|
|
61
|
+
// =========================================================================
|
|
62
|
+
/**
|
|
63
|
+
* Crea un nuovo utente completo
|
|
64
|
+
*/
|
|
65
|
+
addUser(user, cleanPassword) {
|
|
66
|
+
// Rimuovi se esiste (idempotenza)
|
|
67
|
+
this.removeUser(user.username);
|
|
68
|
+
// 1. Passwd
|
|
69
|
+
this.passwd.push(user);
|
|
70
|
+
// 2. Shadow (Hash Password)
|
|
71
|
+
const salt = bcrypt.genSaltSync(10);
|
|
72
|
+
const hash = bcrypt.hashSync(cleanPassword, salt);
|
|
73
|
+
this.shadow.push({
|
|
74
|
+
username: user.username,
|
|
75
|
+
hash: hash,
|
|
76
|
+
lastChange: '19700', // Data approssimativa
|
|
77
|
+
min: '0',
|
|
78
|
+
max: '99999',
|
|
79
|
+
warn: '7',
|
|
80
|
+
inactive: '',
|
|
81
|
+
expire: ''
|
|
82
|
+
});
|
|
83
|
+
// 3. Gruppo Primario
|
|
84
|
+
// Solo se non esiste già un gruppo con quel nome
|
|
85
|
+
if (!this.group.find(g => g.groupName === user.username)) {
|
|
86
|
+
this.group.push({
|
|
87
|
+
groupName: user.username,
|
|
88
|
+
password: 'x',
|
|
89
|
+
gid: user.gid,
|
|
90
|
+
members: []
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
// 4. GShadow (placeholder)
|
|
94
|
+
this.gshadowLines.push(`${user.username}:!::`);
|
|
95
|
+
// 5. SubUID/SubGID (Podman rootless)
|
|
96
|
+
// Calcolo offset standard: 100000 + (UID-1000)*65536
|
|
97
|
+
const uidNum = parseInt(user.uid);
|
|
98
|
+
if (!isNaN(uidNum) && uidNum >= 1000) {
|
|
99
|
+
const startUid = 100000 + (uidNum - 1000) * 65536;
|
|
100
|
+
const subEntry = `${user.username}:${startUid}:65536`;
|
|
101
|
+
this.subuidLines.push(subEntry);
|
|
102
|
+
this.subgidLines.push(subEntry);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Rimuove completamente un utente
|
|
107
|
+
*/
|
|
108
|
+
removeUser(username) {
|
|
109
|
+
this.passwd = this.passwd.filter(u => u.username !== username);
|
|
110
|
+
this.shadow = this.shadow.filter(s => s.username !== username);
|
|
111
|
+
this.group = this.group.filter(g => g.groupName !== username);
|
|
112
|
+
// Rimuovi dai membri di altri gruppi
|
|
113
|
+
this.group.forEach(g => {
|
|
114
|
+
g.members = g.members.filter(m => m !== username);
|
|
115
|
+
});
|
|
116
|
+
this.gshadowLines = this.gshadowLines.filter(l => !l.startsWith(`${username}:`));
|
|
117
|
+
this.subuidLines = this.subuidLines.filter(l => !l.startsWith(`${username}:`));
|
|
118
|
+
this.subgidLines = this.subgidLines.filter(l => !l.startsWith(`${username}:`));
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Aggiunge utente a un gruppo supplementare
|
|
122
|
+
*/
|
|
123
|
+
addUserToGroup(username, groupName) {
|
|
124
|
+
const grp = this.group.find(g => g.groupName === groupName);
|
|
125
|
+
if (grp) {
|
|
126
|
+
if (!grp.members.includes(username)) {
|
|
127
|
+
grp.members.push(username);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Se il gruppo non esiste, lo ignoriamo silenziosamente o potremmo crearlo
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Cambia password utente
|
|
134
|
+
*/
|
|
135
|
+
setPassword(username, password) {
|
|
136
|
+
const entry = this.shadow.find(s => s.username === username);
|
|
137
|
+
if (entry) {
|
|
138
|
+
const salt = bcrypt.genSaltSync(10);
|
|
139
|
+
entry.hash = bcrypt.hashSync(password, salt);
|
|
140
|
+
entry.lastChange = '19700';
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// =========================================================================
|
|
144
|
+
// IMPLEMENTAZIONE FILE (Privata)
|
|
145
|
+
// =========================================================================
|
|
146
|
+
readFile(relativePath) {
|
|
147
|
+
const fullPath = path.join(this.targetRoot, relativePath);
|
|
148
|
+
if (fs.existsSync(fullPath)) {
|
|
149
|
+
return fs.readFileSync(fullPath, 'utf8').split('\n').filter(l => l.trim().length > 0);
|
|
150
|
+
}
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
async writeFile(relativePath, content, contextType) {
|
|
154
|
+
const fullPath = path.join(this.targetRoot, relativePath);
|
|
155
|
+
// Crea dir se manca (es. /etc/sudoers.d/ o simili)
|
|
156
|
+
const dir = path.dirname(fullPath);
|
|
157
|
+
if (!fs.existsSync(dir))
|
|
158
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
159
|
+
try {
|
|
160
|
+
// 1. Scrittura
|
|
161
|
+
fs.writeFileSync(fullPath, content + '\n');
|
|
162
|
+
// 2. Fix SELinux (Solo RHEL Family)
|
|
163
|
+
if (['fedora', 'rhel', 'centos', 'almalinux', 'rocky'].includes(this.distroFamily)) {
|
|
164
|
+
// await exec, echo false per non sporcare i log
|
|
165
|
+
await exec(`chcon -t ${contextType} ${fullPath}`, { echo: false }).catch(() => { });
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch (e) {
|
|
169
|
+
console.error(`SysUsers Error writing ${relativePath}:`, e);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// --- PARSERS & SERIALIZERS ---
|
|
173
|
+
parsePasswd(lines) {
|
|
174
|
+
return lines.map(line => {
|
|
175
|
+
const p = line.split(':');
|
|
176
|
+
if (p.length < 7)
|
|
177
|
+
return null;
|
|
178
|
+
return { username: p[0], password: p[1], uid: p[2], gid: p[3], gecos: p[4], home: p[5], shell: p[6] };
|
|
179
|
+
}).filter((u) => u !== null);
|
|
180
|
+
}
|
|
181
|
+
serializePasswd(entries) {
|
|
182
|
+
return entries.map(u => `${u.username}:${u.password}:${u.uid}:${u.gid}:${u.gecos}:${u.home}:${u.shell}`).join('\n');
|
|
183
|
+
}
|
|
184
|
+
parseShadow(lines) {
|
|
185
|
+
return lines.map(line => {
|
|
186
|
+
const p = line.split(':');
|
|
187
|
+
if (p.length < 2)
|
|
188
|
+
return null;
|
|
189
|
+
return { username: p[0], hash: p[1], lastChange: p[2] || '', min: p[3] || '', max: p[4] || '', warn: p[5] || '', inactive: p[6] || '', expire: p[7] || '' };
|
|
190
|
+
}).filter((u) => u !== null);
|
|
191
|
+
}
|
|
192
|
+
serializeShadow(entries) {
|
|
193
|
+
return entries.map(s => `${s.username}:${s.hash}:${s.lastChange}:${s.min}:${s.max}:${s.warn}:${s.inactive}:${s.expire}:`).join('\n');
|
|
194
|
+
}
|
|
195
|
+
parseGroup(lines) {
|
|
196
|
+
return lines.map(line => {
|
|
197
|
+
const p = line.split(':');
|
|
198
|
+
if (p.length < 3)
|
|
199
|
+
return null;
|
|
200
|
+
return { groupName: p[0], password: p[1], gid: p[2], members: p[3] && p[3].trim() ? p[3].split(',') : [] };
|
|
201
|
+
}).filter((g) => g !== null);
|
|
202
|
+
}
|
|
203
|
+
serializeGroup(entries) {
|
|
204
|
+
return entries.map(g => `${g.groupName}:${g.password}:${g.gid}:${g.members.join(',')}`).join('\n');
|
|
205
|
+
}
|
|
206
|
+
}
|
|
@@ -8,7 +8,7 @@ import fs from 'node:fs';
|
|
|
8
8
|
import path from 'path';
|
|
9
9
|
import Distro from '../distro.js';
|
|
10
10
|
import Utils from '../utils.js';
|
|
11
|
-
import { execSync } from '
|
|
11
|
+
import { execSync } from '../../lib/utils.js';
|
|
12
12
|
/**
|
|
13
13
|
* Kernel utilities for managing vmlinuz and initramfs paths
|
|
14
14
|
*/
|
|
@@ -55,7 +55,7 @@ export default class Kernel {
|
|
|
55
55
|
Utils.warning("Non è possibile determinare il kernel in un container.");
|
|
56
56
|
process.exit(1);
|
|
57
57
|
}
|
|
58
|
-
targetKernel = execSync('uname -r')
|
|
58
|
+
targetKernel = (execSync('uname -r', { stdio: 'ignore' }) || '').trim();
|
|
59
59
|
}
|
|
60
60
|
const kernelVersionShort = targetKernel.split('.').slice(0, 2).join('.');
|
|
61
61
|
const bootDir = '/boot';
|
|
@@ -138,7 +138,7 @@ export default class Kernel {
|
|
|
138
138
|
* debian, fedora, opensuse, rasberry
|
|
139
139
|
*/
|
|
140
140
|
static vmlinuzFromUname() {
|
|
141
|
-
let kernelVersion = execSync('uname -r')
|
|
141
|
+
let kernelVersion = (execSync('uname -r', { stdio: 'ignore' }) || '').trim();
|
|
142
142
|
// Try 1: path standard (es. Debian, Ubuntu, Fedora)
|
|
143
143
|
let standardPath = `/boot/vmlinuz-${kernelVersion}`;
|
|
144
144
|
if (fs.existsSync(standardPath)) {
|