firejail firecfg – Problem mit dem KDE-Login unter Opensuse Leap 42.3 – .ssh-Dateien und ssh-agent als Ursache

Experimentiert man unbedarft mir neuen Dingen, muss man sich nicht wundern, wenn man sich Probleme schafft. Jetzt passierte mir das mit firejail.

Ich muss manchmal Skype benutzen. Zu meinem großen Ärger, da jede Art von Closed Source UDP-Streaming Applikation ein prinzipielles Sicherheitsrisiko darstellt. In einen UDP-Datenstrom können immer Pakete integriert werden, die ganz anderen Zwecken dienen als der Übertragung der eigentlichen Audio- und Videodaten. Und wenn man dann den Code nicht kennt ... Neben dem Aufsetzen eines speziell konfigurierten Containers ist die Nutzung von Firejail eine Gegenmaßnahme, solchen Applikationen das Vordringen ins System zu erschweren.

Opensuse 42.3 bietet im Repository
"http://download.opensuse.org/repositories/Virtualization/openSUSE_Leap_42.3/"
ein RPM-Paket firejail 0.9.5 zum Download an. Das habe ich installiert. nach der Installation war zunächst auch ein kein Problem feststellbar.

Aber dann habe ich ein wenig in der Dokumentation gestöbert. Auf der Seite https://firejail.wordpress.com/documentation-2/ findet man dann unter dem Titel "I’ve just installed Firejail, now what?!" die Hinweise über bestimmte Kommandos "pulseaudio" [PA] zu korrigieren (wundert mich nicht) und auch das Kommando "firecfg" auszuführen:

$ firecfg --fix-sound
$ sudo firecfg

Beides hat u.U. aber unangenehme Nebenwirkungen:

1) Pulseaudio zunächst nicht mehr deaktivierbar
Das erste Kommando korrigiert "pulseaudio"-Konfigurationsdateien zwar; es sorgt aber auch dafür, dass man unter Opensuse PA nicht mehr einfach über YaST und die dortige Audio-Konfiguraiton abschalten kann. Bekanntermaßen verwende ich PA nur, wenn nicht umgehbar. So erzwangen etwa frühere Skype Versionen für Linux ( u.a. die Version 4.3) die Nutzung von PA. Das ist beim aktuellen "skypeforlinux" (Beta) nicht der Fall; das kooperiert auch direkt mit Alsa.

Das ist auf meinem Laptop auch gut so, denn PA erzeugt dort ein dauerhaftes unangenehmes Hintergrundsgeräusch (niederfrequenter Pfeifton; keine Rückkopplung) auf meinem eingebauten Mikrophon (unabhängig von Skype). Das ließ sich auch durch Ändern der Empfindlichkeit und der Regelung von Verstärkungsfaktoren unter dem PA-eigenen "pavucontrol" nie beseitigen. Ein solches Geräusch tritt bei mir im reinen Alsa-Betrieb dagegen nicht auf.

Beseitigt habe ich die Aktivierung von PA dann durch Umbenennen/Verschieben der Datei "~/.pulseaudio" und anschließende erneute Deaktivierung von PA über YaST.

2) Kein KDE-Login und kein X11-Start mehr für bestimmte Accounts möglich
Schlimmer ist dagegen eine Auswirkung des zweiten Kommandos: Es erzeugt eine Liste von Links unter dem Verzeichnis von "/usr/local/bin", die alle auf firejail verweisen. Normalerweise führt das dazu, dass viele Programme/Anwendungen über firejail gestartet und geschützt werden. Leider ist die Liste der Profile für Anwendungen aber keineswegs vollständig. Wird für eine Anwendung nichts gefunden, dann kommt wohl ein "default.profile" zum Einsatz. Letzteres verhindert aber den Zugriff u.a. auf ~/.ssh und ~/.gnupg-Verzeichnisse und dortige Dateien. Das wird aber unter Umständen unter Opensuse zu einem Megaproblem beim Start von KDE aus SDDM oder KDM heraus:

Ich konnte mich unter bestimmten Accounts nach dem Absetzen von firecfg nicht mehr von KDM oder SDDM aus in KDE (Plasma 5) einloggen. X11 wurde nicht gestartet; man landet nach ca. 1 Sekunde wieder auf dem KDM oder SDDM-Login-Schirm.

Die Fehlermeldungen hierzu waren unzureichend. Nach mühsamen Experimenten mit Konfigurationsdateien stieß ich schließlich darauf, dass allein die Existenz eines "~/.ssh"-Verzeichnisses zu Fehlern beim KDE-Start führte. Entfernte ich das Verzeichnis komplett, ließen sich KDE und X11 wieder regulär starten.

Wer oder was will im Start "~/.ssh" auslesen? Mir fiel dazu zunächst nichts ein. Dann stieß ich auf ssh-agent. Und tatsächlich ergab sich unter Opensuse Leap 42.3 folgendes Bild:

mytux:~ # ps aux | grep ssh
root      1574  0.0  0.0  53492  5816 ?        Ss   13:30   0:00 /usr/sbin/
sshd -D
myself       4065  0.0  0.0  20136  1868 ?        S    13:30   0:00 /usr/bin/dbus-launch --sh-syntax --exit-with-session /usr/bin/ssh-agent /usr/bin/gpg-agent --sh --daemon --keep-display --write-env-file /home/myself/.gnupg/
agent.info-mytux.mydomain.de:0 /etc/X11/xinit/xinitrc
myself       4067  0.0  0.0  13312   332 ?        Ss   13:30   0:00 /usr/bin/ssh-agent /usr/bin/gpg-agent --sh --daemon --keep-display --write-env-file /home/myself/.gnupg/agent.info-mytux.mydomain.de:0 /etc/X11/xinit/xinitrc

Offenbar klappt diese Art des X-Starts nicht mit dem komplett gefüllten "/usr/local/bin" (genauer mit den dortigen Links). Ich sehe die Ursache - wie gesagt - im "default.profile".

Die Link-Orgie unter "/usr/local/bin" beseitigt man übrigens mit

firecfg --clean

Danach klappt der Login in das X11/KDE-Gespann alles wieder. Meine Strategie ist im Moment, die Profile für verschiedene Anwendungen zu checken und "/usr/local/bin" langsam und systematisch mit Links zu füllen, die nur bestimmte Anwendungen betreffen.

Bei manchen alten Accounts habe ich zudem festgestellt, dass die Anlage der Links unter /usr/local/bin" auch dazu führt, dass jeglicher Netzwerkzugriff unterbunden ist. Hier bin ich noch an der Ursachenforschung.

Merke: Nicht gleich alles machen, was Manuals vorschlagen, wenn man von einer neuen Anwendung noch zu wenig versteht.

Upgrade Laptop to Opensuse 42.3, Probleme mit Bumblebee und VMware WS 12.5, Workarounds

Gestern war ein Upgrade meines nun schon in die Jahre gekommenen Laptops von Opensuse Leap 42.2 auf Leap 42.3 fälllig.
Ich bin dabei gem. der schönen Anleitung in
https://kamarada.github.io/en/2017/08/03/how-to-upgrade-from-opensuse-leap-422-to-423/
vorgegangen. Zu der Anleitung gibt es nichts weiter zu sagen; die ist perfekt. Im Upgrade hatte ich nur die Standardrepositories (inkl. Update-Repository) für Leap 42.3 benutzt.

Mein Laptop hat eine Nvidia-Karte (Optimus-System). Das ursprüngliche Leap 42.2 lief auf dem Laptop deshalb mit einer Bumblebee-Installation; das funktionierte einwandfrei. Zudem nutzte ich auf dem Laptop VMware WS 12.5. Nach dem Leap-Upgrade hatte ich jedoch sowohl mit Bumblebee als auch VMware-Workstation Probleme - obwohl auch Leap 42.3 nur einen Kernel der nun doch schon recht alten Version 4.4 aufweist! nach dem Ugrade war bei mir 4.4.92-31 aktiv; bei der Leap_42.3 war dagegen der Kernel 4.4.76 der Default-Kernel.

Nebenbei: Bzgl. der Kernelversionen hat der SLES-Unterbau von Opensuse Leap plötzlich den unangenehmen Nebeneffekt, dass man an älteren Kernelversionen kleben bleibt ... Von SUSEs Seite müssen ggf. Back-Portierungen aus neuen Kernelversionen zu älteren Versionen vorgenommen werden. Das kann Nebeneffekte zeitigen (s.u.). Bin mal gespannt wie Opensuse mit diesem Thema in Zukunft umgehen will ...

Wiederholte Modul-Einträge in der Datei "/etc/sysconfig/kernel"

Das erste Problem war, dass die Datei "/etc/sysconfig/kernel" nach dem Neustart mehrfache Einträge zum Laden von nvidia-Modulen enthielt. Woher immer das stammte; vielleicht hatte ich das ja schon früher von Experimenten mit Bumblebee drin. Vielleicht wurden die Einträge aber auch im Upgrade hinzugefügt. Jedenfalls mal checken, dass in dieser Datei nach dem Upgrade kein überflüssiger Unsinn drinsteht.

Bumblebee-Installation wieder zum Laufen bringen

Bumblebee lief nach dem Upgrade nicht mehr. Ok, dachte ich, also die für Leap 42.3 passenden Repositories aktivieren und diverse Bumblebee-Pakete aktualisieren. Es gibt jedoch mehrere Repositories mit Bumblebee-Paketen für Opensuse Leap, u.a.
http://download.opensuse.org/repositories/home:/Bumblebee-Project:/nvidia:/3xx.xx/OpenSUSE_Leap_42.3.
Unter Leap 42.1/42.2 hatte ich etliche Pakete aus diesen Repositories benutzt.

Für Leap 42.3 gilt (nach meiner Erfahrung): Zu nutzen ist
http://download.opensuse.org/repositories/X11:/Bumblebee/openSUSE_Leap_42.3
und sonst gar nichts! Auch nicht das Nvidia-Community-Repository!

Die im "X11:Bumblebee"-Repository vorhandenen Pakete - inklusive der Pakete mit dem proprietären Nvidia-Treiber - kann und sollte man dagegen (bis auf eines) installieren; das Paket "primus" habe ich mir allerdings aus dem 42.3-Update-Repository geholt.

Ergänzung 02.12.2017: Wichtige Ausnahme:
bbswitch sollte man nicht installieren. Es reicht bbswitch-kmp-default! Und nur letzteres hat bei mir funktioniert - und zwar ohne dkms-Service.

Die Installation von bbswitch aktiviert den "dkms"-Service; waren sowohl "bbswitch-kmp-default" und "bbswitch" installiert, so führte dies bei mir anhand von Statusanzeigen erkennbar zu einem wechselseitigen An- und Abschalten der Graka im Bootprozess; sie wird danach von den Treibern nicht mehr erkannt.

(Auf die anderen Repositories zu unterschiedlichen Nvidia-Treibern sollte man wirklich nur im Notfall zurückgreifen, und zwar dann, wenn ihr für eure Graka zwingend einen älteren Nvidia-Treiber benötigt; aber auch dann nur x11-video-nvidia installieren. Nicht dagegen das Paket "dkms-nvidia"!)

Zu beachten ist also auch folgender Hinweis: Falls ihr früher einen laufenden dkms-Service hattet: Unbedingt deaktivieren! Und zwar nach der Installation der Pakete, aber schon vor einem anschließenden Neustart des Systems.

systemctl disable dkms

Das steht im Gegensatz zu den Anweisungen in der Anleitung
https://de.opensuse.org/SDB:NVIDIA_Bumblebee
absolut notwendig! Zumindest auf meinem Laptop ... Fragt mich bitte nicht, warum der dkms-Service zu Problemen führt.

Der Bumblebee-Dämon "bumblebeed" dagegen muss über den zuständigen Service aktiviert werden

systemctl enable bumblebeed

Zudem checken, dass der User, unter dem ihr mit einer grafischen Oberfläche arbeitet, Mitglied der Gruppen "video" und "bumblebee" ist. Ggf. mittels "usermod -G video,bumblebee USERNAME" korrigieren.

Dann Neustart des Systems. Die Kommandos

optirun glxspheres
vblank_mode=0 primusrun glxspheres
optirun -b none nvidia-settings -c :8

sollten danach alle einwandfrei funktionieren.

Sollte das nicht der Fall sein und immer noch eine Meldung kommen, dass die Graka nicht vorhanden sei und der "nvidia"-Treiber nicht geladen werden könne:

Alle Pakete aus dem Nvidia-(Community)-Repository (Treiber nvidia-gfx-GL04 und ähnliche), aus dem Nvidia-Bumblebee-Repository und dem oben angegebenen Standard-Bumblebee-Repository löschen. Danach nur die Pakete aus dem oben angegebenen Standard-Repository http://download.opensuse.org/repositories/X11:/Bumblebee/openSUSE_Leap_42.3
mit Ausnahme von bbswitch (!) installieren. Den dkms-Service dann prophylaktisch deaktivieren! Neustart.

Ein probeweiser Start des dkms-Service führt nach einem vorhergehenden Erfolg mit "primusrun" in jedem Fall wieder in die Katastrophe:

Danach kommen in Logfiles Fehlermeldungen, dass es kein passendes Grafik Device gäbe. Am Terminal erscheint: "Cannot access secondary GPU ...". Das ließ sich durch ein anschließendes normales Stoppen des dkms-Service nicht mehr beheben. Bumblebee funktionierte auch nach dem Stoppen des dkms-Service nicht mehr ordnungsgemäß; nvidia Module ließen sich selbst manuell nicht mehr laden. Da half nur ein Reboot - natürlich bei deaktiviertem dkms-Service.

Ich habe leider keine Zeit, der genauen Ursache auf den Grund zu gehen. Bei künftigen Änderungen des Kernels muss man ohne korrekt funktionierendes dkms ggf. halt ein Update für die nvidia- und bbswitch-Module aus dem Bumblebee-Repository erzwingen und damit (zumindest bzgl. nvidia) eine Neukompilation durchführen lassen. Interessant ist, dass für den bei mir nach dem Leap-Upgrade aktiven Kernel 4.4.92-31 ein Link von
/lib/modules/4.4.92-31-default/weak-updates/updatesbbswitch.ko -> /lib/modules/4.4.676-1-default/updates/bbswitch.ko
angelegt wurde. Der funktioniert offenbar. Irgendwas am Kernel 4.4.92 missfällt womöglich dkms beim Versuch, für den neueren Kernel das passende Modul zu definieren. Der 4.4.92-Kernel führt aufgrund von Rückportierungen, die die SuSE-Leute wohl vorgenommen haben, auch noch in anderem Kontext - nämlich bzgl. der VMware WS - zu Schwierigkeiten.

Probleme mit der VMware Workstation 12.5. unter Leap 42.3 beheben

Meine unter Leap 42.2 installierte VM WS 12.5.1 lief nach dem Leap-Upgrade nicht mehr. Auch ein Upgrade der Workstation-SW auf die aktuelle Version 12.5.8 endete beim ersten Startversuch mit Kompilierungsfehlern. Die konnte ich mir im Detail zwar ansehen; wie man aber die problematischen Stellen im Quellcode der VMware-Module beheben hätte müssen, lag jenseits meiner Kenntnisse und Fähigkeiten.

Hier half aber der Beitrag eines offenbar Kernel-Kundigen im VMware Community Forum:
https://communities.vmware.com/message/2693257#2693257
Dort suche man nach dem Beitrag von "hendrikw84"! Herzlichen Dank an diesen Herrn! Seine Vorgaben zur Korrektur diverser Codezeilen funktionieren nämlich einwandfrei. (Ursache der Probleme sind offenbar Rückwärtsportierungen von Features des Kernels 4.10 in den Code des Kernels 4.4. Was immer die SuSE-Leute dabei gedacht haben ...)

[ Warum allerdings die eine vorgeschlagene Korrektur-Zeile

retval = retval = get_user_pages((unsigned long)uvAddr, numPages, 0, ppages, NULL);

nicht gleich zu

retval = get_user_pages((unsigned long)uvAddr, numPages, 0, ppages, NULL);

verkürzt werden kann, ist mir etwas schleierhaft. Typo? Die letzte Zeile für retval klappt für den Code von hostif.c unter vmmon-only/linux nämlich auch.]

Nach Durchführung der Korrekturen ließen sich die VMware-Codes jedenfalls anstandslos für Kernel "4.4.9-31-default" kompilieren - und die nötigen Kernelmodule wurden fehlerfrei erzeugt. Meine zwei lokalen (non shared) virtuellen Maschinen für Windows-Installationen liefen damit bislang einwandfrei.

Ob es - wie in der Diskussion im VMware Community Forum angedeutet, Probleme mit "shared VMs" auf Servern gibt, habe ich nicht getestet. Auf Servern verwende ich KVM-Installaionen mit spice oder X2GO.

Viel Spaß denn mit Opensuse 42.3 auf dem Laptop!

Fun with veth-devices, Linux bridges and VLANs in unnamed Linux network namespaces – I

Recently, I started writing some blog posts about my first experiences with LXC-containers and libvirt/virt-manager. Whilst gathering knowledge about LXC basics I stumbled across four hurdles for dummies as me, who would like to experiment with network namespaces, veth devices and bridges on the command line and/or in the context of LXC-containers built with virt-manager:

  • When you use virt-manager/libvirt to set up LXC-containers you are no longer able to use the native LXC commands to deal with these containers. virt-manager/virsh/libvirt directly use the kernel API for cgroups/namespaces and provide their own and specific user interfaces (graphical, virsh, XML configuration files) for the setup of LXC containers and their networks. Not very helpful for quick basic experiments on virtual networking in network namespaces ....
  • LXC-containers created via virt-manager/virsh/libvirt use unnamed namespaces which are identified by unique inode numbers, but not by explicit names. However, almost all articles on the Internet which try to provide a basic understanding of network namespaces and veth devices explicitly use "ip" command options for named namespaces. This raises the question: How to deal with unnamed network namespaces?
  • As a beginner you normally do not know how to get a shell for exploring an existing unnamed namespace. Books offer certain options of the "ip"-command - but these again refer to named network namespaces. You may need such a shell - not only for basic experiments, but also as the administrator of the container's host: there are many situations in which you would like to enter the (network) namespace of a LXC container directly.
  • When you experiment with complex network structures you may quickly loose the overview over which of the many veth interfaces on your machine is assigned to which (network) namespace.

Objectives and requirements

Unfortunately, even books as "Containerization with LXC" of K. Ivanov did not provide me with the few hints and commands that would have been helpful. I want to close this gap with 2 blog posts. The simple commands and experiments shown below and in a subsequent article may help others to quickly setup basic network structures for different namespaces - without being dependent on named namespaces, which will not be provided by virt-manager/libvirt. I concentrate on the network namespace type here, but some of the things may work for other namespace types, too.

After a look at some basics, we will create a shell associated with a new unnamed network namespace which will be different from the network namespace of other system processes. Afterwards we will learn how to enter an existing unnamed namespaces by a new shell. A third objective is the attachment of virtual network devices to a network namespace.

In further articles we will use our gathered knowledge to attach veth interfaces of 2 different namespaces to virtual bridges/switches in yet a third namespace, then link the host to the bridge/switch and test communications as well as routing. We shall the extend our virtual networking scenario to isolated groups of namespaces (or containers, if you like) via VLANs. As a side aspect we shall learn how to use a Linux bridge for defining VLANs.

All our experiments will lead to temporary namespaces which can quickly be cretated by scripts and destroyed by killing the basic shell processes associated with them.

Requirements: The kernel should have been compiled with option "CONFIG_NET_NS=y". We make use of userspace tools that are provided as parts of a RPM or DEB packet named "util-linux" on most Linux distributions.

Namespaces

Some basics first. There are 6 different types of "namespaces" for the isolation of processes or process groups on a Linux system. The different namespace types separate PID-trees, the networks, User-UIDs, mounts, inter process communication, host/domain-names (uts) of process groups against each each other. Every process on a host is attached to certain namespace (of each type), which it may or may not have in common with another process.

"Separation" means: Limitation of the view on the process' own environment and on the environment of other processes on the system. "Separation" also means a limitation of the control a process can get on processes/environments associated with other namespaces.

Therefore, to isolate LXC containers from other containers and from the host, the container's processes will typically be assigned to distinct namespaces of most of the 6 types. In addition: The root filesystem of a LXC containers typically resides in a chroot jail.

Three side remarks:

  1. cgroups limit the ressource utilization of process groups on a host. We do not look at cgroups in this article.
  2. Without certain measures the UID namespace of a LXC container will be the same as the namespace of the host. This is e.g. the case for a standard container created with virt-manager. Then root in the container is root on the host. When a container's basic processes are run with root-privileges of the host we talk of a "privileged container". Privileged containers pose a potential danger to the host if the container's environment could be left. There are means to escape chroot jails - and under certain circumstances there are means to cross the borders of a container ... and then root is root on the host.
  3. You should be very clear about the fact that a secure isolation of processes and containers on a host depend on other more sophisticated isolation mechanisms beyond namespaces and chroot jails. Typically, SE Linux or Apparmor rules may be required to prevent crossing the line from a namespace attached process to the host environment.

In our network namespace experiments below we normally will not separate the UID namespaces. If you need to do it, you must map a non-privileged UID (> 1000) on UID 0 inside the namespace to be able to perform certain network operations. See the options in the man pages of the commands used below for this mapping.

Network namespaces

The relevant namespace type for the network environment (NICs, bridges etc.) to which a process has access to is the "network namespace". Below I will sometimes use the abbreviation "net-ns".

When you think about it, you will find the above statements on network isolation unclear: In the real world network packets originate from electronic devices, are transported through cables and are then distributed and redirected by other devices and eventually terminate at yet other electronic devices. So, one may ask : Can a network packet created by a (virtual) network device within a certain namespace cross the namespace border (whatever this may be) at all? Yes, they can:

Network namespaces affect network devices (also virtual ones) and also routing rules coupled to device ports. However, network packets do NOT care about network namespaces on OSI level 2.

To be more precise: Network namespace separation affects network-devices (e.g. Ethernet devices, virtual Linux bridges/switches), IPv4/IPv6 protocol stacks, routing tables, ARP tables, firewalls, /proc/net, /sys/class/net/, QoS policies, ports, port numbers, sockets. But is does not stop an Ethernet packet to reach an Ethernet device in another namespace - as long as the packet can propagate in the virtual network environment at all.

So, now you may ask what means we have available to represent something like cables and Ethernet transport between namespaces? This is one of the purposes veth devices have been invented for! So, we shall study how to bridge different namespaces by the use of Ethernet interfaces of veth devices and virtual Linux bridges/switches.

However, regarding container operation you would still want the following to be true for packet filtering:

A fundamental container process, its children and network devices should be confined to devices of a certain "network namespace" because they should not be able to have any direct influence on network devices of other containers or the host. And: Even if packets move from one network namespace to another you probably want to be able to restrict this traffic in virtual networks as you do in real networks - e.g by packet filter rules (ebtables, iptables) or by VLAN definitions governing ports on virtual bridges/switches.

Many aspects of virtual bridges, filtering, VLANs can be tested already in a simple shell based namespace environment - i.e. without full-fletched containers. See the coming articles for such experiments ...

Listing network namespaces on a host

The first thing we need is an overview over active namespaces on a host. For listing namespaces we can use the command "lsns" on a modern Linux system. This command has several options which you may look up in the man pages. Below I show you an excerpt of the output of "lsns" for network namespaces (option "-t net") on a system where a LXC container was previously started by virt-manager:

mytux:~ # lsns -t net -o NS,TYPE,PATH,NPROCS,PID,PPID,COMMAND,UID,USER 
        NS TYPE PATH              NPROCS   PID  PPID COMMAND                  UID USER
4026531963 net  /proc/1/ns/net       389     1     0 /usr/lib/systemd/system    0 root
4026540989 net  /proc/5284/ns/net     21  5284  5282 /sbin/init                 0 root

Actually, I have omitted some more processes with separate namespaces, which are not relevant in our context. So, do not be surprised if you should find more processes with distinct network namespaces on your system.

The "NS" numbers given in the output are so called "namespace identification numbers". Actually they are unique inode numbers. (For the reader it may be instructive to let "lsns" run for all namespaces of the host - and compare the outputs.)

Obviously, in our case there is some process with PID "5282", which has provided a special net-ns for the process with PID "5284":

mytux:~ # ps aux | grep 5282
root      5282  0.0  0.0 161964  8484 ?        Sl   09:58   0:00 /usr/lib64/libvirt/libvirt_lxc --name lxc1 --console 23 --security=apparmor --handshake 26 --veth vnet1

This is the process which started the running LXC container from the virt-manager interface. The process with PID "5284" actually is the "init"-Process of this container - which is limited to the network namespace created for it.

Now let us filter or group namespace and process information in different ways:

Overview over all namespaces associated with a process
This is easy - just use the option "-p" :

mytux:~ # lsns -p 5284 -o NS,TYPE,PATH,NPROCS,PID,PPID,COMMAND,UID,USER 
        NS TYPE  PATH              NPROCS   PID  PPID COMMAND                                                            UID USER
4026531837 user  /proc/1/ns/user      416     1     0 /usr/lib/systemd/systemd --switched-root --system --deserialize 24   0 root
4026540984 mnt   /proc/5284/ns/mnt     20  5284  5282 /sbin/init                                                           0 root
4026540985 uts   /proc/5284/ns/uts     20  5284  5282 /sbin/init                                                           0 root
4026540986 ipc   /proc/5284/ns/ipc     20  5284  5282 /sbin/init                                                           0 root
4026540987 pid   /proc/5284/ns/pid     20  5284  5282 /sbin/init                                                           0 root
4026540989 net   /proc/5284/ns/net     21  5284  5282 /sbin/init                                                           0 root

Looking up namespaces for a process in the proc-directory
Another approach for looking up namespaces makes use of the "/proc" directory. E.g. on a different system, where a process with PID 4634 is associated with a LXC-container:

mylx:/proc # ls -lai /proc/1/ns
total 0
344372 dr-x--x--x 2 root root 0 Oct  7 11:28 .
  1165 dr-xr-xr-x 9 root root 0 Oct  7 09:34 ..
341734 lrwxrwxrwx 1 root root 0 Oct  7 11:28 ipc -> ipc:[4026531839]
341737 lrwxrwxrwx 1 root root 0 Oct  7 11:28 mnt -> mnt:[4026531840]
344373 lrwxrwxrwx 1 root root 0 Oct  7 11:28 net -> net:[4026531963]
341735 lrwxrwxrwx 1 root root 0 Oct  7 11:28 pid -> pid:[4026531836]
341736 lrwxrwxrwx 1 root root 0 Oct  7 11:28 user -> user:[4026531837]
341733 lrwxrwxrwx 1 root root 0 Oct  7 11:28 uts -> uts:[4026531838]
mylx:/proc # ls -lai /proc/4634/ns
total 0
 38887 dr-x--x--x 2 root root 0 Oct  7 09:36 .
 40573 dr-xr-xr-x 9 root root 0 Oct  7 09:36 ..
341763 lrwxrwxrwx 1 root root 0 Oct  7 11:28 ipc -> ipc:[4026540980]
341765 lrwxrwxrwx 1 root root 0 Oct  7 11:28 mnt -> mnt:[4026540978]
345062 lrwxrwxrwx 1 root root 0 Oct  7 11:28 net -> net:[4026540983]
 38888 lrwxrwxrwx 1 root root 0 Oct  7 09:36 pid -> pid:[4026540981]
341764 lrwxrwxrwx 1 root root 0 Oct  7 11:28 user -> user:[4026531837]
341762 lrwxrwxrwx 1 root root 0 Oct  7 11:28 uts -> uts:[4026540979]

What does this output for 2 different processes tell us? Obviously, the host and the LXC container have different namespaces - with one remarkable exception: the "user namespace"! They are identical. Meaning: Root on the container is root on the host. A typical sign of a "privileged" LXC container and of potential security issues.

List all processes related to a given namespace?
"lsns" does not help us here. Note:

"lsns" only shows you the lowest PID associated with a certain (network) namespace.

So, you have to use the "ps" commands with appropriate filters. The following is from a system, where a LXC container is bound to the network namespace with identification number 4026540989:

mytux:~ # lsns -t net -o NS,TYPE,PATH,NPROCS,PID,PPID,COMMAND,UID,USER
        NS TYPE PATH              NPROCS   PID  PPID COMMAND                                               UID USER
4026531963 net  /proc/1/ns/net       401     1     0 /usr/lib/systemd/systemd --switched-root --system --d   0 root
4026540989 net  /proc/6866/ns/net     20  6866  6864 /sbin/init                                              0 root
mytux:~ #  ps -eo netns,pid,ppid,user,args --sort netns | grep 4026540989
4026531963 16077  4715 root     grep --color=auto 4026540989
4026540989  6866  6864 root     /sbin/init
4026540989  6899  6866 root     /usr/lib/systemd/systemd-journald
4026540989  6922  6866 root     /usr/sbin/ModemManager
4026540989  6925  6866 message+ /bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation
4026540989  6927  6866 tftp     /usr/sbin/nscd
4026540989  6943  6866 root     /usr/lib/wicked/bin/wickedd-dhcp6 --systemd --foreground
4026540989  6945  6866 root     /usr/lib/wicked/bin/wickedd-dhcp4 --systemd --foreground
4026540989  6947  6866 systemd+ avahi-daemon: running [linux.local]
4026540989  6949  6866 root     /usr/lib/wicked/bin/wickedd-auto4 --systemd --foreground
4026540989  6951  6866 avahi-a+ /usr/lib/polkit-1/polkitd --no-debug
4026540989  6954  6866 root     /usr/lib/systemd/systemd-logind
4026540989  6955  6866 root     login -- root
4026540989  6967  6866 root     /usr/sbin/wickedd --systemd --foreground
4026540989  6975  6866 root     /usr/sbin/wickedd-nanny --systemd --foreground
4026540989  7032  6866 root     /usr/lib/accounts-daemon
4026540989  7353  6866 root     /usr/sbin/cupsd -f
4026540989  7444  6866 root     /usr/lib/postfix/master -w
4026540989  7445  7444 postfix  pickup -l -t fifo -u
4026540989  7446  7444 postfix  qmgr -l -t fifo -u
4026540989  7463  6866 root     /usr/sbin/cron -n
4026540989  7507  6866 root     /usr/lib/systemd/systemd --user
4026540989  7511  7507 root     (sd-pam)
4026540989  7514  6955 root     -bash

If you work a lot with LXC containers it my be worth writing some clever bash or python-script for analyzing the "/proc"-directory with adjustable filters to achieve a customizable overview over processes attached to certain namespaces or containers.

Hint regarding the NS values in the following examples:
The following examples have been performed on different systems or after different start situations of one and the same system. So it makes no sense to compare all NS values between different examples - but only within an example.

Create a shell inside a new network namespace with the "unshare" command ...

For some simple experiments it would be helpful if we could create a shell with its own network-namespace. For this purpose Linux provides us with the command "unshare" (again with a lot of options, which you should look up). For starting a new bash with a separate net-ns we use the option "-n":

mytux:~ # unshare -n /bin/bash 
mytux:~ # lsns -t net
        NS TYPE NPROCS   PID USER  COMMAND
4026531963 net     398     1 root  /usr/lib/systemd/systemd --switched-root --system --deserialize 24
4026540989 net      21  5284 root  /sbin/init
4026541186 net       2 27970 root  /bin/bash
mytux:~ # ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
mytux:~ # exit
exit
mytux:~ # ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether d7:58:88:ab:cd:ef brd ff:ff:ff:ff:ff:ff
....
....

Obviously, it is not possible to see from the prompt that we have entered a different (network) namespace with the creation of the new shell. We shall take care of this in a moment. For the time being, it may be a good idea to issue commands like

lsns -t net -p 1; lsns -t net -p $$

in the shell opened with "unshare". However, also our look at the network interfaces proved that the started "bash" was directly associated with a different net-ns than the "parent" bash. In the "unshared" bash only a "lo"-device was provided. When we left the newly created "bash" we at once saw more network devices (namely the devices of the host).

Information about host processes from a shell inside a specific network namespace?
You can get information about all processes on a host from any process with a specific network namespace - as log as the PID namespace for this process is not separated from the PID namespace of the host. And as long as we have not separated the UID namespaces: root in a network namespace then is root on the host with all the rights there!

Can a normal unprivileged user use "unshare", too?

Yes, but his/her UID must be mapped to root inside the new network namespace. For this purpose we can use the option "-r" of the unshare command; see the man pages. Otherwise: Not without certain measures - e.g. on the sudo side. (And think about security when using sudo directives. The links at the end of the article may give you some ideas about some risks.)

You may try the following commands (here executed on a freshly started system):

myself@mytux:~> unshare -n -r /bin/bash 
mytux:~ # lsns -t net -t user
        NS TYPE  NPROCS   PID USER COMMAND
4026540842 user       2  6574 root /bin/bash
4026540846 net        2  6574 root /bin/bash
mytux:~ # 

Note the change of the prompt as the shell starts inside the new network namespace! And "lsns" does not give us any information on the NS numbers for net and user namespaces of normal host processes! However, on another host terminal the "real" root of the host gets:

mytux:~ # lsns -t net -t user 
        NS TYPE  NPROCS   PID USER   COMMAND
4026531837 user     382     1 root   /usr/lib/systemd/systemd --switched-root --system --deserialize 24
4026531963 net      380     1 root   /usr/lib/systemd/systemd --switched-root --system --deserialize 24
4026540842 user       1  6574 myself /bin/bash
4026540846 net        1  6574 myself /bin/bash

There, we see that the user namespaces of the unshared shell and other host processes really are different.

Open a shell for a new named network namespace

The "unshare" command does not care about "named" network namespaces. So, for the sake of completeness: If you like to or must experiment with named network namespaces you may want to use the "ip" command with appropriate options, e.g.:

mytux:~ # ip netns add mynetns1 
mytux:~ # ip netns exec mynetns1 bash
mytux:~ # lsns -o NS -t net -p $$
        NS
4026541079
mytux:~ # exit 
mytux:~ # lsns -o NS -t net -p $$
        NS
4026531963
mytux:~ # 

"mynetns1" in the example is the name that I gave to my newly created named network namespace.

How to open a shell for an already existing network namespace? Use "nsenter" ...

Regarding processes with their specific namespaces or LXC containers: How can we open a shell that is assigned to the same network namespace as a specific process? This is what the command "nsenter" is good for. For our purposes the options "-t" and "-n" are relevant (see the man pages). In the following example we first create a bash shell (PID 15150) with a new network namespace and move its process in the background. Then we open a new bash in the foreground (PID 15180) and attach this bash shell to the namespace of the process with PID 15150:

mylx:~ # unshare -n /bin/bash &
[1] 15150
mylx:~ # lsns -t net 
        NS TYPE NPROCS   PID USER  COMMAND
4026531963 net     379     1 root  /usr/lib/systemd/systemd --switched-root --system --deserialize 24
4026540983 net      23  4634 root  /sbin/init
4026541170 net       1 15150 root  /bin/bash

[1]+  Stopped                 unshare -n /bin/bash
mylx:~ # nsenter -t 15150 -n /bin/bash
mylx:~ # ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
mylx:~ # echo $$
15180
mylx:~ # lsns -t net -p $$
        NS TYPE NPROCS   PID USER COMMAND
4026541170 net       3 15150 root /bin/bash
mylx:~ # 

Note, again, that "lsns" only gives you the lowest process number that opened a namespace. Actually, we are in a different bash with PID "15180". If you want to see all process using the same network namespace you may use :

mylx:~ # echo $$
15180
mylx:~ # ps -eo pid,user,netns,args --sort user | grep 4026541170
15150 root     4026541170 /bin/bash
15180 root     4026541170 /bin/bash
16284 root     4026541170 ps -eo pid,user,netns,args --sort user
16285 root     4026541170 grep --color=auto 4026541170

In the same way you can create a shell and assign it to the network namespace of a running LXC container. Let us try this for an existing LXC container on system "mylx" with PID 4634 (see above: 4026540983 net 23 4634 root /sbin/init).

  
mylx:~ # nsenter -t 4634 -n /bin/bash
mylx:~ # ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
13: eth0@if14: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 00:16:3e:a3:22:b8 brd ff:ff:ff:ff:ff:ff link-netnsid 0
mylx:~ # exit
exit

Obviously, an ethernet device eth0 exists in this container. Actually, it is an interface of a veth device with a peer interface "if14"; see below.

Change the hostname part of a shell's prompt in a separate network namespace

We saw that the prompt of a shell in a separate network namespace normally does not indicate anything about the namespace environment. How can we change this? We need 2 steps to achieve this:

  • We open a shell in the background not only for a separate network namespace but also for a different uts namespace. Then any changes to the hostname inside the uts namespace for the running background process will have no impact on the host.
  • The "nsenter" command does not only work for shells but for any reasonable command. Therefore, we can also apply it for the command "hostname".

Now, before we enter the separate namespaces of the process with yet another shell we can first change the hostname in the newly created uts namespace:

mytx:~ # unshare --net --uts /bin/bash &
[1] 25512
mytux:~ # nsenter -t 25512 -u hostname netns1

[1]+  Stopped                 unshare --net --uts /bin/bash
mytx:~ # echo $$
20334
mytx:~ # nsenter -t 25512 -u -n /bin/bash 
netns1:~ #
netns1:~ # lsns -t net -t uts -p $$
        NS TYPE NPROCS   PID USER COMMAND
4026540975 uts       3 25512 root /bin/bash
4026540977 net       3 25512 root /bin/bash
netns1:~ # exit
mytx:~ # hostname
mytx

Note the "-u" in the command line where we set the hostname! Note further the change of the hostname in the prompt! In more complex scenarios, this little trick may help you to keep an overview over which namespace we are currently working in.

veth-devices

For container technology "veth" devices are of special importance. A veth device has two associated Ethernet interfaces - so called "peer" interfaces. One can imagine these interfaces like linked by a cable on OSI level 2 - a packet arriving at one interface gets available at the other interface, too. Even if one of the interfaces has no IP address assigned.

This feature is handy when we e.g. need to connect a host or a virtualized guest to an IP-less bridge. Or we can use veth-devices to uplink several bridges to one another. See a former blog post
Fun with veth devices, Linux virtual bridges, KVM, VMware – attach the host and connect bridges via veth
about these possibilities.

As a first trial we will assign the veth device and both its interfaces to one and the same network namespace. Most articles and books show you how to achieve this by the use of the "ip" command with an option for a "named" namespace. In most cases the "ip" command would have been used to create a named net-ns by something like

ip netns add NAME

where NAME is the name we explicitly give to the added network namespace. When such a named net-ns exists we can assign an Ethernet interface named "ethx" to the net-ns by:

ip link set ethx netns NAME

However, in all our previous statements no NAME for a network namespace has been used so far. So, how to achieve something similar for unnamed network namespaces? A look into the man pages helps: The "ip" command allows the introduction of a PID together with the option parameter "netns" at least for the variant "ip link set". Does this work for veth devices and the command "ip link add", too? And does it work for both Ethernet interfaces?

In the example discussed above we had a namespace 4026541170 of process with PID 15180. We open a bash shell on our host mylx, where PID 15150 still runs in the background, and :

mylx:~ # echo $$
27977
mylx:~ # lsns -t net
        NS TYPE NPROCS   PID USER  COMMAND
4026531963 net     393     1 root  /usr/lib/systemd/systemd --switched-root --system --deserialize 24
4026540983 net      23  4634 root  /sbin/init
4026541170 net       1 15150 root  /bin/bash
mylx:~ # ip link add veth1 netns 15150 type veth peer name veth2 netns 15150
mylx:~ # nsenter -t 15150 -n /bin/bash
mylx:~ # echo $$
28350
mylx:~ # ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: veth2@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 8e:a0:79:28:ae:12 brd ff:ff:ff:ff:ff:ff
3: veth1@veth2: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether fa:1e:2c:e3:00:8f brd ff:ff:ff:ff:ff:ff
mylx:~ # 

Success! Obviously, we have managed to create a veth device with both its 2 interfaces inside the network namespace associated with our background process of PID 15150.

The Ethernet interfaces are DOWN - but this was to be expected. So far, so good. Of course it would be more interesting to position the first veth interface in one network namespace and the second interface in another network namespace. This would allow network packets from a container to cross the border of the container's namespace into an external one. Topics for the next articles ...

Summary and outlook

Enough for today. We have seen how we can list (network) namespaces and associated processes. We are able to create shells together with and inside in a new network namespace. And we can open a shell that can be attached to an already existing network namespace. All without using a "NAME" of the network namespace! We have also shown how a veth device can be added to a specific network namespace. We have a set of tools now, which we can use in more complicated virtual network experiments.

In the next article

Fun with veth-devices, Linux bridges and VLANs in unnamed Linux network namespaces – II

I shall present a virtual network environment for several interesting experiments with network namespaces - or containers, if you like. Further articles will discuss such experiments step by setp.

Links

Introduction into network namespaces
http://www.linux-magazin.de/Ausgaben/2016/06/Network-Namespaces

Using unshare without root-privileges
https://unix.stackexchange.com/questions/252714/is-it-possible-to-run-unshare-n-program-as-an-unprivileged-user
https://bbs.archlinux.org/viewtopic.php?id=205240
https://blog.mister-muffin.de/2015/10/25/unshare-without-superuser-privileges/