SSH comme technique de remplacement de sudo
(whynothugo.nl)sudoetdoasreposent sur des binaires setuid et sur l’élévation de privilèges ; il s’agit ici d’une expérimentation qui confie l’exécution de commandes root à unsshdlocal lié à une socket de domaine Unix- L’objectif est de n’autoriser que les utilisateurs approuvés à exécuter des commandes root, sans laisser à toute leur session la capacité d’élever ses privilèges
- L’implémentation se compose d’un fichier de clé SSH réservé à root, de
/run/sshd/sshd.sockavec accès restreint, et d’une instance séparée desshdavec les optionsAuthorizedKeysFileetPermitRootLogin=yes - Comme
sshn’a pas d’option pour recevoir directement une socket existante, l’approche a d’abord utiliséProxyCommandetsocat, puis ProxyUseFdpass avec un court script Python pour transmettre le descripteur de fichier de la socket - Cette méthode fonctionne et s’appuie surtout sur OpenSSH pour le traitement de la sécurité, mais pour un usage quotidien il vaut mieux transformer
passfd.pyen petit exécutable et encapsuler la commandesshentière dans un wrapper
Les contraintes structurelles de sudo et doas
sudoetdoass’appuient sur des binairessetuidet sur l’élévation de privilèges pour exécuter des commandes en root- Cette conception présente plusieurs limites
- toute la session utilisateur doit conserver la capacité d’effectuer une élévation de privilèges
- elle ne fonctionne pas quand toute la session utilisateur s’exécute dans un espace de noms utilisateur restreint
- les binaires
setuidimposent des contraintes à la configuration de sécurité de l’ensemble du système
s6-sudodest une alternative qui sépare le programme entre un serveur privilégié et un client non privilégié
Objectif de l’expérimentation
- Utiliser
sshen local pour remplir un rôle similaire àsudo, sans exposer l’instancesshdconcernée au réseau - Deux conditions sont au cœur de l’approche
- seuls les utilisateurs autorisés peuvent exécuter des commandes en root
- ne pas utiliser d’élévation de privilèges
Configuration de sshd pour une connexion root locale
- Créer une clé SSH dédiée, utilisée uniquement pour l’authentification root, et la stocker dans
/root/.ssh/local_keysplutôt que dans l’authorized_keyshabituel - Lier une instance séparée de
sshdà une socket de domaine Unix et restreindre les permissions de/run/sshd/pour empêcher tout utilisateur non autorisé d’accéder à la socket - L’exemple d’exécution consiste à créer
/run/sshd/sshd.sockavecs6-ipcserver, puis à lancersshdavec les options suivantesAuthorizedKeysFile=/root/.ssh/local_keysPermitRootLogin=yes
- Il n’est pas nécessaire de modifier
/etc/ssh/sshd_config- car on ne veut pas autoriser la connexion root sur le
sshdexistant lié au réseau - la configuration existante conserve donc
PermitRootLogin no
- car on ne veut pas autoriser la connexion root sur le
Verrouillage du compte root et gestion de la connexion par mot de passe
- Le compte root était verrouillé de façon à empêcher toute connexion, quel qu’en soit le moyen, en préfixant le hash du mot de passe avec
! sshdinterprète ce préfixe!comme un verrouillage du compte et n’autorise donc pas la connexion root- La valeur du mot de passe root dans
/etc/passwdest modifiée pour remplacer!par*sshdn’interprète pas*comme une valeur spéciale de verrouillage*ne correspond à aucun hash de mot de passe, donc l’authentification par mot de passe reste de fait désactivée
- En plus de cela,
PasswordAuthentication noest défini danssshd_config, et il est prudent de désactiver l’authentification par mot de passe danssshd
Se connecter à une socket Unix avec ssh
sshdpossède un drapeau-ipermettant de lui transmettre une socket existante, maissshn’a pas de drapeau équivalent- Au départ, l’approche utilisait
socatviaProxyCommandpour se connecter à/run/sshd/sshd.sock - La commande de connexion se compose des éléments suivants
ProxyCommand='socat STDIO UNIX-CONNECT:/run/sshd/sshd.sock'- la clé root dédiée est indiquée avec
-i .ssh/root-key.pub - connexion à
root@root - déplacement vers le répertoire courant, puis lancement d’un shell de connexion
- La clé SSH utilisée était une clé liée au matériel, ce qui exigeait de toucher le périphérique physique pour approuver la connexion
- Un
ssh-agentqui ne publie la clé qu’après approbation explicite, commehissh-agent, peut aussi être une alternative
La surcharge de socat et ProxyUseFdpass
socatlit toute l’entrée desshavant de l’écrire vers la socket, ce qui duplique en pratique la surcharge de connexionProxyUseFdpasspermet à une commande d’envoyer le descripteur de fichier de la socket àsshviastdout, puis àsshd’utiliser cette socket pour la connexion- En s’appuyant sur un exemple d’utilisation de ProxyUseFdpass dans OpenSSH datant de 2016, un script Python
passfd.pya été écrit- connexion à la socket de domaine Unix
/run/sshd/sshd.sock - transmission du descripteur de fichier avec
sendmsgetSCM_RIGHTS
- connexion à la socket de domaine Unix
- La commande de connexion utilise ensuite
passfd.pydansProxyCommandet ajouteProxyUseFdpass=yes nc -FU /run/sshd/sshd.socksemble aussi envisageable, mais la page de manuel précise que-Fne peut pas être utilisé avec-U
Conclusion et forme adaptée à un usage réel
- Cette technique fonctionne et confie surtout à OpenSSH les traitements de sécurité sensibles
- OpenSSH bénéficie d’un bon historique et permet d’utiliser plusieurs méthodes d’authentification, notamment des clés SSH matérielles
- La mise en place sur un nouvel hôte ne comporte pas d’étapes particulièrement complexes, et la commande
ipcserverpeut être lancée via le gestionnaire de services du système passfd.pyressemble davantage à un hack rapide pour faire avancer l’expérimentation- Pour un usage quotidien, il est plus approprié de créer un petit exécutable remplissant le même rôle et de le placer dans
/usr/local/bin, puis d’encapsuler aussi la commandesshentière dans un petit wrapper
1 commentaires
Commentaires sur Hacker News
La principale objection à cette approche est l’augmentation de la complexité. Au lieu d’un unique binaire
suidqui lit un fichier de configuration et appelleexec(), on se retrouve avec un binaire exécuté en root qui écoute sur un socket UNIX, plus un autre binaire qui dialogue avec ce socket, et tous deux doivent en plus gérer de la cryptographie asymétriqueSi l’argument central contre sudo/doas est qu’« il existe un binaire
suidaccessible à tous les utilisateurs, et qu’en cas de bug il peut être exploité pour une élévation de privilèges », alors on peut faire cecichgrp wheel /usr/bin/sudochmod o-rwx /usr/bin/sudoEn mettant les utilisateurs sudo dans le groupe
wheel, seuls les utilisateurs autorisés à utiliser sudo peuvent lire les octets du fichier sur le disque et l’exécuter. En matière de contrôle d’accès, la sécurité est presque la même que dans l’approche sshd, où seuls les utilisateurswheelpeuvent accéder au socket UNIX, mais avec une complexité bien moindreEn outre, l’approche sshd ne permet pas, comme sudo, de limiter l’accès root à des commandes spécifiques ; donc même s’il existe un bug permettant de contourner les restrictions de commandes de sudo, cela n’accorde pas plus de privilèges que l’approche sshd
Si vous craignez qu’un gestionnaire de paquets casse les permissions de
/usr/bin/sudo, vous pouvez les corriger périodiquement via cron, ou supprimer complètement sudo et l’installer manuellement ailleurs à partir des sources. Bien sûr, dans ce cas il faut aussi assurer soi-même la maintenance et les mises à jour/etc. Qu’elles viennent d’une installation/mise à niveau logicielle ou d’une intervention humaine, tout est enregistré, et c’est utile lors d’une mise à niveau vers une nouvelle version : on peut générer un patch des modifications locales, l’appliquer à une installation propre, puis vérifier les conflits via une fusion à 3 voieshttps://etckeeper.branchable.com/
wheelafin que seuls ceux qui peuvent utiliser sudo puissent lire et exécuter le fichier paraît tout à fait raisonnable. Je me demande pourquoi ce n’est pas la configuration par défaut partout/usr/bin/sudoimmutable ? Cela pourrait aider à empêcher qu’un gestionnaire de paquets y toucheN’est-ce pas exactement ce que fait systemd run0 aujourd’hui ? systemd a un nouvel outil appelé
run0, mais en réalité ce n’est pas un outil entièrement nouveau : c’est une façon de faire agir l’anciensystemd-runcomme un remplaçant de sudo lorsqu’on l’invoque via un lien symbolique nommérun0La différence essentielle est que ce n’est pas réellement du
SUID. Il demande au gestionnaire de services d’exécuter une commande ou un shell avec l’UID de l’utilisateur cible, alloue un nouveau PTY, puis transfère les données entre le TTY d’origine et ce PTYAutrement dit, la commande cible n’hérite pas du contexte du client ; elle s’exécute dans un contexte isolé nouvellement forké depuis PID 1.
$TERMest transmis, mais c’est une exception explicite, donc on est plus proche d’une liste d’autorisations que d’une liste de blocageÀ bien des égards,
run0peut être vu comme plus proche du fonctionnement desshque de celui desudohttps://mastodon.social/@pid_eins/112353324518585654
Est-ce qu’il y a quelque chose qui m’échappe ? Je ne vois pas en quoi se connecter en SSH en root serait plus sûr que d’utiliser sudo. On m’a toujours appris à ne jamais l’autoriser, donc je ne sais même pas vraiment à quel point c’est dangereux en pratique
Ici, il semble qu’il y ait une réflexion pour bloquer les utilisateurs distants, donc je ne parle pas de cet aspect de sécurité
Cela a peut-être un lien avec la troisième limite de sudo, mais au moins par rapport au premier point je n’y vois aucun avantage
Je comprends l’idée de l’expérimentation, mais cela me semble plus vulnérable que sudo, pas moins. Un proxy socket ouvert paraît exposé aux attaques de l’homme du milieu
Cela dit, j’ai quand même appris quelques techniques faisables avec de vieux outils, et j’ai apprécié qu’on montre quelque chose de nouveau
sudoest suid root et constitue un binaire privilégié directement exposé à des utilisateurs non fiables. Si quelque chose tourne mal dans sudo, l’ensemble de l’environnement utilisateur devient une surface d’attaque exploitableL’approche ssh n’expose pas de binaire
suidet s’appuie sur la couche réseau de ssh. Elle n’est donc pas moins sûre qu’un accès à ssh via le réseau, ce qui est généralement considéré comme assez sûrsudo bash, ce qui améliore l’auditabilitéCe démon peut lui aussi avoir des erreurs de configuration ou des vulnérabilités, et il réduit plusieurs couches d’autorisation basées sur les utilisateurs à un unique niveau root
Et pourtant cela semble considéré comme plus sûr. D’un point de vue de sécurité rationnel, on ne peut pas dire que ce soit plus sûr ; c’est simplement une approche différente
En pratique, se connecter en SSH directement en root est même strictement plus sûr que de se connecter d’abord avec un utilisateur distant puis d’utiliser
sudoSi
user@homese connecte en ssh àroot@server, alorsroot@servern’est compromis que siuser@homel’estEn revanche, si
user@homese connecte en ssh àuser@serverpuis devientroot@servervia sudo, il suffit queuser@homeouuser@serversoit compromis pour que root le soit aussi. En particulier, suruser@server, d’autres logiciels comme des démons ou des tâches cron s’exécutent souventIl ne faut pas offrir une élévation de privilèges root gratuite à quelqu’un qui a réussi à infecter par ce biais, ni faciliter ensuite les mouvements latéraux, fréquents à cause de la réutilisation des mots de passe
Bien sûr, cela ne s’applique pas si sudo est utilisé uniquement en mode liste blanche de commandes autorisées, et sans mot de passe ni identifiants entièrement accessibles depuis l’hôte distant
user@serverQue se passe-t-il si
sshne démarre pas au boot ? Dans une configuration classique, il me semble qu’il démarre en dépendant du réseau. Donc on ne peut pas non plus se connecter depuis une console failsafeJe ne vois pas vraiment ce qu’on y gagne par rapport à
sudoousu. Au lieu d’éviter un binaire setuid, on se retrouve à exécuter un service réseau avec les privilèges root, même s’il n’écoute que sur un socketEt si le SSD lâche ? Dans ce cas non plus, on ne peut pas se connecter à la console failsafe
En 30 ans de Linux, j’ai vu des disques durs mourir bien plus souvent qu’un démon
sshdne pas démarrer ; en proportion, c’est du niveau division par zéroSi le démon
sshdde l’OS cesse de démarrer aléatoirement, j’y verrais un signe qu’il est temps de passer à un OS plus fiableCe qu’on gagne par rapport à
sudoousu, c’est que les exploits d’élévation de privilèges locale deviennent bien plus difficilesttyaffiché sur l’écran local ou via un KVM distant, ainsi que les périphériquesttyS*sur port série et IPMI SoL, n’utilise nisudonisuCes consoles utilisent des programmes comme
gettyou un gestionnaire de fenêtres, qui sont des programmes non-suid lancés en rootIl est donc logique de configurer un mot de passe root pour la connexion console
sudopour les journaux d’audit. Aujourd’hui, je gère rarement des machines multi-utilisateurs ou multi-services, et la plupart de ce qui est multi-tenant est sur k8s, donc on peut utiliser un endpointkubectlau lieu desshSi quelqu’un peut se connecter, je lui laisse la possibilité de faire un setuid root. Sur une machine k8s, ce sera l’équipe d’infrastructure plateforme ; l’accès aux services au-dessus passe via le fournisseur d’autorisations k8s
Du point de vue de l’équipe d’infrastructure plateforme, si on n’a besoin que de métriques et de logs, ils sont déjà hors de la machine ; et s’il faut déclencher une tâche ou un workflow, on peut utiliser un pipeline
Malgré tout, si quelqu’un se connecte et effectue des opérations root, je veux garder une trace d’audit
Je ne vois pas de machine chez moi où quelqu’un ayant le droit de se connecter n’aurait pas aussi tous les privilèges root
Bien sûr, je comprends les cas où un service fait du setuid, mais pour les services, c’est généralement systemd qui fait le setuid non pas pour élever les privilèges, mais pour les réduire
systems.unit=emergency.target,init=/bin/bash,rd.break=pre-pivot, ou démarrer sur un environnement live CD. Toutes les options classiques de récupération d’urgence continuent de fonctionnerEt dans une urgence moins grave, je ne vois pas non plus pourquoi cette instance de
sshddevrait être liée au réseauC’est un peu dommage de ne pas inclure tous les inconvénients de cette approche.
sudopermet de contrôler quels groupes peuvent exécuter quelles commandes, quels arguments ces commandes peuvent accepter, si elles peuvent lancer des sous-shells, etc.Cette méthode perd beaucoup de cette granularité de contrôle et repose sur des clés de confiance plus difficiles à administrer qu’un fichier
sudoersSi vous voulez voir les capacités impressionnantes de
sudo, je recommande vraiment le livre Sudo MasterySSHpeut aussi faire une partie de cela avec ForceCommand, mais je suis d’accord sur le fait que ce n’est ni aussi flexible ni aussi précisC’est une idée proche de
run0de Systemd : https://news.itsfoss.com/systemd-run0/run0n’est pas un bricolage maison. Il a été audité et sera probablement meilleur qu’une solution faite soi-mêmeL’un des problèmes de
ssh, c’est que la création de processus ne fait pas partie du protocole. Et comme c’est un protocole distant, on ne peut pas transmettre de ressources locales au processus enfantIl est donc impossible de passer un tableau d’arguments délimités par des caractères nuls, des descripteurs de fichiers supplémentaires, ou de spécifier un exécutable
À la place, on transmet une chaîne unique au shell configuré sur le serveur. Cela impose des échappements shell et suppose aussi de savoir quel shell tourne côté serveur
Pour faire de SSH un vrai remplaçant de
sudo, il faudrait une extension offrant quelque chose de proche deposix_spawnhttps://bugzilla.mindrot.org/show_bug.cgi?id=2283
Entièrement d’accord. Je fais quelque chose de similaire moi aussi, et je l’avais déjà expliqué dans un commentaire HN il y a quelque temps
Ma méthode est un peu différente. J’utilise une machine dédiée comme console SSH physique, sur un LAN privé séparé des autres machines de la maison. Ce n’est pas un switch administrable mais un switch ordinaire, avec des câbles Ethernet et sans trunk
La connexion se fait uniquement en SSH, et j’y ai branché une Yubikey
Le PC de bureau a son propre pare-feu et n’autorise le trafic SSH que depuis l’adresse IP/MAC de cette console SSH. C’est uniquement sur leur LAN privé commun ; sur les autres LAN physiques, le bureau peut accéder à Internet
Le démon
sshdn’autorise que la connexion par paire de clés publique/privée et bloque la connexion par mot de passeQuand j’ai besoin de root, je démarre la « console SSH ». C’est une machine presque vide, donc elle démarre très vite. Je me connecte, je rappelle la ligne
ssh root@...avec la flèche du haut, j’appuie sur Entrée, puis j’active la YubikeyCette console SSH et son clavier sont sur le bureau, toujours à portée de main
À chacun de décider si
iptables/nftables+sshdsur un LAN privé, en plus physiquement séparé d’un autre LAN privé, est plus ou moins sûr qu’un binairesudoousuSi on me demande « pourquoi », je répondrai : « parce que je peux ». Je l’ai configuré il y a si longtemps que je ne me souviens même plus quand. J’ai probablement commencé à jouer avec cette idée il y a environ deux ans, et je l’utilise depuis. Je n’ai eu absolument aucun problème
C’est une solution élégante à ce problème. Pas besoin de traiter les utilisateurs comme des enfants, mais il faut quand même éviter les erreurs potentielles avec des valeurs par défaut raisonnables
Si on a besoin de root, il suffit de se connecter en root sur la console, donc je dirais qu’on n’a même pas besoin de
su. Cette méthode se rapproche autant que possible d’une connexion root sur un tty consolePremièrement, contrairement à
sudo, tous les utilisateurs doivent connaître le mot de passe rootDeuxièmement, si tout le monde se connecte directement en root, il n’y a aucun moyen d’auditer qui s’est connecté ni ce qu’il a fait ensuite
J'avais essayé quelque chose de similaire il y a 10 ans. Il n'y avait pas la partie socket UNIX, et j'avais lancé un
sshdséparé n'écoutant que sur localhost, sans avoir besoin de gérerSCM_RIGHTSCela n'a donné ni de très bons ni de très mauvais résultats, et j'ai simplement perdu l'intérêt, donc je n'ai pas reporté la configuration sur la machine suivante