Recherche de site Web

Comment lier un conteneur rootless à un port privilégié sous Linux


L’une des principales innovations introduites par Podman était la possibilité d’exécuter des conteneurs sans racines. Du point de vue de la sécurité, il s’agissait d’une grande amélioration, car un conteneur potentiellement compromis s’exécutant en tant que root représente une menace pour la sécurité du système hôte. Afin d’obtenir un comportement similaire, les versions récentes de Docker prennent en charge l’exécution du démon docker dans le contexte utilisateur. L’exécution de conteneurs non privilégiés, bien que plus sûrs, présente également des inconvénients, comme l’impossibilité de se lier à des ports hôtes privilégiés.

Dans ce tutoriel, nous apprenons comment autoriser un conteneur Docker/Podman sans racine à se lier à un port hôte privilégié sous Linux.

Dans ce tutoriel, vous allez apprendre :

  • Comment rediriger un port privilégié vers un port non privilégié en créant une règle de pare-feu ou en utilisant redir
  • Comment autoriser un conteneur non privilégié à se lier à un port privilégié en définissant la fonctionnalité CAP_NET_BIND_SERVICE
  • Comment modifier la plage de ports privilégiés

Introduction

docker run -p 80:80 httpd:latest


error while calling PortManager.AddPort(): cannot expose privileged port 80

Redirection d’un port privilégié vers un port non privilégié à l’aide d’une règle de pare-feu

La première solution possible consiste à rediriger le trafic du port privilégié que l’on souhaite utiliser, vers un port non privilégié de notre choix, que l’on va ensuite mapper sur le port exposé par le conteneur.

Pour rediriger un port vers un autre, nous pouvons créer une règle de pare-feu. La commande exacte que nous devons exécuter dépend du gestionnaire de pare-feu que nous utilisons. Le composant du noyau Linux qui fournit la fonctionnalité de manipulation de paquets est « netfilter ». Dans les distributions Linux modernes, pour « parler » avec ce composant depuis l’espace utilisateur, nous utilisons le framework « nftables ». Cependant, comme la création directe de règles nftables peut être une tâche fastidieuse, la plupart du temps, nous interagissons avec des gestionnaires de configuration de pare-feu de niveau supérieur. Un exemple d’un tel utilitaire est firewalld.

Création de la règle à l’aide de firewalld

Si vous avez lu notre article d’introduction sur firewalld, vous savez déjà qu’il est géré via l’utilitaire firewall-cmd qui n’a pas de moyen natif de spécifier une règle de redirection de port. Pour ce faire, nous devons nous rabattre sur une règle « directe », qui nous permet d’interagir avec le pare-feu à un niveau inférieur. Pour définir une règle « directe », nous lançons l’utilitaire avec l’option --direct. Supposons que nous voulions rediriger le trafic du port 80 vers le port 8080 sur localhost, nous exécuterions :

sudo firewall-cmd --direct --add-rule ipv4 nat OUTPUT 0 -p tcp --dport=80 -o lo -j REDIRECT --to-port=8080

Deux choses à noter : la première est que la commande ci-dessus ajoute la règle à la zone firewalld « par défaut », puisque nous n’en avons pas explicitement spécifié une (les zones sont des abstractions liées au firewalld. Encore une fois, jetez un coup d’œil à notre tutoriel firewalld si vous voulez en savoir plus à leur sujet).  La seconde est que la règle n’est pas persistante et ne survivrait pas à un redémarrage ou à un redémarrage du service firewalld. Pour le rendre permanent, nous devons utiliser l’option --permanent, et redémarrer le pare-feu en exécutant :

sudo firewall-cmd --reload

Création de la règle à l’aide des utilitaires iptables ou nft

La syntaxe utilisée pour spécifier les règles directes de firewalld, comme vous pouvez le voir ci-dessus, ressemble à celle utilisée par iptables. Si vous (comme moi), le préférez, il est possible de définir une règle en utilisant directement les utilitaires iptables/ip6tables. Puisque, dans les distributions Linux modernes, nftables a remplacé le framework iptables, ces utilitaires ne sont plus que des liens vers iptables-nft, un wrapper qui nous permet de continuer à utiliser l’ancienne syntaxe :

sudo iptables -t nat -A OUTPUT -o lo -p tcp --dport=80 -j REDIRECT --to-port=8080

Enfin, pour définir la même redirection en utilisant la syntaxe native de nftables, nous pouvons utiliser l’utilitaire nft et exécuter la commande suivante :

sudo nft 'add rule ip nat OUTPUT oifname "lo" tcp dport 80 counter redirect to :8080'

Les règles établies avec les utilitaires iptables ou nft de niveau inférieur ne sont pas non plus persistantes. Pour les renforcer à chaque démarrage, nous devons vider l’ensemble de règles actuel et utiliser un outil qui les recharge automatiquement. La stratégie précise varie en fonction de la distribution que nous utilisons, et nous ne l’explorerons pas ici.

Redirection d’un port à l’aide de redir

Une alternative à la création d’une règle de pare-feu consiste à utiliser un outil dédié comme redir, qui est open source, et disponible dans les dépôts par défaut de toutes les principales distributions Linux. Sur Fedora, on peut l’installer avec la commande suivante :

sudo dnf install redir

Sur Debian et les distributions basées sur Debian, à la place, nous pouvons utiliser :

sudo apt install redir

Redir est capable de rediriger les connexions TCP provenant d’un port local vers une combinaison spécifique <adresse>:p ort. La partie <adresse> est facultative : si elle est omise, l’adresse 0.0.0.0 (méta) est utilisée (elle représente toutes les adresses IPv4 de la machine). Pour rediriger le port 80 vers le port 8080 à l’aide de redir, nous exécuterions :

sudo redir :80 127.0.0.1:8080

Les redirections définies avec redir ne sont pas persistantes ; Pour qu’ils soient appliqués à chaque démarrage du système, nous devons créer un service qui lance la commande avec les options -n et -s. Nous utilisons le premier pour exécuter l’application au premier plan, et le second pour rediriger les journaux vers syslog. De nos jours, presque toutes les distributions Linux utilisent Systemd comme système d’initialisation et gestionnaire de services. Voici un exemple très minime de ce à quoi pourrait ressembler une unité de service systemd qui lance redir au démarrage :

[Unit]
Description=Redirect tcp port 80 to 8080 with redir

[Service]
ExecStart=/bin/redir -sn :80 127.0.0.1:8080

[Install]
WantedBy=multi-user.target

Nous pouvons enregistrer l’unité sous le nom /etc/systemd/system/redir.service. Pour démarrer et activer le service au démarrage, nous exécutons :

sudo systemctl enable --now redir.service

Mappage du port non privilégié au conteneur

Une fois que nous avons redirigé le trafic portuaire, nous pouvons enfin exécuter le conteneur et le lier au port non privilégié :

docker run --name test_httpd -p 8080:80 httpd:latest

Réglage de la capacité CAP_NET_BIND_SERVICE

Une alternative docker uniquement à la redirection du trafic portuaire consiste à définir la capacité CAP_NET_BIND_SERVICE sur le binaire /usr/bin/rootlesskit. Les capacités sont des autorisations spéciales qui peuvent être attribuées aux processus sous Linux. CAP_NET_BIND_SERVICE’est celle qui nous intéresse dans ce cas, puisqu’elle permet de lier un socket à un port privilégié. Pour attribuer la capacité CAP_NET_BIND_SERVICE au binaire rootlesskit, nous exécutons la commande suivante :

sudo setcap cap_net_bind_service=ep /usr/bin/rootlesskit

Pour supprimer la fonctionnalité, procédez comme suit :

sudo setcap -r /usr/bin/rootlesskit

Modification de la plage de port non privilégié

La dernière stratégie que nous allons examiner ici consiste à changer la plage des ports privilégiés. Pour vérifier quels ports sont considérés comme privilégiés par le système, il suffit de lire le fichier /proc/sys/net/ipv4/ip_unprivileged_port_start : il contient le premier port non privilégié du système (la valeur par défaut est 1024). En s’en tenant à l’exemple précédent, pour que le système considère le port 80 comme non privilégié, nous écririons la valeur appropriée dans le fichier :

echo 80 | sudo tee /proc/sys/net/ipv4/ip_unprivileged_port_start

Pour que ce changement soit persistant, nous devons ajouter la ligne suivante à un fichier de configuration dans le répertoire /etc/sysctl.d/ :

echo net.ipv4.ip_unprivileged_port_start = 80 | sudo tee /etc/sysctl.d/90-unprivileged_port_start.conf

Notez qu’en adoptant cette stratégie, le système considérera tous les ports >= 80 comme non privilégiés !

Conclusions

Dans ce tutoriel, nous avons vu quelques méthodes que nous pouvons utiliser pour permettre à un conteneur sans racine de se lier à un port privilégié. Nous avons vu comment rediriger le trafic d’un port privilégié vers un port non privilégié, comment définir la capacité CAP_NET_BIND_SERVICE et, enfin, comment modifier la plage de ports privilégiés.

Articles connexes: