Add hardering
This commit is contained in:
75
CLAUDE.md
Normal file
75
CLAUDE.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Commandes de développement
|
||||
|
||||
```bash
|
||||
# Lancer l'application (mode interactif, demande sudo automatiquement)
|
||||
python3 -m securecheck
|
||||
|
||||
# Lancer sans sudo (pour tester sans élévation)
|
||||
SECURECHECK_SKIP_SUDO=1 python3 -m securecheck
|
||||
|
||||
# Mode non interactif
|
||||
python3 -m securecheck --dry-run --tasks system_update,lynis_audit
|
||||
python3 -m securecheck --scenario baseline_workstation --run
|
||||
python3 -m securecheck --list-scenarios
|
||||
|
||||
# Avec sudo (recommandé pour les tâches système réelles)
|
||||
sudo -E python3 -m securecheck
|
||||
|
||||
# Installation locale
|
||||
pip install .
|
||||
|
||||
# Build du binaire autonome (nécessite .venv avec pyinstaller)
|
||||
python3 -m venv .venv
|
||||
.venv/bin/pip install pyinstaller
|
||||
./build_executable.sh
|
||||
# Résultat dans dist/securecheck
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
### Flux d'exécution principal
|
||||
|
||||
`__main__.py:main()` orchestre tout :
|
||||
1. `ensure_root()` — ré-exécute automatiquement avec `sudo -E` si non root (sauf `SECURECHECK_SKIP_SUDO=1`)
|
||||
2. `detect_system()` — détecte distro, gestionnaire de paquets, user invocant
|
||||
3. `task_catalog()` + `builtin_scenarios()` — charge tâches et scénarios
|
||||
4. En mode interactif : `SecureCheckTUI` → sélection → `execute_tasks()` → `RunSummaryTUI`
|
||||
5. En mode non interactif : résolution des tâches via `--tasks`/`--scenario` → `execute_tasks()` → affichage terminal
|
||||
|
||||
### Modules clés
|
||||
|
||||
- **`models.py`** — dataclasses centrales : `TaskDefinition`, `TaskResult`, `Scenario`
|
||||
- **`catalog.py`** — registre de toutes les tâches (`task_catalog()`) et scénarios builtin (`builtin_scenarios()`). Utilise `bind()` pour attacher les handlers.
|
||||
- **`tasks.py`** — implémentation de chaque tâche (une fonction par tâche). Chaque fonction reçoit `(context: ExecutionContext, task: TaskDefinition)` et retourne `TaskResult`.
|
||||
- **`executor.py`** — `ExecutionContext` (contexte partagé par toutes les tâches) et `CommandRunner` (abstraction pour toutes les opérations système : paquets, fichiers, services, shell). `execute_tasks()` itère sur les tâches et capture les exceptions.
|
||||
- **`app.py`** — TUI curses : `SecureCheckTUI` (menu principal) et `RunSummaryTUI` (résumé post-exécution)
|
||||
- **`config.py`** — `AppPaths` et `build_paths()` : résolution XDG des chemins (config, state, logs). Logs dans `/var/log/securecheck` si root, sinon `~/.local/state/securecheck/logs`.
|
||||
- **`storage.py`** — `ScenarioStore` : lecture/écriture des scénarios utilisateur dans `~/.config/securecheck/scenarios.json`
|
||||
- **`system_info.py`** — `SystemInfo` + `detect_system()` : détecte OS, package manager, user réel (via `SUDO_USER`), home, uid/gid
|
||||
- **`status.py`** — `collect_status()` : sonde les services/composants pour le tableau d'état LED
|
||||
- **`assets.py`** — accès aux fichiers embarqués dans `securecheck/assets/` (banner.txt, p10k.zsh, icônes)
|
||||
|
||||
### Ajouter une nouvelle tâche
|
||||
|
||||
1. Écrire la fonction dans `tasks.py` avec la signature `(context: ExecutionContext, task: TaskDefinition) -> TaskResult`
|
||||
2. Enregistrer dans `catalog.py` : ajouter un `TaskDefinition` dans `task_catalog()` et mapper son handler dans `handlers`
|
||||
3. L'ajouter optionnellement à un `builtin_scenarios()` existant
|
||||
|
||||
### Conventions importantes
|
||||
|
||||
- `CommandRunner` est le seul point d'entrée pour les opérations système (jamais `subprocess` directement dans `tasks.py`)
|
||||
- `context.runner.write_text_file()` est idempotent : ne réécrit pas si le contenu est identique
|
||||
- `context.runner.update_package_index()` est appelé une seule fois par exécution (flag `_package_index_updated`)
|
||||
- Le `target_user` dans `SystemInfo` est l'utilisateur réel ayant lancé `sudo`, pas root
|
||||
- `--dry-run` : toutes les opérations système sont simulées, les commandes sont loggées sans être exécutées
|
||||
|
||||
### Emplacements runtime
|
||||
|
||||
- Scénarios : `~/.config/securecheck/scenarios.json`
|
||||
- Logs : `/var/log/securecheck/` (root) ou `~/.local/state/securecheck/logs/`
|
||||
- Rapports par exécution : `<log_dir>/run-YYYYMMDD-HHMMSS.log`
|
||||
- Rapports tâches (lynis, rootkits) : `<log_dir>/reports/`
|
||||
BIN
__pycache__/run.cpython-313.pyc
Normal file
BIN
__pycache__/run.cpython-313.pyc
Normal file
Binary file not shown.
@@ -31,6 +31,9 @@
|
||||
'DATA'),
|
||||
('securecheck/assets/securecheck-icon.svg',
|
||||
'/home/tuxgyver/scripts/securecheck/securecheck/assets/securecheck-icon.svg',
|
||||
'DATA'),
|
||||
('securecheck/assets/system_hardening.sh',
|
||||
'/home/tuxgyver/scripts/securecheck/securecheck/assets/system_hardening.sh',
|
||||
'DATA')],
|
||||
'3.13.7 (main, Mar 3 2026, 12:19:54) [GCC 15.2.0]',
|
||||
[('pyi_rth_inspect',
|
||||
@@ -189,12 +192,12 @@
|
||||
('opcode', '/usr/lib/python3.13/opcode.py', 'PYMODULE'),
|
||||
('_opcode_metadata', '/usr/lib/python3.13/_opcode_metadata.py', 'PYMODULE'),
|
||||
('ast', '/usr/lib/python3.13/ast.py', 'PYMODULE'),
|
||||
('_py_abc', '/usr/lib/python3.13/_py_abc.py', 'PYMODULE'),
|
||||
('stringprep', '/usr/lib/python3.13/stringprep.py', 'PYMODULE'),
|
||||
('_colorize', '/usr/lib/python3.13/_colorize.py', 'PYMODULE'),
|
||||
('tracemalloc', '/usr/lib/python3.13/tracemalloc.py', 'PYMODULE'),
|
||||
('subprocess', '/usr/lib/python3.13/subprocess.py', 'PYMODULE'),
|
||||
('signal', '/usr/lib/python3.13/signal.py', 'PYMODULE'),
|
||||
('stringprep', '/usr/lib/python3.13/stringprep.py', 'PYMODULE'),
|
||||
('tracemalloc', '/usr/lib/python3.13/tracemalloc.py', 'PYMODULE'),
|
||||
('_colorize', '/usr/lib/python3.13/_colorize.py', 'PYMODULE'),
|
||||
('_py_abc', '/usr/lib/python3.13/_py_abc.py', 'PYMODULE'),
|
||||
('securecheck.__main__',
|
||||
'/home/tuxgyver/scripts/securecheck/securecheck/__main__.py',
|
||||
'PYMODULE'),
|
||||
@@ -250,6 +253,9 @@
|
||||
('securecheck.app',
|
||||
'/home/tuxgyver/scripts/securecheck/securecheck/app.py',
|
||||
'PYMODULE'),
|
||||
('securecheck.summary_utils',
|
||||
'/home/tuxgyver/scripts/securecheck/securecheck/summary_utils.py',
|
||||
'PYMODULE'),
|
||||
('curses', '/usr/lib/python3.13/curses/__init__.py', 'PYMODULE'),
|
||||
('curses.has_key', '/usr/lib/python3.13/curses/has_key.py', 'PYMODULE'),
|
||||
('__future__', '/usr/lib/python3.13/__future__.py', 'PYMODULE')],
|
||||
@@ -307,8 +313,8 @@
|
||||
('python3.13/lib-dynload/_curses.cpython-313-x86_64-linux-gnu.so',
|
||||
'/usr/lib/python3.13/lib-dynload/_curses.cpython-313-x86_64-linux-gnu.so',
|
||||
'EXTENSION'),
|
||||
('libz.so.1', '/lib/x86_64-linux-gnu/libz.so.1', 'BINARY'),
|
||||
('libexpat.so.1', '/lib/x86_64-linux-gnu/libexpat.so.1', 'BINARY'),
|
||||
('libz.so.1', '/lib/x86_64-linux-gnu/libz.so.1', 'BINARY'),
|
||||
('libcrypto.so.3', '/lib/x86_64-linux-gnu/libcrypto.so.3', 'BINARY'),
|
||||
('libzstd.so.1', '/lib/x86_64-linux-gnu/libzstd.so.1', 'BINARY'),
|
||||
('liblzma.so.5', '/lib/x86_64-linux-gnu/liblzma.so.5', 'BINARY'),
|
||||
@@ -333,33 +339,49 @@
|
||||
('securecheck/assets/securecheck-icon.svg',
|
||||
'/home/tuxgyver/scripts/securecheck/securecheck/assets/securecheck-icon.svg',
|
||||
'DATA'),
|
||||
('wheel-0.46.1.dist-info/METADATA',
|
||||
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/METADATA',
|
||||
'DATA'),
|
||||
('wheel-0.46.1.dist-info/WHEEL',
|
||||
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/WHEEL',
|
||||
'DATA'),
|
||||
('wheel-0.46.1.dist-info/INSTALLER',
|
||||
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/INSTALLER',
|
||||
('securecheck/assets/system_hardening.sh',
|
||||
'/home/tuxgyver/scripts/securecheck/securecheck/assets/system_hardening.sh',
|
||||
'DATA'),
|
||||
('wheel-0.46.1.dist-info/entry_points.txt',
|
||||
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/entry_points.txt',
|
||||
'DATA'),
|
||||
('wheel-0.46.1.dist-info/INSTALLER',
|
||||
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/INSTALLER',
|
||||
'DATA'),
|
||||
('wheel-0.46.1.dist-info/WHEEL',
|
||||
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/WHEEL',
|
||||
'DATA'),
|
||||
('wheel-0.46.1.dist-info/METADATA',
|
||||
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/METADATA',
|
||||
'DATA'),
|
||||
('base_library.zip',
|
||||
'/home/tuxgyver/scripts/securecheck/build/securecheck/base_library.zip',
|
||||
'DATA')],
|
||||
[('functools', '/usr/lib/python3.13/functools.py', 'PYMODULE'),
|
||||
('sre_compile', '/usr/lib/python3.13/sre_compile.py', 'PYMODULE'),
|
||||
('copyreg', '/usr/lib/python3.13/copyreg.py', 'PYMODULE'),
|
||||
('operator', '/usr/lib/python3.13/operator.py', 'PYMODULE'),
|
||||
('genericpath', '/usr/lib/python3.13/genericpath.py', 'PYMODULE'),
|
||||
('keyword', '/usr/lib/python3.13/keyword.py', 'PYMODULE'),
|
||||
[('locale', '/usr/lib/python3.13/locale.py', 'PYMODULE'),
|
||||
('abc', '/usr/lib/python3.13/abc.py', 'PYMODULE'),
|
||||
('ntpath', '/usr/lib/python3.13/ntpath.py', 'PYMODULE'),
|
||||
('types', '/usr/lib/python3.13/types.py', 'PYMODULE'),
|
||||
('sre_parse', '/usr/lib/python3.13/sre_parse.py', 'PYMODULE'),
|
||||
('os', '/usr/lib/python3.13/os.py', 'PYMODULE'),
|
||||
('_collections_abc', '/usr/lib/python3.13/_collections_abc.py', 'PYMODULE'),
|
||||
('linecache', '/usr/lib/python3.13/linecache.py', 'PYMODULE'),
|
||||
('posixpath', '/usr/lib/python3.13/posixpath.py', 'PYMODULE'),
|
||||
('collections', '/usr/lib/python3.13/collections/__init__.py', 'PYMODULE'),
|
||||
('io', '/usr/lib/python3.13/io.py', 'PYMODULE'),
|
||||
('locale', '/usr/lib/python3.13/locale.py', 'PYMODULE'),
|
||||
('re._parser', '/usr/lib/python3.13/re/_parser.py', 'PYMODULE'),
|
||||
('re._constants', '/usr/lib/python3.13/re/_constants.py', 'PYMODULE'),
|
||||
('re._compiler', '/usr/lib/python3.13/re/_compiler.py', 'PYMODULE'),
|
||||
('re._casefix', '/usr/lib/python3.13/re/_casefix.py', 'PYMODULE'),
|
||||
('re', '/usr/lib/python3.13/re/__init__.py', 'PYMODULE'),
|
||||
('operator', '/usr/lib/python3.13/operator.py', 'PYMODULE'),
|
||||
('_collections_abc', '/usr/lib/python3.13/_collections_abc.py', 'PYMODULE'),
|
||||
('sre_constants', '/usr/lib/python3.13/sre_constants.py', 'PYMODULE'),
|
||||
('reprlib', '/usr/lib/python3.13/reprlib.py', 'PYMODULE'),
|
||||
('keyword', '/usr/lib/python3.13/keyword.py', 'PYMODULE'),
|
||||
('codecs', '/usr/lib/python3.13/codecs.py', 'PYMODULE'),
|
||||
('genericpath', '/usr/lib/python3.13/genericpath.py', 'PYMODULE'),
|
||||
('enum', '/usr/lib/python3.13/enum.py', 'PYMODULE'),
|
||||
('sre_parse', '/usr/lib/python3.13/sre_parse.py', 'PYMODULE'),
|
||||
('_weakrefset', '/usr/lib/python3.13/_weakrefset.py', 'PYMODULE'),
|
||||
('copyreg', '/usr/lib/python3.13/copyreg.py', 'PYMODULE'),
|
||||
('weakref', '/usr/lib/python3.13/weakref.py', 'PYMODULE'),
|
||||
('encodings.zlib_codec',
|
||||
'/usr/lib/python3.13/encodings/zlib_codec.py',
|
||||
'PYMODULE'),
|
||||
@@ -590,23 +612,10 @@
|
||||
('encodings.ascii', '/usr/lib/python3.13/encodings/ascii.py', 'PYMODULE'),
|
||||
('encodings.aliases', '/usr/lib/python3.13/encodings/aliases.py', 'PYMODULE'),
|
||||
('encodings', '/usr/lib/python3.13/encodings/__init__.py', 'PYMODULE'),
|
||||
('enum', '/usr/lib/python3.13/enum.py', 'PYMODULE'),
|
||||
('weakref', '/usr/lib/python3.13/weakref.py', 'PYMODULE'),
|
||||
('_weakrefset', '/usr/lib/python3.13/_weakrefset.py', 'PYMODULE'),
|
||||
('warnings', '/usr/lib/python3.13/warnings.py', 'PYMODULE'),
|
||||
('sre_constants', '/usr/lib/python3.13/sre_constants.py', 'PYMODULE'),
|
||||
('heapq', '/usr/lib/python3.13/heapq.py', 'PYMODULE'),
|
||||
('codecs', '/usr/lib/python3.13/codecs.py', 'PYMODULE'),
|
||||
('traceback', '/usr/lib/python3.13/traceback.py', 'PYMODULE'),
|
||||
('linecache', '/usr/lib/python3.13/linecache.py', 'PYMODULE'),
|
||||
('re._parser', '/usr/lib/python3.13/re/_parser.py', 'PYMODULE'),
|
||||
('re._constants', '/usr/lib/python3.13/re/_constants.py', 'PYMODULE'),
|
||||
('re._compiler', '/usr/lib/python3.13/re/_compiler.py', 'PYMODULE'),
|
||||
('re._casefix', '/usr/lib/python3.13/re/_casefix.py', 'PYMODULE'),
|
||||
('re', '/usr/lib/python3.13/re/__init__.py', 'PYMODULE'),
|
||||
('posixpath', '/usr/lib/python3.13/posixpath.py', 'PYMODULE'),
|
||||
('reprlib', '/usr/lib/python3.13/reprlib.py', 'PYMODULE'),
|
||||
('abc', '/usr/lib/python3.13/abc.py', 'PYMODULE'),
|
||||
('collections', '/usr/lib/python3.13/collections/__init__.py', 'PYMODULE'),
|
||||
('ntpath', '/usr/lib/python3.13/ntpath.py', 'PYMODULE'),
|
||||
('stat', '/usr/lib/python3.13/stat.py', 'PYMODULE')])
|
||||
('stat', '/usr/lib/python3.13/stat.py', 'PYMODULE'),
|
||||
('warnings', '/usr/lib/python3.13/warnings.py', 'PYMODULE'),
|
||||
('os', '/usr/lib/python3.13/os.py', 'PYMODULE'),
|
||||
('sre_compile', '/usr/lib/python3.13/sre_compile.py', 'PYMODULE'),
|
||||
('heapq', '/usr/lib/python3.13/heapq.py', 'PYMODULE'),
|
||||
('functools', '/usr/lib/python3.13/functools.py', 'PYMODULE')])
|
||||
|
||||
@@ -90,8 +90,8 @@
|
||||
('python3.13/lib-dynload/_curses.cpython-313-x86_64-linux-gnu.so',
|
||||
'/usr/lib/python3.13/lib-dynload/_curses.cpython-313-x86_64-linux-gnu.so',
|
||||
'EXTENSION'),
|
||||
('libz.so.1', '/lib/x86_64-linux-gnu/libz.so.1', 'BINARY'),
|
||||
('libexpat.so.1', '/lib/x86_64-linux-gnu/libexpat.so.1', 'BINARY'),
|
||||
('libz.so.1', '/lib/x86_64-linux-gnu/libz.so.1', 'BINARY'),
|
||||
('libcrypto.so.3', '/lib/x86_64-linux-gnu/libcrypto.so.3', 'BINARY'),
|
||||
('libzstd.so.1', '/lib/x86_64-linux-gnu/libzstd.so.1', 'BINARY'),
|
||||
('liblzma.so.5', '/lib/x86_64-linux-gnu/liblzma.so.5', 'BINARY'),
|
||||
@@ -114,17 +114,20 @@
|
||||
('securecheck/assets/securecheck-icon.svg',
|
||||
'/home/tuxgyver/scripts/securecheck/securecheck/assets/securecheck-icon.svg',
|
||||
'DATA'),
|
||||
('wheel-0.46.1.dist-info/METADATA',
|
||||
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/METADATA',
|
||||
('securecheck/assets/system_hardening.sh',
|
||||
'/home/tuxgyver/scripts/securecheck/securecheck/assets/system_hardening.sh',
|
||||
'DATA'),
|
||||
('wheel-0.46.1.dist-info/WHEEL',
|
||||
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/WHEEL',
|
||||
('wheel-0.46.1.dist-info/entry_points.txt',
|
||||
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/entry_points.txt',
|
||||
'DATA'),
|
||||
('wheel-0.46.1.dist-info/INSTALLER',
|
||||
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/INSTALLER',
|
||||
'DATA'),
|
||||
('wheel-0.46.1.dist-info/entry_points.txt',
|
||||
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/entry_points.txt',
|
||||
('wheel-0.46.1.dist-info/WHEEL',
|
||||
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/WHEEL',
|
||||
'DATA'),
|
||||
('wheel-0.46.1.dist-info/METADATA',
|
||||
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/METADATA',
|
||||
'DATA'),
|
||||
('base_library.zip',
|
||||
'/home/tuxgyver/scripts/securecheck/build/securecheck/base_library.zip',
|
||||
@@ -132,7 +135,7 @@
|
||||
[],
|
||||
False,
|
||||
False,
|
||||
1775421404,
|
||||
1775457449,
|
||||
[('run',
|
||||
'/home/tuxgyver/.local/lib/python3.13/site-packages/PyInstaller/bootloader/Linux-64bit-intel/run',
|
||||
'EXECUTABLE')],
|
||||
|
||||
@@ -85,8 +85,8 @@
|
||||
('python3.13/lib-dynload/_curses.cpython-313-x86_64-linux-gnu.so',
|
||||
'/usr/lib/python3.13/lib-dynload/_curses.cpython-313-x86_64-linux-gnu.so',
|
||||
'EXTENSION'),
|
||||
('libz.so.1', '/lib/x86_64-linux-gnu/libz.so.1', 'BINARY'),
|
||||
('libexpat.so.1', '/lib/x86_64-linux-gnu/libexpat.so.1', 'BINARY'),
|
||||
('libz.so.1', '/lib/x86_64-linux-gnu/libz.so.1', 'BINARY'),
|
||||
('libcrypto.so.3', '/lib/x86_64-linux-gnu/libcrypto.so.3', 'BINARY'),
|
||||
('libzstd.so.1', '/lib/x86_64-linux-gnu/libzstd.so.1', 'BINARY'),
|
||||
('liblzma.so.5', '/lib/x86_64-linux-gnu/liblzma.so.5', 'BINARY'),
|
||||
@@ -109,17 +109,20 @@
|
||||
('securecheck/assets/securecheck-icon.svg',
|
||||
'/home/tuxgyver/scripts/securecheck/securecheck/assets/securecheck-icon.svg',
|
||||
'DATA'),
|
||||
('wheel-0.46.1.dist-info/METADATA',
|
||||
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/METADATA',
|
||||
('securecheck/assets/system_hardening.sh',
|
||||
'/home/tuxgyver/scripts/securecheck/securecheck/assets/system_hardening.sh',
|
||||
'DATA'),
|
||||
('wheel-0.46.1.dist-info/WHEEL',
|
||||
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/WHEEL',
|
||||
('wheel-0.46.1.dist-info/entry_points.txt',
|
||||
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/entry_points.txt',
|
||||
'DATA'),
|
||||
('wheel-0.46.1.dist-info/INSTALLER',
|
||||
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/INSTALLER',
|
||||
'DATA'),
|
||||
('wheel-0.46.1.dist-info/entry_points.txt',
|
||||
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/entry_points.txt',
|
||||
('wheel-0.46.1.dist-info/WHEEL',
|
||||
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/WHEEL',
|
||||
'DATA'),
|
||||
('wheel-0.46.1.dist-info/METADATA',
|
||||
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/METADATA',
|
||||
'DATA'),
|
||||
('base_library.zip',
|
||||
'/home/tuxgyver/scripts/securecheck/build/securecheck/base_library.zip',
|
||||
|
||||
Binary file not shown.
@@ -178,6 +178,9 @@
|
||||
('securecheck.storage',
|
||||
'/home/tuxgyver/scripts/securecheck/securecheck/storage.py',
|
||||
'PYMODULE'),
|
||||
('securecheck.summary_utils',
|
||||
'/home/tuxgyver/scripts/securecheck/securecheck/summary_utils.py',
|
||||
'PYMODULE'),
|
||||
('securecheck.system_info',
|
||||
'/home/tuxgyver/scripts/securecheck/securecheck/system_info.py',
|
||||
'PYMODULE'),
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -18,8 +18,8 @@ missing module named _frozen_importlib_external - imported by importlib._bootstr
|
||||
excluded module named _frozen_importlib - imported by importlib (optional), importlib.abc (optional), zipimport (top-level)
|
||||
missing module named 'collections.abc' - imported by traceback (top-level), typing (top-level), inspect (top-level), logging (top-level), importlib.resources.readers (top-level), selectors (top-level), tracemalloc (top-level), http.client (top-level), pkg_resources (top-level), setuptools (top-level), setuptools._distutils.filelist (top-level), setuptools._distutils.util (top-level), jaraco.functools (top-level), more_itertools.more (top-level), more_itertools.recipes (top-level), setuptools._distutils._modified (top-level), setuptools._distutils.compat (top-level), setuptools._distutils.spawn (top-level), typing_extensions (top-level), asyncio.base_events (top-level), asyncio.coroutines (top-level), setuptools._distutils.compilers.C.base (top-level), setuptools._distutils.fancy_getopt (top-level), setuptools._reqs (top-level), setuptools.discovery (top-level), setuptools.dist (top-level), setuptools._distutils.command.bdist (top-level), setuptools._distutils.core (top-level), setuptools._distutils.cmd (top-level), setuptools._distutils.dist (top-level), configparser (top-level), setuptools._distutils.extension (top-level), setuptools.config.setupcfg (top-level), setuptools.config.expand (top-level), setuptools.config.pyprojecttoml (top-level), setuptools.config._apply_pyprojecttoml (top-level), tomllib._parser (top-level), setuptools._vendor.tomli._parser (top-level), setuptools.command.egg_info (top-level), setuptools._distutils.command.build (top-level), setuptools._distutils.command.sdist (top-level), setuptools.glob (top-level), setuptools.command._requirestxt (top-level), setuptools.command.bdist_wheel (top-level), platformdirs.api (conditional), platformdirs.windows (conditional), platformdirs.unix (conditional), setuptools._distutils.command.build_ext (top-level), _pyrepl.types (top-level), _pyrepl.readline (top-level), setuptools._distutils.compilers.C.msvc (top-level)
|
||||
missing module named winreg - imported by importlib._bootstrap_external (conditional), mimetypes (optional), urllib.request (delayed, conditional, optional), platform (delayed, optional), platformdirs.windows (delayed, optional), setuptools._distutils.compilers.C.msvc (top-level), setuptools.msvc (conditional)
|
||||
missing module named nt - imported by shutil (conditional), importlib._bootstrap_external (conditional), _colorize (delayed, conditional, optional), os (delayed, conditional, optional), ntpath (optional), ctypes (delayed, conditional), _pyrepl.windows_console (delayed, optional)
|
||||
missing module named _winapi - imported by encodings (delayed, conditional, optional), shutil (conditional), subprocess (conditional), ntpath (optional), mimetypes (optional), sysconfig (delayed), multiprocessing.connection (optional), multiprocessing.spawn (delayed, conditional), multiprocessing.reduction (conditional), multiprocessing.shared_memory (conditional), multiprocessing.heap (conditional), multiprocessing.popen_spawn_win32 (top-level), asyncio.windows_events (top-level), asyncio.windows_utils (top-level)
|
||||
missing module named nt - imported by shutil (conditional), importlib._bootstrap_external (conditional), ntpath (optional), _colorize (delayed, conditional, optional), os (delayed, conditional, optional), ctypes (delayed, conditional), _pyrepl.windows_console (delayed, optional)
|
||||
missing module named _winapi - imported by encodings (delayed, conditional, optional), shutil (conditional), ntpath (optional), subprocess (conditional), mimetypes (optional), sysconfig (delayed), multiprocessing.connection (optional), multiprocessing.spawn (delayed, conditional), multiprocessing.reduction (conditional), multiprocessing.shared_memory (conditional), multiprocessing.heap (conditional), multiprocessing.popen_spawn_win32 (top-level), asyncio.windows_events (top-level), asyncio.windows_utils (top-level)
|
||||
missing module named msvcrt - imported by subprocess (optional), getpass (optional), multiprocessing.spawn (delayed, conditional), multiprocessing.popen_spawn_win32 (top-level), asyncio.windows_events (top-level), asyncio.windows_utils (top-level), _pyrepl.windows_console (top-level)
|
||||
missing module named win32evtlog - imported by logging.handlers (delayed, optional)
|
||||
missing module named win32evtlogutil - imported by logging.handlers (delayed, optional)
|
||||
|
||||
@@ -352,6 +352,7 @@ imported by:
|
||||
• <a href="#securecheck.models">securecheck.models</a>
|
||||
• <a href="#securecheck.status">securecheck.status</a>
|
||||
• <a href="#securecheck.storage">securecheck.storage</a>
|
||||
• <a href="#securecheck.summary_utils">securecheck.summary_utils</a>
|
||||
• <a href="#securecheck.system_info">securecheck.system_info</a>
|
||||
• <a href="#securecheck.tasks">securecheck.tasks</a>
|
||||
• <a href="#setuptools">setuptools</a>
|
||||
@@ -10444,9 +10445,12 @@ imported by:
|
||||
• <a href="#random">random</a>
|
||||
• <a href="#run.py">run.py</a>
|
||||
• <a href="#runpy">runpy</a>
|
||||
• <a href="#securecheck.__main__">securecheck.__main__</a>
|
||||
• <a href="#securecheck.config">securecheck.config</a>
|
||||
• <a href="#securecheck.executor">securecheck.executor</a>
|
||||
• <a href="#securecheck.logging_utils">securecheck.logging_utils</a>
|
||||
• <a href="#securecheck.system_info">securecheck.system_info</a>
|
||||
• <a href="#securecheck.tasks">securecheck.tasks</a>
|
||||
• <a href="#setuptools">setuptools</a>
|
||||
• <a href="#setuptools._core_metadata">setuptools._core_metadata</a>
|
||||
• <a href="#setuptools._distutils.archive_util">setuptools._distutils.archive_util</a>
|
||||
@@ -11840,7 +11844,7 @@ imported by:
|
||||
• <a href="#re._parser">re._parser</a>
|
||||
• <a href="#rlcompleter">rlcompleter</a>
|
||||
• <a href="#run.py">run.py</a>
|
||||
• <a href="#securecheck.app">securecheck.app</a>
|
||||
• <a href="#securecheck.summary_utils">securecheck.summary_utils</a>
|
||||
• <a href="#securecheck.tasks">securecheck.tasks</a>
|
||||
• <a href="#setuptools">setuptools</a>
|
||||
• <a href="#setuptools._distutils.cmd">setuptools._distutils.cmd</a>
|
||||
@@ -12115,6 +12119,7 @@ imported by:
|
||||
• <a href="#securecheck.models">securecheck.models</a>
|
||||
• <a href="#securecheck.status">securecheck.status</a>
|
||||
• <a href="#securecheck.storage">securecheck.storage</a>
|
||||
• <a href="#securecheck.summary_utils">securecheck.summary_utils</a>
|
||||
• <a href="#securecheck.system_info">securecheck.system_info</a>
|
||||
• <a href="#securecheck.tasks">securecheck.tasks</a>
|
||||
|
||||
@@ -12130,6 +12135,7 @@ imports:
|
||||
<a href="#__future__">__future__</a>
|
||||
• <a href="#argparse">argparse</a>
|
||||
• <a href="#datetime">datetime</a>
|
||||
• <a href="#os">os</a>
|
||||
• <a href="#securecheck">securecheck</a>
|
||||
• <a href="#securecheck.app">securecheck.app</a>
|
||||
• <a href="#securecheck.catalog">securecheck.catalog</a>
|
||||
@@ -12159,12 +12165,12 @@ imports:
|
||||
• <a href="#collections">collections</a>
|
||||
• <a href="#curses">curses</a>
|
||||
• <a href="#dataclasses">dataclasses</a>
|
||||
• <a href="#re">re</a>
|
||||
• <a href="#securecheck">securecheck</a>
|
||||
• <a href="#securecheck.assets">securecheck.assets</a>
|
||||
• <a href="#securecheck.models">securecheck.models</a>
|
||||
• <a href="#securecheck.status">securecheck.status</a>
|
||||
• <a href="#securecheck.storage">securecheck.storage</a>
|
||||
• <a href="#securecheck.summary_utils">securecheck.summary_utils</a>
|
||||
• <a href="#securecheck.system_info">securecheck.system_info</a>
|
||||
• <a href="#textwrap">textwrap</a>
|
||||
• <a href="#typing">typing</a>
|
||||
@@ -12280,6 +12286,7 @@ imports:
|
||||
<a href="#__future__">__future__</a>
|
||||
• <a href="#logging">logging</a>
|
||||
• <a href="#logging.handlers">logging.handlers</a>
|
||||
• <a href="#os">os</a>
|
||||
• <a href="#pathlib">pathlib</a>
|
||||
• <a href="#securecheck">securecheck</a>
|
||||
|
||||
@@ -12311,6 +12318,7 @@ imported by:
|
||||
• <a href="#securecheck.catalog">securecheck.catalog</a>
|
||||
• <a href="#securecheck.executor">securecheck.executor</a>
|
||||
• <a href="#securecheck.storage">securecheck.storage</a>
|
||||
• <a href="#securecheck.summary_utils">securecheck.summary_utils</a>
|
||||
• <a href="#securecheck.tasks">securecheck.tasks</a>
|
||||
|
||||
</div>
|
||||
@@ -12361,6 +12369,26 @@ imported by:
|
||||
|
||||
</div>
|
||||
|
||||
<div class="node">
|
||||
<a name="securecheck.summary_utils"></a>
|
||||
<a target="code" href="/home/tuxgyver/scripts/securecheck/securecheck/summary_utils.py" type="text/plain"><tt>securecheck.summary_utils</tt></a>
|
||||
<span class="moduletype">SourceModule</span> <div class="import">
|
||||
imports:
|
||||
<a href="#__future__">__future__</a>
|
||||
• <a href="#re">re</a>
|
||||
• <a href="#securecheck">securecheck</a>
|
||||
• <a href="#securecheck.models">securecheck.models</a>
|
||||
• <a href="#typing">typing</a>
|
||||
|
||||
</div>
|
||||
<div class="import">
|
||||
imported by:
|
||||
<a href="#securecheck.app">securecheck.app</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="node">
|
||||
<a name="securecheck.system_info"></a>
|
||||
<a target="code" href="/home/tuxgyver/scripts/securecheck/securecheck/system_info.py" type="text/plain"><tt>securecheck.system_info</tt></a>
|
||||
@@ -12394,12 +12422,15 @@ imports:
|
||||
<a href="#__future__">__future__</a>
|
||||
• <a href="#datetime">datetime</a>
|
||||
• <a href="#json">json</a>
|
||||
• <a href="#os">os</a>
|
||||
• <a href="#pathlib">pathlib</a>
|
||||
• <a href="#re">re</a>
|
||||
• <a href="#securecheck">securecheck</a>
|
||||
• <a href="#securecheck.assets">securecheck.assets</a>
|
||||
• <a href="#securecheck.executor">securecheck.executor</a>
|
||||
• <a href="#securecheck.models">securecheck.models</a>
|
||||
• <a href="#stat">stat</a>
|
||||
• <a href="#tempfile">tempfile</a>
|
||||
|
||||
</div>
|
||||
<div class="import">
|
||||
@@ -16098,6 +16129,7 @@ imported by:
|
||||
• <a href="#posixpath">posixpath</a>
|
||||
• <a href="#run.py">run.py</a>
|
||||
• <a href="#securecheck.executor">securecheck.executor</a>
|
||||
• <a href="#securecheck.tasks">securecheck.tasks</a>
|
||||
• <a href="#setuptools._core_metadata">setuptools._core_metadata</a>
|
||||
• <a href="#setuptools._distutils.file_util">setuptools._distutils.file_util</a>
|
||||
• <a href="#setuptools._shutil">setuptools._shutil</a>
|
||||
@@ -16612,6 +16644,7 @@ imported by:
|
||||
• <a href="#pkg_resources">pkg_resources</a>
|
||||
• <a href="#securecheck.config">securecheck.config</a>
|
||||
• <a href="#securecheck.executor">securecheck.executor</a>
|
||||
• <a href="#securecheck.tasks">securecheck.tasks</a>
|
||||
• <a href="#setuptools._core_metadata">setuptools._core_metadata</a>
|
||||
• <a href="#setuptools._distutils.compilers.C.base">setuptools._distutils.compilers.C.base</a>
|
||||
• <a href="#setuptools._distutils.util">setuptools._distutils.util</a>
|
||||
@@ -17166,6 +17199,7 @@ imported by:
|
||||
• <a href="#platformdirs.windows">platformdirs.windows</a>
|
||||
• <a href="#securecheck.app">securecheck.app</a>
|
||||
• <a href="#securecheck.models">securecheck.models</a>
|
||||
• <a href="#securecheck.summary_utils">securecheck.summary_utils</a>
|
||||
• <a href="#setuptools">setuptools</a>
|
||||
• <a href="#setuptools._distutils._modified">setuptools._distutils._modified</a>
|
||||
• <a href="#setuptools._distutils.archive_util">setuptools._distutils.archive_util</a>
|
||||
|
||||
BIN
dist/securecheck
vendored
BIN
dist/securecheck
vendored
Binary file not shown.
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
@@ -14,6 +15,20 @@ from .storage import ScenarioStore
|
||||
from .system_info import detect_system
|
||||
|
||||
|
||||
def ensure_root() -> None:
|
||||
if os.geteuid() == 0:
|
||||
return
|
||||
if os.environ.get("SECURECHECK_SKIP_SUDO") == "1":
|
||||
return
|
||||
args = sys.argv[1:]
|
||||
if getattr(sys, "frozen", False):
|
||||
target = sys.argv[0]
|
||||
cmd = ["sudo", "-E", target, *args]
|
||||
else:
|
||||
cmd = ["sudo", "-E", sys.executable, "-m", "securecheck", *args]
|
||||
os.execvp("sudo", cmd)
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="SecureCheck - console semi-graphique pour contrôles sécurité Linux")
|
||||
parser.add_argument("--dry-run", action="store_true", help="Simule les commandes sans modifier le système")
|
||||
@@ -66,6 +81,7 @@ def print_summary(results, run_log_path, system) -> None:
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ensure_root()
|
||||
args = parse_args()
|
||||
paths = build_paths()
|
||||
ensure_app_dirs(paths)
|
||||
@@ -77,6 +93,7 @@ def main() -> int:
|
||||
store = ScenarioStore(paths.scenario_file, builtin_scenarios())
|
||||
|
||||
if args.list_scenarios:
|
||||
print(f"Scénarios stockés dans {paths.scenario_file}")
|
||||
for scenario in store.list_all():
|
||||
print(f"{scenario.name}: {scenario.description}")
|
||||
return 0
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
securecheck/__pycache__/summary_utils.cpython-313.pyc
Normal file
BIN
securecheck/__pycache__/summary_utils.cpython-313.pyc
Normal file
Binary file not shown.
Binary file not shown.
@@ -1,7 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import curses
|
||||
import re
|
||||
import textwrap
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
@@ -11,6 +10,7 @@ from .assets import banner_text
|
||||
from .models import Scenario, TaskDefinition, TaskResult
|
||||
from .status import StatusItem
|
||||
from .storage import ScenarioStore
|
||||
from .summary_utils import SCORE_PREFIXES, clean_text, collect_details
|
||||
from .system_info import SystemInfo
|
||||
|
||||
|
||||
@@ -334,18 +334,12 @@ class SecureCheckTUI:
|
||||
|
||||
|
||||
class RunSummaryTUI:
|
||||
ANSI_RE = re.compile(r"\x1b\[[0-?]*[ -/]*[@-~]")
|
||||
|
||||
def __init__(self, results: list[TaskResult], status_items: list[StatusItem], run_log_path: str) -> None:
|
||||
self.results = results
|
||||
self.status_items = status_items
|
||||
self.run_log_path = run_log_path
|
||||
self.scroll_offset = 0
|
||||
|
||||
@classmethod
|
||||
def _clean(cls, text: str) -> str:
|
||||
return cls.ANSI_RE.sub("", text)
|
||||
|
||||
def run(self) -> None:
|
||||
curses.wrapper(self._main)
|
||||
|
||||
@@ -368,8 +362,7 @@ class RunSummaryTUI:
|
||||
height, width = stdscr.getmaxyx()
|
||||
ok_count = sum(1 for result in self.results if result.success)
|
||||
ko_count = len(self.results) - ok_count
|
||||
score_lines: list[str] = []
|
||||
notif_lines: list[str] = []
|
||||
score_lines, notif_lines = collect_details(self.results)
|
||||
entries: list[tuple[str, int]] = []
|
||||
entries.append((f"OK: {ok_count} | ECHEC: {ko_count} | Appuie sur une touche pour revenir", curses.color_pair(Palette.HEADER) | curses.A_BOLD))
|
||||
for result in self.results:
|
||||
@@ -377,12 +370,10 @@ class RunSummaryTUI:
|
||||
color = curses.color_pair(Palette.SUCCESS if result.success else Palette.ERROR)
|
||||
entries.append((f"{status:<4} {result.label} ({result.duration_seconds:.1f}s)", color | curses.A_BOLD))
|
||||
for detail in result.details:
|
||||
clean = self._clean(detail)
|
||||
if clean.startswith("Score Lynis") or clean.startswith("Hardening index"):
|
||||
score_lines.append(clean)
|
||||
clean = clean_text(detail).strip()
|
||||
if any(clean.startswith(prefix) for prefix in SCORE_PREFIXES):
|
||||
continue
|
||||
if clean.startswith("Modifications") or clean.strip().startswith("•"):
|
||||
notif_lines.append(clean)
|
||||
if clean.startswith("Modifications") or clean.startswith("•"):
|
||||
continue
|
||||
wrapped = textwrap.wrap(clean, width - 9) or [""]
|
||||
for line in wrapped:
|
||||
|
||||
1154
securecheck/assets/system_hardening.sh
Normal file
1154
securecheck/assets/system_hardening.sh
Normal file
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@ from .tasks import (
|
||||
log_rotation,
|
||||
lynis_audit,
|
||||
rootkit_check,
|
||||
system_hardening,
|
||||
system_update,
|
||||
utilities_setup,
|
||||
zram_setup,
|
||||
@@ -98,6 +99,14 @@ def task_catalog() -> list[TaskDefinition]:
|
||||
handler=lambda context: None,
|
||||
default_selected=False,
|
||||
),
|
||||
TaskDefinition(
|
||||
key="system_hardening",
|
||||
label="Durcissement système (hardening)",
|
||||
description="Applique un durcissement complet : sysctl, SSH, PAM, modules noyau, permissions, AIDE, bannières, limites de sécurité.",
|
||||
category="Sécurité",
|
||||
handler=lambda context: None,
|
||||
default_selected=False,
|
||||
),
|
||||
]
|
||||
|
||||
handlers = {
|
||||
@@ -111,6 +120,7 @@ def task_catalog() -> list[TaskDefinition]:
|
||||
"zram_setup": zram_setup,
|
||||
"firewall_setup": firewall_setup,
|
||||
"docker_setup": docker_setup,
|
||||
"system_hardening": system_hardening,
|
||||
}
|
||||
return [bind(task, handlers[task.key]) for task in base]
|
||||
|
||||
|
||||
@@ -1,15 +1,28 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
from logging.handlers import RotatingFileHandler
|
||||
from pathlib import Path
|
||||
|
||||
_LOG_FILE_MODE = 0o644
|
||||
|
||||
|
||||
def _prepare_log_file(path: Path) -> None:
|
||||
"""Crée le fichier de log s'il n'existe pas et fixe ses permissions à 644."""
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
if not path.exists():
|
||||
path.touch()
|
||||
os.chmod(path, _LOG_FILE_MODE)
|
||||
|
||||
|
||||
def setup_logging(log_file: Path) -> logging.Logger:
|
||||
logger = logging.getLogger("securecheck")
|
||||
if logger.handlers:
|
||||
return logger
|
||||
|
||||
_prepare_log_file(log_file)
|
||||
|
||||
logger.setLevel(logging.INFO)
|
||||
logger.propagate = False
|
||||
formatter = logging.Formatter(
|
||||
@@ -24,6 +37,8 @@ def setup_logging(log_file: Path) -> logging.Logger:
|
||||
|
||||
|
||||
def attach_run_handler(logger: logging.Logger, run_log_file: Path) -> RotatingFileHandler:
|
||||
_prepare_log_file(run_log_file)
|
||||
|
||||
formatter = logging.Formatter(
|
||||
fmt="%(asctime)s | %(levelname)s | %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S",
|
||||
|
||||
@@ -91,12 +91,10 @@ def collect_status(system: SystemInfo) -> list[StatusItem]:
|
||||
|
||||
docker_active = _command_exists("docker") and _service_active("docker.service")
|
||||
fail2ban_active = _command_exists("fail2ban-client") and _service_active("fail2ban.service")
|
||||
services.append(StatusItem("Services", "Service Docker", docker_active, "actif" if docker_active else "inactif"))
|
||||
services.append(StatusItem("Services", "Service fail2ban", fail2ban_active, "actif" if fail2ban_active else "inactif"))
|
||||
services.append(StatusItem("Services", "Docker", docker_active, "actif" if docker_active else "inactif"))
|
||||
services.append(StatusItem("Services", "fail2ban", fail2ban_active, "actif" if fail2ban_active else "inactif"))
|
||||
avahi_running = _command_exists("avahi-daemon") and _service_active("avahi-daemon")
|
||||
services.append(StatusItem("Services", "Avahi", not avahi_running, "désactivé" if not avahi_running else "actif"))
|
||||
services.append(_binary_status("Services", "Docker", "docker"))
|
||||
services.append(_binary_status("Services", "fail2ban", "fail2ban-client"))
|
||||
|
||||
poste.extend([
|
||||
_binary_status("Poste", "zsh", "zsh"),
|
||||
|
||||
41
securecheck/summary_utils.py
Normal file
41
securecheck/summary_utils.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from typing import Sequence
|
||||
|
||||
from .models import TaskResult
|
||||
|
||||
ANSI_RE = re.compile(r"\x1b\[[0-?]*[ -/]*[@-~]")
|
||||
SCORE_PREFIXES = ("Score Lynis", "Hardening index")
|
||||
RECOMMENDATION_PREFIXES = (
|
||||
"Modifications recommandées",
|
||||
"•",
|
||||
)
|
||||
|
||||
|
||||
def clean_text(text: str) -> str:
|
||||
return ANSI_RE.sub("", text)
|
||||
|
||||
|
||||
def collect_details(results: Sequence[TaskResult]) -> tuple[list[str], list[str]]:
|
||||
score_lines: list[str] = []
|
||||
recommendation_lines: list[str] = []
|
||||
seen_scores: set[str] = set()
|
||||
seen_recommendations: set[str] = set()
|
||||
|
||||
for result in results:
|
||||
for detail in result.details:
|
||||
line = clean_text(detail).strip()
|
||||
if not line:
|
||||
continue
|
||||
|
||||
if any(line.startswith(prefix) for prefix in SCORE_PREFIXES) and line not in seen_scores:
|
||||
seen_scores.add(line)
|
||||
score_lines.append(line)
|
||||
continue
|
||||
|
||||
if any(line.startswith(prefix) for prefix in RECOMMENDATION_PREFIXES) and line not in seen_recommendations:
|
||||
seen_recommendations.add(line)
|
||||
recommendation_lines.append(line)
|
||||
|
||||
return score_lines, recommendation_lines
|
||||
@@ -1,7 +1,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import stat
|
||||
import tempfile
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
@@ -195,8 +198,21 @@ def rootkit_check(context: ExecutionContext, task: TaskDefinition) -> TaskResult
|
||||
report_path = context.paths.report_dir / f"{datetime.now().strftime('%Y%m%d-%H%M%S')}-rootkit-report.json"
|
||||
context.runner.write_text_file(report_path, json.dumps(report_payload, indent=2) + "\n", mode=0o640, requires_root=False)
|
||||
details.append(f"Rapport rootkits: {report_path}")
|
||||
success = rkhunter_result.returncode == 0 and chkrootkit_result.returncode == 0
|
||||
return context.make_result(task, success=success, changed=changed, started_at=started_at, details=details, error=None if success else "Vérification rootkit incomplète ou avec alertes")
|
||||
|
||||
# rkhunter: rc=0 clean, rc=1 warnings trouvés, rc=2+ erreur critique (outil n'a pas pu tourner)
|
||||
rkhunter_ran = rkhunter_result.returncode <= 1
|
||||
rkhunter_warnings = [line for line in rkhunter_result.stdout.splitlines() if line.startswith("Warning:")]
|
||||
if rkhunter_warnings:
|
||||
details.append(f"rkhunter: {len(rkhunter_warnings)} warning(s) à vérifier dans le rapport")
|
||||
# chkrootkit: rc=0 signifie que l'outil a tourné (pas qu'il n'y a pas d'infection — les INFECTED sont dans stdout)
|
||||
chkrootkit_ran = chkrootkit_result.returncode == 0
|
||||
infected_lines = [line for line in chkrootkit_result.stdout.splitlines() if "INFECTED" in line]
|
||||
if infected_lines:
|
||||
details.append(f"chkrootkit: {len(infected_lines)} détection(s) à vérifier dans le rapport")
|
||||
|
||||
success = rkhunter_ran and chkrootkit_ran
|
||||
error = None if success else "Les outils de vérification n'ont pas pu s'exécuter correctement"
|
||||
return context.make_result(task, success=success, changed=changed, started_at=started_at, details=details, error=error)
|
||||
|
||||
|
||||
def log_rotation(context: ExecutionContext, task: TaskDefinition) -> TaskResult:
|
||||
@@ -434,7 +450,6 @@ def utilities_setup(context: ExecutionContext, task: TaskDefinition) -> TaskResu
|
||||
context.runner.run(["cp", "-f", str(aide_db_new), "/var/lib/aide/aide.db"], requires_root=True, check=False)
|
||||
details.append("Base AIDE mise à jour")
|
||||
if context.runner.command_exists("systemctl"):
|
||||
context.runner.run(["systemctl", "enable", "--now", "aidecheck.timer"], requires_root=True, check=False)
|
||||
context.runner.run(["systemctl", "enable", "--now", "dailyaidecheck.timer"], requires_root=True, check=False)
|
||||
details.append("Timers AIDE activés")
|
||||
|
||||
@@ -560,6 +575,83 @@ def docker_setup(context: ExecutionContext, task: TaskDefinition) -> TaskResult:
|
||||
return _result(context, task, started_at, changed=changed, details=details)
|
||||
|
||||
|
||||
def system_hardening(context: ExecutionContext, task: TaskDefinition) -> TaskResult:
|
||||
started_at = datetime.now()
|
||||
details: list[str] = []
|
||||
|
||||
script_content = asset_text("system_hardening.sh")
|
||||
|
||||
tmp_dir = context.paths.state_dir
|
||||
tmp_dir.mkdir(parents=True, exist_ok=True)
|
||||
script_path: Path | None = None
|
||||
try:
|
||||
with tempfile.NamedTemporaryFile(
|
||||
mode="w", suffix=".sh", delete=False, dir=tmp_dir, encoding="utf-8"
|
||||
) as fh:
|
||||
fh.write(script_content)
|
||||
script_path = Path(fh.name)
|
||||
|
||||
current_mode = stat.S_IMODE(script_path.stat().st_mode)
|
||||
script_path.chmod(current_mode | 0o111)
|
||||
|
||||
env = os.environ.copy()
|
||||
env.update({
|
||||
"AUTO_YES": "yes",
|
||||
"AUTO_SSH_PORT": "22",
|
||||
"AUTO_CHANGE_ROOT_PWD": "no",
|
||||
"AUTO_DISABLE_ROOT_LOGIN": "no",
|
||||
"AUTO_SKIP_LYNIS": "no",
|
||||
"AUTO_ENABLE_FAIL2BAN": "yes",
|
||||
"AUTO_ENABLE_UFW": "yes",
|
||||
"AUTO_ENABLE_AIDE": "yes",
|
||||
"AUTO_ENABLE_CLAMAV": "yes",
|
||||
"AUTO_SKIP_PORTS_DETECTION": "no",
|
||||
"DEBIAN_FRONTEND": "noninteractive",
|
||||
})
|
||||
|
||||
result = context.runner.run(
|
||||
["bash", str(script_path), "--unattended"],
|
||||
requires_root=True,
|
||||
check=False,
|
||||
capture_output=True,
|
||||
env=env,
|
||||
)
|
||||
|
||||
ok_steps = [line for line in result.stdout.splitlines() if "[OK]" in line]
|
||||
warn_steps = [line for line in result.stdout.splitlines() if "[WARN]" in line]
|
||||
err_steps = [line for line in result.stdout.splitlines() if "[ERR]" in line]
|
||||
|
||||
details.append(f"{len(ok_steps)} étape(s) réussie(s)")
|
||||
if warn_steps:
|
||||
details.append(f"{len(warn_steps)} avertissement(s)")
|
||||
if err_steps:
|
||||
details.append(f"{len(err_steps)} erreur(s)")
|
||||
for line in err_steps[:5]:
|
||||
details.append(f" {line.strip()}")
|
||||
|
||||
score_line = next(
|
||||
(line for line in result.stdout.splitlines() if "Score:" in line and "Lynis" in line),
|
||||
None,
|
||||
)
|
||||
if score_line:
|
||||
details.append(score_line.strip())
|
||||
|
||||
log_path = Path("/var/log/system_hardening.log")
|
||||
if log_path.exists():
|
||||
details.append(f"Log complet: {log_path}")
|
||||
backup_path = next(Path("/root").glob("backup_hardening_*"), None) if Path("/root").exists() else None
|
||||
if backup_path:
|
||||
details.append(f"Sauvegardes: {backup_path}")
|
||||
|
||||
success = result.returncode == 0 and not err_steps
|
||||
error = None if success else f"Le script s'est terminé avec le code {result.returncode}"
|
||||
return context.make_result(task, success=success, changed=True, started_at=started_at, details=details, error=error)
|
||||
|
||||
finally:
|
||||
if script_path and script_path.exists():
|
||||
script_path.unlink(missing_ok=True)
|
||||
|
||||
|
||||
def bind(task: TaskDefinition, func) -> TaskDefinition:
|
||||
return TaskDefinition(
|
||||
key=task.key,
|
||||
|
||||
Reference in New Issue
Block a user