Outils pour utilisateurs

Outils du site


linux:rewinged

Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Les deux révisions précédentesRévision précédente
Prochaine révision
Révision précédente
linux:rewinged [2026/09/12 07:05] – rootlinux:rewinged [2026/09/18 17:40] (Version actuelle) – [13.2 Règles personnalisées] root
Ligne 8: Ligne 8:
  
 <code> <code>
-                              Poste client +                                Poste client 
-                                   | +                                     | 
-                                   | HTTPS 443 +                                     | HTTPS 443 
-                                   v +                                     v 
-                    +------------------------------+ +                      +------------------------------+ 
-                    | VM Caddy (frontal existant)  | +                      | VM Caddy (frontal existant)  | 
-                    | repo.makeitsimple.be         | +                      | repo.makeitsimple.be         | 
-                    | TLS Let's Encrypt            | +                      | TLS Let's Encrypt            | 
-                    +---------------+--------------+ +                      +---------------+--------------+ 
-                                    | +                                      | 
-                                    | HTTP 8080 (réseau interne) +                                      | HTTP 8080 (interne) 
-                                    v +                                      v 
-Forgejo                 +------------------------------+ +Forgejo                +--------------------------------+ 
-(winget-manifests)      | VM Debian  winget-repo       | +mis/winget-manifests   | VM Debian  winget-repo         | 
-  |                     |                              | +  manifests YAML       |                                | 
-  | git pull --ff-only  |  rewinged (binaire Go)       | +  + MSI en Git LFS     |  rewinged :8080                | 
-  | timer 1 min         |    lit /srv/winget/manifests | +        |              |    -manifestPath  manifests    | 
-  +-------------------->|                              | +        | git pull     |    -autoInternalizePath        | 
-                        +------------------------------++        | + lfs pull   |         installers             | 
 +        | timer 1 min  |                                | 
 +        +------------->|                                | 
 +                       +--------------------------------+
 </code> </code>
  
 ^ Composant ^ Machine ^ Rôle ^ ^ Composant ^ Machine ^ Rôle ^
-| Forgejo | existante | Source de vérité des manifests. Revue et historique. | +| Forgejo | existante | Source de vérité : manifests YAML et installeurs MSI. | 
-| Caddy | frontal existant | Terminaison TLS uniquement. Aucun fichier local. | +| Caddy frontal | frontal existant | Terminaison TLS. Aucun fichier local. | 
-| rewinged | VM dédiée | Sert l'API REST ''Microsoft.Rest'' et les installeurs. | +| rewinged | VM dédiée | API REST ''Microsoft.Rest'' **et** service des installeurs. | 
-| Wazuh | existant | FIM sur manifests et binaire de la VM rewinged. |+| Wazuh | existant | FIM sur manifests, installeurs et binaires. |
  
-**Deux machines distinctes.** rewinged n'écoute donc pas sur la boucle locale +**Le dépôt Forgejo peut rester privé.** Les postes ne dialoguent qu'avec 
-mais sur l'interface interne, filtrée au pare-feu vers la seule IP du frontal.+''repo.makeitsimple.be''. Aucun accès anonyme à Forgejo n'est requis, puisque 
 +la synchronisation se fait par clé de déploiement SSH.
  
-**Principe de sécurité** : l'intégrité des paquets ne repose pas sur le TLS +**Chaîne de confiance.** L'intégrité des paquets ne repose pas sur le TLS mais 
-mais sur le SHA256 déclaré dans chaque manifest. Un installeur substitué +sur le SHA256 déclaré dans chaque manifest. Un installeur substitué échoue à 
-échoue à la vérification et winget refuse l'installation.+la vérification et winget refuse l'installation.
  
 ===== 2. Prérequis ===== ===== 2. Prérequis =====
  
-  * VM Debian 12 minimale, 1 vCPU / 1 Go RAM / 20 Go suffisent +  * VM Debian 12 minimale, 1 vCPU / 1 Go RAM / disque dimensionné au volume d'installeurs 
-  * Enregistrement DNS public ''repo.makeitsimple.be'' vers l'IP de la VM +  * Enregistrement DNS public ''repo.makeitsimple.be'' vers le frontal Caddy 
-  * Ports 80 et 443 joignables (ACME + service) +  * Dépôt Forgejo ''mis/winget-manifests'' avec Git LFS activé
-  * Dépôt Forgejo ''mis/winget-manifests'' créé+
   * Clé de déploiement Forgejo **en lecture seule**   * Clé de déploiement Forgejo **en lecture seule**
 +  * Paquets ''git'' et ''git-lfs'' sur la VM
  
 ===== 3. Installation de rewinged ===== ===== 3. Installation de rewinged =====
Ligne 56: Ligne 60:
 <code bash> <code bash>
 useradd --system --home /srv/winget --shell /usr/sbin/nologin rewinged useradd --system --home /srv/winget --shell /usr/sbin/nologin rewinged
-mkdir -p /srv/winget/{manifests,installers}+mkdir -p /srv/winget
 chown -R rewinged:rewinged /srv/winget chown -R rewinged:rewinged /srv/winget
 </code> </code>
 +
 +Les répertoires ''manifests'' et ''installers'' seront créés par le clone Git
 +(section 6).
 +
 +Le compte ''rewinged'' doit pouvoir écrire dans ''installers'' : c'est là que
 +l'auto-internalisation dépose les installeurs tiers rapatriés.
  
 ==== 3.2 Binaire ==== ==== 3.2 Binaire ====
Ligne 76: Ligne 86:
 </code> </code>
  
-<WRAP center round important> +==== 3.3 Options utiles ====
-Relever dans ''--help'' le nom exact du flag désignant le répertoire de +
-manifests, et vérifier si rewinged recharge à chaud ou s'il faut un signal +
-après un ''git pull''. Adapter les sections 3.3 et 6 en conséquence. +
-</WRAP>+
  
-==== 3.3 Service systemd ====+^ Flag ^ Rôle ^ 
 +| ''-manifestPath'' | Répertoire des manifests (défaut ''./packages'') | 
 +| ''-autoInternalize'' | Rapatrie et sert localement les installeurs distants | 
 +| ''-autoInternalizePath'' | Répertoire des installeurs servis (défaut ''./installers'') | 
 +| ''-trustedProxies'' | IP du frontal, pour journaliser l'IP réelle du client | 
 +| ''-listen'' | Adresse et port d'écoute | 
 +| ''-sourceAuthType'' | ''none'' ou ''microsoftEntraId'' | 
 +| ''-https'' | TLS assuré par rewinged, inutile derrière un frontal | 
 +| ''-logLevel'' | ''disable'', ''error'', ''warn'', ''info'', ''debug'', ''trace'' | 
 + 
 +**Deux usages de ''-autoInternalizePath''.** rewinged y dépose les installeurs 
 +qu'il rapatrie automatiquement, et sert directement tout fichier qui s'y 
 +trouve sous ''/installers/''. Les MSI synchronisés depuis Forgejo y sont donc 
 +servis sans serveur de fichiers supplémentaire. 
 + 
 +Vérifié : 
 +<code bash> 
 +curl -I http://10.0.0.20:8080/installers/mis-agent-1.2.0.msi 
 +# HTTP/1.1 200 OK 
 +# Content-Type: application/x-msi 
 +# Accept-Ranges: bytes 
 +</code> 
 + 
 +==== 3.4 Service systemd ====
  
 <file ini /etc/systemd/system/rewinged.service> <file ini /etc/systemd/system/rewinged.service>
Ligne 92: Ligne 121:
 User=rewinged User=rewinged
 Group=rewinged Group=rewinged
-ExecStart=/usr/local/bin/rewinged -listen 10.0.0.20:8080+ExecStart=/usr/local/bin/rewinged \ 
 +  -listen 10.0.0.20:8080 \ 
 +  -manifestPath /srv/winget/manifests \ 
 +  -autoInternalize \ 
 +  -autoInternalizePath /srv/winget/installers \ 
 +  -trustedProxies 10.0.0.10 \ 
 +  -logLevel info
 Restart=on-failure Restart=on-failure
 RestartSec=5 RestartSec=5
 +
 +# Journalisation vers fichier, pour collecte Wazuh en JSON natif
 +StandardOutput=append:/var/log/rewinged/rewinged.log
 +StandardError=append:/var/log/rewinged/rewinged.log
  
 # Durcissement # Durcissement
Ligne 106: Ligne 145:
 MemoryDenyWriteExecute=true MemoryDenyWriteExecute=true
 ReadOnlyPaths=/srv/winget/manifests ReadOnlyPaths=/srv/winget/manifests
-ReadWritePaths=/srv/winget/installers+ReadWritePaths=/srv/winget/installers /var/log/rewinged
  
 [Install] [Install]
Ligne 113: Ligne 152:
  
 <code bash> <code bash>
 +mkdir -p /var/log/rewinged
 +chown rewinged:rewinged /var/log/rewinged
 +
 systemctl daemon-reload systemctl daemon-reload
 systemctl enable --now rewinged systemctl enable --now rewinged
Ligne 118: Ligne 160:
 </code> </code>
  
-rewinged écoute en clair sur le réseau interne. Le TLS est assuré par le +Rotation du journal : 
-frontal Caddy, sur une autre machine.+ 
 +<file - /etc/logrotate.d/rewinged> 
 +/var/log/rewinged/rewinged.log { 
 +    daily 
 +    rotate 30 
 +    compress 
 +    missingok 
 +    notifempty 
 +    copytruncate 
 +    su rewinged rewinged 
 +} 
 +</file>
  
-==== 3.4 Pare-feu de la VM rewinged ====+===== 4. Pare-feu de la VM =====
  
-Le port 8080 ne doit être joignable que par le frontal.+Seul le frontal doit joindre rewinged.
  
 <code bash> <code bash>
Ligne 133: Ligne 186:
 </code> </code>
  
-===== 4. Configuration du frontal Caddy =====+===== 5. Configuration du frontal Caddy =====
  
-À ajouter au Caddyfile **de la machine frontale**, pas de la VM rewinged.+À ajouter au Caddyfile **de la machine frontale**.
  
 <file - /etc/caddy/Caddyfile> <file - /etc/caddy/Caddyfile>
Ligne 155: Ligne 208:
 } }
 </file> </file>
- 
-Un seul ''reverse_proxy'', sans bloc ''file_server'' : le frontal n'héberge 
-aucun fichier de la solution. Les installeurs sont servis soit par Forgejo, 
-soit par rewinged lui-même (section 6). 
  
 Winget exige HTTPS avec une chaîne validée par le magasin de confiance Winget exige HTTPS avec une chaîne validée par le magasin de confiance
Ligne 164: Ligne 213:
 interne sur chaque poste. interne sur chaque poste.
  
-===== 5. Dépôt Git des manifests =====+Un seul ''reverse_proxy'' : rewinged sert à la fois l'API sous ''/api'' et les 
 +installeurs sous ''/installers''.
  
-Le dépôt ne contient **que du YAML**. Les binaires sont hébergés séparément +===== 6. Dépôt Forgejo =====
-(voir section 6).+
  
-==== 5.1 Structure ====+Un seul dépôt contient manifests et installeurs. Les MSI passent par Git LFS 
 +pour ne pas alourdir l'historique. 
 + 
 +==== 6.1 Structure ====
  
 <code> <code>
 winget-manifests/ winget-manifests/
 +├── .gitattributes
 ├── manifests/ ├── manifests/
-│   └── m/ +│   └── m/MakeItSimple/ 
-│       └── MakeItSimple/ +│       ├── Agent/1.2.0/MakeItSimple.Agent.yaml 
-│           ├── Agent/ +│       └── RustDesk/1.4.3/MakeItSimple.RustDesk.yaml 
-│           │   └── 1.2.0/ +└── installers/ 
-│           │       └── MakeItSimple.Agent.yaml +    ├── mis-agent-1.2.0.msi 
-│           └── RustDesk/ +    └── rustdesk-1.4.3.msi
-│               └── 1.4.3/ +
-│                   └── MakeItSimple.RustDesk.yaml +
-└── README.md+
 </code> </code>
  
-==== 5.2 Clone initial ====+==== 6.2 Activation de Git LFS ==== 
 + 
 +Côté poste de travail, une seule fois :
  
 <code bash> <code bash>
 +git lfs install
 +git lfs track "*.msi"
 +git lfs track "*.exe"
 +git add .gitattributes
 +git commit -m "Suivi LFS des installeurs"
 +git push
 +</code>
 +
 +Vérifier que LFS est activé sur l'instance Forgejo et que le quota de
 +stockage est suffisant. LFS ne fait pas de delta : chaque version d'un MSI
 +est stockée intégralement.
 +
 +==== 6.3 Clone initial sur la VM ====
 +
 +<code bash>
 +apt install git-lfs
 +sudo -u rewinged git lfs install --skip-repo
 +
 sudo -u rewinged git clone \ sudo -u rewinged git clone \
   git@forgejo.makeitsimple.be:mis/winget-manifests.git \   git@forgejo.makeitsimple.be:mis/winget-manifests.git \
-  /srv/winget/manifests+  /srv/winget 
 + 
 +sudo -u rewinged git -C /srv/winget lfs pull
 </code> </code>
  
Ligne 196: Ligne 268:
 ne doit jamais pouvoir écrire dans le dépôt. ne doit jamais pouvoir écrire dans le dépôt.
  
-===== 6. Stockage des installeurs =====+===== 7. Synchronisation automatique =====
  
-Trois options. Choix retenu : **package registry Forgejo**, qui évite le +Pas de webhook : un timer évite d'exposer un endpoint entrant sur la machine 
-SFTP tout en gardant le dépôt Git léger et purement textuel.+qui décide de ce que le parc installe. Le flux part toujours du serveur vers 
 +Forgejo.
  
-^ Option ^ Avantage ^ Inconvénient ^ +==== 7.1 Rechargement natif ====
-| Package registry Forgejo | Dépôt Git léger, publication en une commande | Un système de plus à connaître | +
-| Git LFS | Manifest et binaire dans un même commit | Pas de delta, volume croissant | +
-| URL éditeur | Rien à héberger | Ne convient qu'aux logiciels tiers |+
  
-==== 6.1 Publication d'un installeur ====+rewinged surveille le répertoire de manifests et recharge sans intervention. 
 +Confirmé au démarrage du service :
  
-<code bash> +<code> 
-curl --user vincent:$FORGEJO_TOKEN \ +{"level":"info","message":"watching manifestPath for changes"}
-  --upload-file mis-agent-1.2.0.msi \ +
-  https://forgejo.makeitsimple.be/api/packages/mis/generic/agent/1.2.0/mis-agent-1.2.0.msi+
 </code> </code>
  
-L'URL obtenue est celle à placer dans le champ ''InstallerUrl'' du manifest.+**Conséquence** : aucun redémarrage n'est nécessaire après une publication. 
 +L'unité de synchronisation tourne donc intégralement sous le compte 
 +''rewinged'', sans privilège root ni règle polkit.
  
-<WRAP center round important> +==== 7.2 Script de synchronisation ==== 
-**Point bloquant à vérifier.** Winget ne sait pas s'authentifier auprès d'une + 
-source auto-hébergée : il télécharge l'installeur en anonyme. Si l'organisation +<file bash /usr/local/bin/winget-sync.sh> 
-Forgejo ''mis'' est privée, le téléchargement échouera sur tous les postes.+#!/bin/bash 
 +# 
 +# Synchronise le depot de paquets depuis Forgejo. 
 +# Execute par winget-sync.timer sous le compte rewinged. 
 +# 
 +set -euo pipefail 
 + 
 +REPO_DIR="/srv/winget" 
 +BRANCH="main" 
 + 
 +cd "$REPO_DIR" 
 + 
 +BEFORE=$(git rev-parse HEAD) 
 + 
 +git fetch origin "$BRANCH" 
 +git merge --ff-only "origin/${BRANCH}" 
 +git lfs pull origin 
 + 
 +AFTER=$(git rev-parse HEAD) 
 + 
 +if [ "$BEFORE" != "$AFTER" ]; then 
 +    # Verification de signature : voir section 13.3 
 +    if ! git verify-commit HEAD >/dev/null 2>&1; then 
 +        logger -t winget-sync -p auth.crit \ 
 +            "SIGNATURE INVALIDE sur ${AFTER:0:8} - retour a ${BEFORE:0:8}" 
 +        git reset --hard "$BEFORE" 
 +        exit 1 
 +    fi 
 +    logger -t winget-sync "Publication verifiee: ${BEFORE:0:8} -> ${AFTER:0:8}" 
 +fi 
 + 
 +exit 0 
 +</file> 
 + 
 +Le bloc ''verify-commit'' suppose que les commits sont signés en GPG et que la 
 +clé publique du signataire est présente dans le trousseau du compte 
 +''rewinged''. Le retirer si la signature n'est pas en place — mais le conserver 
 +est ce qui rend la détection Wazuh exploitable (13.3).
  
-Tester avant de bâtir dessus : 
 <code bash> <code bash>
-curl -I https://forgejo.makeitsimple.be/api/packages/mis/generic/agent/1.2.0/mis-agent-1.2.0.msi+chown root:root /usr/local/bin/winget-sync.sh 
 +chmod 0755 /usr/local/bin/winget-sync.sh
 </code> </code>
-Un 401 ou 404 impose de rendre l'organisation lisible publiquement, ou de 
-basculer sur l'internalisation rewinged (6.2). 
-</WRAP> 
  
-==== 6.2 Logiciels tiers ====+Le script appartient à root en lecture seule pour ''rewinged'' : le compte de 
 +service exécute le script sans pouvoir le modifier.
  
-Pour Chrome, Acrobat et assimilés, deux voies :+Le ''merge --ff-only'' est délibéré : si l'historique local diverge, l'opération 
 +échoue au lieu de fusionner silencieusement. Une divergence signifie une 
 +écriture locale sur le serveur, ce qui ne doit jamais arriver.
  
-  * pointer directement l'URL éditeur dans le manifest +Le ''logger'' trace chaque publication dans syslog. C'est ce qui permettra de 
-  * utiliser l'internalisation de rewinged, qui télécharge et sert lui-même +corréler les alertes FIM Wazuh avec une publication légitime (section 12.2).
-    l'installeur — utile pour figer une version que l'éditeur a retirée, et +
-    pour les postes sans accès internet direct+
  
-===== 7. Synchronisation automatique ===== +==== 7.3 Unités systemd ====
- +
-Pas de webhook : un timer évite d'exposer un endpoint entrant sur la machine +
-qui décide de ce que le parc installe. Le flux part toujours du serveur vers +
-Forgejo.+
  
 <file ini /etc/systemd/system/winget-sync.service> <file ini /etc/systemd/system/winget-sync.service>
 [Unit] [Unit]
-Description=Sync winget manifests from Forgejo+Description=Sync winget manifests and installers from Forgejo 
 +After=network-online.target 
 +Wants=network-online.target
  
 [Service] [Service]
 Type=oneshot Type=oneshot
 User=rewinged User=rewinged
-WorkingDirectory=/srv/winget/manifests +Group=rewinged 
-ExecStart=/usr/bin/git pull --ff-only+WorkingDirectory=/srv/winget 
 +ExecStart=/usr/local/bin/winget-sync.sh 
 + 
 +# Durcissement 
 +ProtectSystem=strict 
 +ProtectHome=true 
 +PrivateTmp=true 
 +NoNewPrivileges=true 
 +ReadWritePaths=/srv/winget
 </file> </file>
  
 <file ini /etc/systemd/system/winget-sync.timer> <file ini /etc/systemd/system/winget-sync.timer>
 [Unit] [Unit]
-Description=Sync winget manifests every minute+Description=Sync winget repository every minute
  
 [Timer] [Timer]
 OnBootSec=1min OnBootSec=1min
 OnUnitActiveSec=1min OnUnitActiveSec=1min
 +AccuracySec=10s
  
 [Install] [Install]
Ligne 271: Ligne 384:
 systemctl enable --now winget-sync.timer systemctl enable --now winget-sync.timer
 systemctl list-timers winget-sync.timer systemctl list-timers winget-sync.timer
 +journalctl -u winget-sync.service -f
 </code> </code>
  
-Le ''--ff-only'' est délibéré : si l'historique local diverge, le pull échoue +==== 7.4 Propriété des fichiers ==== 
-au lieu de fusionner silencieusement. Une divergence signifie une écriture + 
-locale, ce qui ne doit jamais arriver.+Le clone doit appartenir à ''rewinged'' de bout en bout. Un clone effectué en 
 +root bloque la synchronisation. 
 + 
 +<code bash> 
 +# Preparation, en root 
 +mkdir -p /srv/winget 
 +chown rewinged:rewinged /srv/winget 
 + 
 +# Clone, sous le compte de service 
 +sudo -u rewinged git clone git@forgejo.makeitsimple.be:mis/winget-manifests.git /srv/winget 
 +</code> 
 + 
 +Rattrapage si le clone a déjà été fait en root : 
 + 
 +<code bash> 
 +chown -R rewinged:rewinged /srv/winget 
 +sudo -u rewinged git -C /srv/winget status 
 +</code> 
 + 
 +Git refuse d'opérer sur un dépôt dont le propriétaire diffère de l'utilisateur 
 +courant (''dubious ownership''). C'est le garde-fou qui signale un ''chown'' 
 +oublié. 
 + 
 +Vérification décisive : 
 + 
 +<code bash> 
 +sudo -u rewinged /usr/local/bin/winget-sync.sh 
 +</code> 
 + 
 +Si elle passe, le timer passera aussi. 
 + 
 +==== 7.5 Variante avec redémarrage ==== 
 + 
 +À n'utiliser que si le rechargement natif s'avère insuffisant — par exemple 
 +après une montée de version de rewinged qui modifierait ce comportement. 
 + 
 +Autorisation ciblée dans sudoers : 
 + 
 +<file - /etc/sudoers.d/winget-sync> 
 +rewinged ALL=(root) NOPASSWD: /usr/bin/systemctl try-reload-or-restart rewinged 
 +</file> 
 + 
 +<code bash> 
 +visudo -c -f /etc/sudoers.d/winget-sync 
 +chmod 0440 /etc/sudoers.d/winget-sync 
 +</code> 
 + 
 +Ligne à ajouter dans le bloc conditionnel du script : 
 + 
 +<code bash> 
 +    sudo /usr/bin/systemctl try-reload-or-restart rewinged 
 +</code> 
 + 
 +**Redémarrer uniquement sur changement de HEAD.** À raison de 1440 exécutions 
 +par jour, un redémarrage systématique pollue les journaux et provoque des 
 +micro-coupures pendant les téléchargements.
  
 ===== 8. Format d'un manifest ===== ===== 8. Format d'un manifest =====
Ligne 290: Ligne 459:
   - Architecture: x64   - Architecture: x64
     InstallerType: msi     InstallerType: msi
-    InstallerUrl: https://forgejo.makeitsimple.be/api/packages/mis/generic/agent/1.2.0/mis-agent-1.2.0.msi+    InstallerUrl: https://repo.makeitsimple.be/installers/mis-agent-1.2.0.msi
     InstallerSha256: A1B2C3D4E5F6...     InstallerSha256: A1B2C3D4E5F6...
     Scope: machine     Scope: machine
Ligne 313: Ligne 482:
 sur le PATH. sur le PATH.
  
-Le bloc ''AppsAndFeaturesEntries'' sert à l'adoption des logiciels déjà +===== 9. Convention pour les paquets par client =====
-installés depuis d'anciens paquets custom : y lister leurs ''ProductCode'' +
-pour que winget établisse le lien.+
  
-===== 9. Workflow de publication =====+RustDesk est publié en un paquet par client : le mot de passe d'accès 
 +non-surveillé est préconfiguré dans le MSI et diffère d'un client à l'autre.
  
-  - Construire l'installeur, relever son ''ProductCode'' (Orca ou ''Get-Package'') +==== 9.1 Nommage ==== 
-  - Pousser le binaire dans le package registry Forgejo (6.1)+ 
 +<code> 
 +PackageIdentifier: Rustdesk.<CodeClient> 
 +</code> 
 + 
 +  * manifest : ''manifests/r/Rustdesk/<CodeClient>/<version>/Rustdesk.<CodeClient>.yaml'' 
 +  * installeur : ''installers/rustdesk-<codeclient>.msi'' 
 + 
 +==== 9.2 Conséquence opérationnelle ==== 
 + 
 +À chaque version de RustDesk, autant de MSI à régénérer et de manifests à 
 +republier qu'il y a de clients. À l'échelle de la centaine, la publication 
 +doit être scriptée : un outil qui parcourt la liste des clients, appelle 
 +''Get-MsiCodes'' (section 10.1), calcule le SHA256 et génère le YAML. 
 + 
 +La saisie manuelle n'est pas tenable et expose au risque de coller le GUID 
 +d'une version précédente. 
 + 
 +==== 9.3 Visibilité ==== 
 + 
 +La source n'a pas de notion de périmètre : tout poste ayant enregistré 
 +''mis-repo'' peut énumérer l'ensemble des paquets, donc la liste des codes 
 +clients. Sans conséquence sur l'accès aux machines, mais à connaître. 
 + 
 +==== 9.4 Alternative : paquet unique ==== 
 + 
 +Le paquet par client existe parce que le mot de passe non-surveillé est 
 +préconfiguré dans le MSI. Ce mot de passe peut aussi être posé **après** 
 +installation, ce qui autorise un paquet unique pour tout le parc. 
 + 
 +=== Ce qui n'est pas possible === 
 + 
 +Le mot de passe permanent **ne se propage pas par API ni par stratégie**. 
 +C'est un secret local à la machine. La demande d'en faire un paramètre de 
 +stratégie Pro est ouverte depuis 2023 et n'a pas abouti. 
 + 
 +Ne pas confondre trois objets distincts : 
 + 
 +^ Élément ^ Où il vit ^ Comment il se pose ^ 
 +| Mot de passe permanent | Sur la machine, haché localement | ''RustDesk.exe --password'' en local | 
 +| Mot de passe du carnet | Côté serveur Pro | ''--assign --address_book_password'' | 
 +| Stratégie Pro | Côté serveur Pro | Réglages client, **hors** mot de passe | 
 + 
 +Le premier authentifie les connexions entrantes. Le second est celui que le 
 +client technicien utilise pour remplir automatiquement le champ. 
 + 
 +=== Le flux en deux temps === 
 + 
 +Le secret naît sur la machine, puis remonte vers le serveur. 
 + 
 +<file powershell onboard-rustdesk.ps1> 
 +$rd = "C:\Program Files\RustDesk\RustDesk.exe" 
 + 
 +# 1. Generer un mot de passe propre a CETTE machine 
 +$pw = -join ((48..57)+(65..90)+(97..122) | Get-Random -Count 20 | % {[char]$_}) 
 + 
 +# 2. Le poser localement 
 +& $rd --password $pw 
 + 
 +# 3. Recuperer l'ID attribue 
 +$id = (& $rd --get-id).Trim() 
 + 
 +# 4. Rattacher au carnet du client et y stocker le mot de passe 
 +& $rd --assign ` 
 +    --token $env:RUSTDESK_API_TOKEN ` 
 +    --address_book_name "Client-$ClientCode" ` 
 +    --address_book_alias $env:COMPUTERNAME ` 
 +    --address_book_password $pw 
 + 
 +# 5. Archiver dans Vaultwarden (ID + mot de passe) 
 +</file> 
 + 
 +=== Ce que cela change === 
 + 
 +  * **Un seul manifest** RustDesk pour l'ensemble du parc 
 +  * **Un mot de passe par machine** au lieu d'un par client : la fuite d'un 
 +    secret n'expose plus tout le parc d'un client 
 +  * Plus de régénération de MSI à chaque release 
 +  * Prérequis : Server Pro 1.5.8 et client 1.4.1 pour ''--address_book_alias'' 
 + 
 +=== À valider avant de basculer === 
 + 
 +<WRAP center round important> 
 +Un dysfonctionnement de ''RustDesk.exe --password'' sous Windows a été 
 +signalé en mai 2025 : la commande retourne sans effet. Tester sur un poste 
 +témoin, service arrêté puis démarré, avant de bâtir l'onboarding dessus. 
 +</WRAP> 
 + 
 +Vérifier également que le token API utilisé porte les seules permissions 
 +''Device'' et ''Address Book'', et qu'il n'est pas écrit en clair dans le 
 +script — le passer par variable d'environnement ou le récupérer depuis 
 +Vaultwarden au moment de l'exécution. 
 + 
 +===== 10. Extraction des codes MSI ===== 
 + 
 +Le ''ProductCode'' change à chaque version d'un MSI bien construit ; 
 +l'''UpgradeCode'' reste stable et identifie le produit dans la durée. 
 +Le bloc ''AppsAndFeaturesEntries'' en dépend, notamment pour l'adoption des 
 +logiciels déjà installés depuis d'anciens paquets custom. 
 + 
 +==== 10.1 Depuis un fichier MSI ==== 
 + 
 +À exécuter sur la machine de build (Windows). Lecture seule, sans installation 
 +ni Orca. 
 + 
 +<file powershell Get-MsiCodes.ps1> 
 +function Get-MsiCodes { 
 +    param([string]$Path) 
 +    $installer = New-Object -ComObject WindowsInstaller.Installer 
 +    $db = $installer.GetType().InvokeMember('OpenDatabase','InvokeMethod',$null,$installer,@($Path,0)) 
 +    $props = @{} 
 +    foreach ($p in 'ProductCode','UpgradeCode','ProductVersion','ProductName') { 
 +        $view = $db.GetType().InvokeMember('OpenView','InvokeMethod',$null,$db, 
 +                  @("SELECT Value FROM Property WHERE Property='$p'")) 
 +        $view.GetType().InvokeMember('Execute','InvokeMethod',$null,$view,$null) 
 +        $rec = $view.GetType().InvokeMember('Fetch','InvokeMethod',$null,$view,$null) 
 +        if ($rec) { 
 +            $props[$p] = $rec.GetType().InvokeMember('StringData','GetProperty',$null,$rec,1) 
 +        } 
 +    } 
 +    [PSCustomObject]$props 
 +} 
 + 
 +Get-MsiCodes 'C:\build\mis-agent-1.2.0.msi' 
 +</file> 
 + 
 +Sortie type : 
 + 
 +<code> 
 +ProductName    : MIS Agent 
 +ProductVersion : 1.2.0 
 +ProductCode    : {A1B2C3D4-1111-2222-3333-444455556666} 
 +UpgradeCode    : {99887766-AAAA-BBBB-CCCC-DDDDEEEEFFFF} 
 +</code> 
 + 
 +Le COM ''WindowsInstaller.Installer'' n'existe que sous Windows : ce script ne 
 +tourne pas sur la VM Debian. 
 + 
 +==== 10.2 Depuis un poste où le logiciel est installé ==== 
 + 
 +Pour recenser les GUID en circulation dans le parc : 
 + 
 +<code powershell> 
 +Get-ItemProperty ` 
 +  HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*, ` 
 +  HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | 
 +  Where-Object DisplayName -like '*RustDesk*' | 
 +  Select-Object DisplayName, DisplayVersion, PSChildName 
 +</code> 
 + 
 +''PSChildName'' porte le ProductCode lorsque la clé est un GUID, ce qui est le 
 +cas des MSI. L'UpgradeCode n'y figure pas. 
 + 
 +<WRAP center round important> 
 +Éviter ''Get-CimInstance Win32_Product'' en production : la classe déclenche 
 +une réévaluation de tous les MSI installés, ce qui est lent et peut générer 
 +des événements de réparation. Acceptable ponctuellement sur un poste témoin. 
 +</WRAP> 
 + 
 +==== 10.3 Automatisation ==== 
 + 
 +L'agent d'inventaire devrait remonter le ProductCode de chaque logiciel 
 +installé. Cela donne côté serveur la liste exacte des GUID du parc, donc les 
 +''AppsAndFeaturesEntries'' à renseigner, sans interroger les postes un par un. 
 + 
 +===== 11. Workflow de publication ===== 
 + 
 +  - Construire l'installeur 
 +  - Relever les codes avec ''Get-MsiCodes'' (9.1)
   - Calculer le SHA256   - Calculer le SHA256
-  - Créer le manifest dans une branche, ouvrir une PR +  - Placer le MSI dans ''installers/'' et le manifest dans ''manifests/'' 
-  - Revue, merge sur ''main''+  - Commit sur une branche, ouvrir une PR, revue, merge sur ''main''
   - Le timer applique dans la minute   - Le timer applique dans la minute
   - Valider depuis un poste témoin avant diffusion   - Valider depuis un poste témoin avant diffusion
Ligne 332: Ligne 668:
 </code> </code>
  
-===== 10. Configuration des postes =====+===== 12. Configuration des postes =====
  
-==== 10.1 Enregistrement de la source ====+==== 12.1 Enregistrement de la source ====
  
 <code powershell> <code powershell>
Ligne 345: Ligne 681:
 l'utilisateur courant et reste invisible à l'agent tournant en service. l'utilisateur courant et reste invisible à l'agent tournant en service.
  
-==== 10.2 Winget en contexte SYSTEM ====+==== 12.2 Winget en contexte SYSTEM ====
  
 ''winget'' n'est pas dans le PATH pour le compte SYSTEM. Résoudre le chemin ''winget'' n'est pas dans le PATH pour le compte SYSTEM. Résoudre le chemin
Ligne 359: Ligne 695:
 un prompt bloque le processus indéfiniment. un prompt bloque le processus indéfiniment.
  
-==== 10.3 Adoption du parc existant ====+==== 12.3 Adoption du parc existant ====
  
 Un paquet installé autrement que depuis la source privée n'est pas reconnu Un paquet installé autrement que depuis la source privée n'est pas reconnu
Ligne 370: Ligne 706:
 À valider sur un poste témoin **avant** de dimensionner la campagne. À valider sur un poste témoin **avant** de dimensionner la campagne.
  
-==== 10.4 Compatibilité ====+==== 12.4 Compatibilité ====
  
 ^ Système ^ App Installer ^ ^ Système ^ App Installer ^
Ligne 381: Ligne 717:
 le canal unique ou s'il faut un second canal. le canal unique ou s'il faut un second canal.
  
-===== 11. Surveillance Wazuh =====+===== 13. Surveillance Wazuh =====
  
-==== 11.1 FIM ====+Trois sources distinctes : l'intégrité des fichiers, le journal applicatif de 
 +rewinged, et l'état des services.
  
-<file xml ossec.conf> +==== 13.1 Configuration de l'agent ==== 
-<syscheck> + 
-  <directories check_all="yes" realtime="yes" report_changes="yes"> +<file xml /var/ossec/etc/ossec.conf> 
-    /srv/winget/manifests +<ossec_config> 
-  </directories> + 
-  <directories check_all="yes" realtime="yes"> +  <!-- Integrite des fichiers --> 
-    /srv/winget/installers +  <syscheck> 
-  </directories> +    <disabled>no</disabled> 
-  <directories check_all="yes" realtime="yes"> +    <frequency>3600</frequency> 
-    /usr/local/bin + 
-  </directories> +    <!-- Manifests : decident de ce que le parc installe --> 
-</syscheck>+    <directories check_all="yes" realtime="yes" report_changes="yes" 
 +                 tags="winget,manifests">/srv/winget/manifests</directories> 
 + 
 +    <!-- Installeurs : les binaires servis aux postes --> 
 +    <directories check_all="yes" realtime="yes" 
 +                 tags="winget,installers">/srv/winget/installers</directories> 
 + 
 +    <!-- Binaire rewinged et script de synchronisation --> 
 +    <directories check_all="yes" realtime="yes" 
 +                 tags="winget,binaries">/usr/local/bin</directories> 
 + 
 +    <!-- Unites systemd du service --> 
 +    <directories check_all="yes" realtime="yes" 
 +                 tags="winget,systemd">/etc/systemd/system</directories> 
 + 
 +    <!-- Bruit git interne --> 
 +    <ignore type="sregex">^/srv/winget/\.git/</ignore> 
 +  </syscheck> 
 + 
 +  <!-- Journal applicatif rewinged, deja en JSON --> 
 +  <localfile> 
 +    <log_format>json</log_format> 
 +    <location>/var/log/rewinged/rewinged.log</location> 
 +  </localfile> 
 + 
 +  <!-- Etat des services, toutes les 5 minutes --> 
 +  <localfile> 
 +    <log_format>full_command</log_format> 
 +    <command>systemctl is-active rewinged winget-sync.timer | tr '\n' ' '</command> 
 +    <alias>winget-services</alias> 
 +    <frequency>300</frequency> 
 +  </localfile> 
 + 
 +</ossec_config>
 </file> </file>
  
-==== 11.2 Réduction du bruit ====+Le journal de ''winget-sync'' passe par ''logger'', donc par syslog, déjà 
 +collecté par la configuration Wazuh par défaut.
  
-La FIM se déclenche à chaque ''git pull'' légitime. Pour distinguer le +==== 13.2 Règles personnalisées ====
-légitime de l'anormal :+
  
-  * signer les commits en GPG et vérifier la signature dans le service de +<file xml /var/ossec/etc/rules/local_rules.xml> 
-    sync avant application — un manifest modifié sans commit signé devient +<group name="winget,rewinged,">
-    alors une alerte non ambiguë +
-  * à défaut, corréler avec les logs de ''winget-sync.service''+
  
-==== 11.3 Alertes complémentaires ====+  <rule id="100600" level="0"> 
 +    <decoded_as>json</decoded_as> 
 +    <location>/var/log/rewinged/rewinged.log</location> 
 +    <description>rewinged: evenement applicatif</description> 
 +  </rule>
  
-  * arrêt du service ''rewinged'' +  <rule id="100601" level="7"> 
-  * échecs répétés de ''winget-sync.timer'' — un dépôt figé est un mode de +    <if_sid>100600</if_sid> 
-    panne silencieux +    <field name="level">error</field> 
-  * volumétrie anormale de téléchargements sur ''/files/''+    <description>rewinged: erreur applicative</description> 
 +  </rule>
  
-===== 12. Points ouverts =====+  <rule id="100602" level="10"> 
 +    <if_sid>100601</if_sid> 
 +    <field name="message">ingestManifests</field> 
 +    <description>rewinged: echec ingestion des manifests - $(message)</description> 
 +  </rule> 
 + 
 +  <rule id="100610" level="3"> 
 +    <program_name>winget-sync</program_name> 
 +    <match>Publication verifiee</match> 
 +    <description>winget: publication synchronisee et verifiee</description> 
 +  </rule> 
 + 
 +  <rule id="100611" level="14"> 
 +    <program_name>winget-sync</program_name> 
 +    <match>SIGNATURE INVALIDE</match> 
 +    <description>winget: signature invalide - synchronisation annulee</description> 
 +    <group>attack,</group> 
 +  </rule> 
 + 
 +  <rule id="100612" level="10" frequency="5" timeframe="1800"> 
 +    <if_matched_sid>100601</if_matched_sid> 
 +    <description>winget: erreurs applicatives repetees</description> 
 +  </rule> 
 + 
 +  <rule id="100620" level="3"> 
 +    <if_group>syscheck</if_group> 
 +    <field name="file">/srv/winget/manifests/</field> 
 +    <description>winget: manifest modifie - $(file)</description> 
 +  </rule> 
 + 
 +  <rule id="100621" level="12"> 
 +    <if_group>syscheck</if_group> 
 +    <field name="file">/srv/winget/installers/</field> 
 +    <description>winget: installeur modifie - $(file)</description> 
 +    <group>attack,</group> 
 +  </rule> 
 + 
 +  <rule id="100622" level="14"> 
 +    <if_group>syscheck</if_group> 
 +    <field name="file">/usr/local/bin/rewinged</field> 
 +    <description>winget: binaire rewinged modifie - $(file)</description> 
 +    <group>attack,</group> 
 +  </rule> 
 + 
 +  <rule id="100630" level="0"> 
 +    <location>winget-services</location> 
 +    <description>winget: releve d'etat des services</description> 
 +  </rule> 
 + 
 +  <rule id="100631" level="10"> 
 +    <if_sid>100630</if_sid> 
 +    <regex>inactive|failed|unknown</regex> 
 +    <description>winget: service rewinged ou timer arrete</description> 
 +  </rule> 
 + 
 +</group> 
 +</file> 
 + 
 +<WRAP center round important> 
 +Les identifiants 100600–100631 sont donnés à titre indicatif. Les adapter à 
 +la numérotation des règles personnalisées déjà en place. 
 +</WRAP> 
 + 
 +==== 13.3 Traiter le bruit FIM ==== 
 + 
 +La FIM se déclenche à chaque publication légitime. Wazuh ne sait pas exprimer 
 +nativement « alerter sauf si précédé de tel événement », il faut donc déplacer 
 +le contrôle plutôt que tenter de corréler. 
 + 
 +**Approche retenue** : la signature GPG des commits est le contrôle réel. 
 + 
 +  - Les commits de publication sont signés ; la clé publique du signataire est 
 +    dans le trousseau du compte ''rewinged'' 
 +  - ''winget-sync.sh'' vérifie la signature et annule la synchronisation en cas 
 +    d'échec (section 7.2) 
 +  - La FIM sur ''manifests'' reste **informative** (niveau 3) : elle documente, 
 +    elle n'alerte pas 
 +  - L'alerte forte porte sur la signature invalide (100611, niveau 14) 
 + 
 +Une modification de manifest effectuée directement sur le serveur ne passe pas 
 +par un commit, donc ne déclenche pas 100611 — mais elle sera écrasée au 
 +prochain ''merge --ff-only'', et la FIM en garde la trace. 
 + 
 +Les installeurs et les binaires, eux, ne changent pas hors publication : leurs 
 +règles restent à niveau élevé. 
 + 
 +==== 13.4 Mise en place de la signature ==== 
 + 
 +Côté poste de publication : 
 + 
 +<code bash> 
 +git config --global user.signingkey <ID_CLE> 
 +git config --global commit.gpgsign true 
 +gpg --export --armor <ID_CLE> > mis-publication.pub 
 +</code> 
 + 
 +Côté serveur rewinged : 
 + 
 +<code bash> 
 +sudo -u rewinged gpg --import mis-publication.pub 
 +sudo -u rewinged gpg --edit-key <ID_CLE> trust   # niveau ultimate 
 +sudo -u rewinged git -C /srv/winget verify-commit HEAD 
 +</code> 
 + 
 +Idéalement la clé de signature réside sur une YubiKey, cohérent avec 
 +l'autorité de certification SSH déjà en place. 
 + 
 +==== 13.5 Ce qui reste non couvert ==== 
 + 
 +  * **Publication légitime mais erronée** — un mauvais ''PackageIdentifier'' 
 +    ou un SHA256 incorrect signé de bonne foi passe toutes les vérifications. 
 +    Le garde-fou est la revue de la PR, pas la détection. 
 +  * **Compromission du poste de publication** — si la clé de signature est 
 +    utilisable par un attaquant, la chaîne entière est valide de son point de 
 +    vue. D'où la YubiKey. 
 +  * **Saturation disque** — à surveiller côté Zabbix plutôt que Wazuh, la 
 +    croissance LFS étant progressive et prévisible. 
 + 
 +===== 14. Points ouverts =====
  
 ^ Sujet ^ Action ^ ^ Sujet ^ Action ^
-| Flags rewinged | Confirmer le nom du flag de répertoire et le rechargement à chaud via ''--help'' |+| Auto-internalisation | Valider le rapatriement des installeurs tiers depuis les URL éditeur |
 | Bug de matching | Tester sur un poste témoin avec un logiciel préinstallé | | Bug de matching | Tester sur un poste témoin avec un logiciel préinstallé |
 | Server 2019/2022 | Inventorier la proportion dans le parc | | Server 2019/2022 | Inventorier la proportion dans le parc |
 | Upgrade RustDesk | Vérifier que la mise à jour préserve ''RustDesk2.toml'' et le mot de passe permanent | | Upgrade RustDesk | Vérifier que la mise à jour préserve ''RustDesk2.toml'' et le mot de passe permanent |
-| Rétention | Définir une politique de purge des anciennes versions d'installeurs | +| ''--password'' sous Windows | Tester le réglage du mot de passe en ligne de commande (9.4) avant d'envisager le paquet unique | 
-| Forgejo anonyme | Vérifier que les paquets génériques sont téléchargeables sans token (6.1) | +| Rétention LFS | Définir une politique de purge des anciennes versions d'installeurs | 
-| Internalisation | Relever le chemin d'URL servi par rewinged si Forgejo n'est pas lisible en anonyme |+| Quota Forgejo | Dimensionner le stockage LFS et surveiller sa croissance |
  
-===== 13. VersionID ===== +===== 15. Références =====
- +
-<code powershell> +
-function Get-MsiCodes { +
-    param([string]$Path) +
-    $installer = New-Object -ComObject WindowsInstaller.Installer +
-    $db = $installer.GetType().InvokeMember('OpenDatabase','InvokeMethod',$null,$installer,@($Path,0)) +
-    $props = @{} +
-    foreach ($p in 'ProductCode','UpgradeCode','ProductVersion','ProductName') { +
-        $view = $db.GetType().InvokeMember('OpenView','InvokeMethod',$null,$db, +
-                  @("SELECT Value FROM Property WHERE Property='$p'")) +
-        $view.GetType().InvokeMember('Execute','InvokeMethod',$null,$view,$null) +
-        $rec = $view.GetType().InvokeMember('Fetch','InvokeMethod',$null,$view,$null) +
-        if ($rec) { +
-            $props[$p] = $rec.GetType().InvokeMember('StringData','GetProperty',$null,$rec,1) +
-        } +
-    } +
-    [PSCustomObject]$props +
-} +
- +
-Get-MsiCodes 'C:\build\mis-agent-1.2.0.msi' +
-</code> +
-===== 14. Références =====+
  
   * [[https://github.com/jantari/rewinged|rewinged — dépôt GitHub]]   * [[https://github.com/jantari/rewinged|rewinged — dépôt GitHub]]
   * [[https://learn.microsoft.com/windows/package-manager/|Documentation Windows Package Manager]]   * [[https://learn.microsoft.com/windows/package-manager/|Documentation Windows Package Manager]]
   * [[https://rustdesk.com/docs/en/self-host/rustdesk-server-pro/console/|RustDesk Server Pro — console et tokens API]]   * [[https://rustdesk.com/docs/en/self-host/rustdesk-server-pro/console/|RustDesk Server Pro — console et tokens API]]
linux/rewinged.1789196740.txt.gz · Dernière modification : de root