I have an Ubuntu 18.04 server in which I've created two virtual interface pairs (veth0a & veth0b) and assigned the end of one interface (veth0b) to a new network namespace (netns0):

ip netns add netns0
ip netns exec netns0 ip link set lo up
ip link add veth0a type veth peer name veth0b
ip link set veth0b netns netns0

I then used firejail to force a specific user (test-user) to use this new namespace by default by setting /usr/bin/firejail as the default shell for this user and by adding the following to the /etc/firejail/login.users file:

test-user: --netns=netns0

I've ran the following test to make sure this works:

  1. Run tshark -i veth0a -f "port 443" from the root account
  2. SSH into the server as test-user
  3. Run curl as SSH user

The tshark output shows the proper veth0b source IP address for the traffic.

The issue I'm running into is when trying to use the test-user account to establish socks5 dynamic port forwarding over SSH:

ssh -D 10000 -q -C -N test-user@server_ip

Running this command from my laptop or workstation allows me to establish a local socks5 server on port 1000 and tunnel it over the SSH connection. Setting this as my local socks5 proxy and going to https://api.ipify.org demonstrates that the proxy is working and that my laptop is using the server's IP address.

The issue is that the sock5 traffic does not appear to be going through the proper namespace. In other words, while browsing the web on my laptop while connected to the socks5 server over the test-user ssh connection my traffic does not appear in tshark -i veth0a.

Is there an additional step needed to ensure the port forwarding tunnel also uses firejail? Does setting the shell in /etc/passwd only impact an interactive login shell? Do i need to also change the user's non-interactive shell to /usr/bin/firejail? If so how would I do that?

Any help would be greatly appreciated.


1 Answer 1


What you're attempting can't work and I'm explaining why.

Your ssh connection connects to the remote server's sshd daemon, which is running on the host, not the network namespace. There an instance is created for your connection. Then you're authenticated, then tunnels are setup, then your user session is setup.

Here's an excerpt from an example run with an ssh client version 7.9p1 using ssh -v -4 -D 10000 remoteuser@server:

debug1: Offering public key: /home/localuser/.ssh/id_ed25519 ED25519 SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
debug1: Server accepts key: /home/localuser/.ssh/id_ed25519 ED25519 SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
debug1: Authentication succeeded (publickey).
Authenticated to server ([]:22).
debug1: Local connections to LOCALHOST:10000 forwarded to remote address socks:0
debug1: Local forwarding listening on port 10000.
debug1: channel 0: new [port listener]
debug1: channel 1: new [client-session]
debug1: Requesting no-more-sessions@openssh.com
debug1: Entering interactive session.
debug1: pledge: network
debug1: client_input_global_request: rtype hostkeys-00@openssh.com want_reply 0
debug1: Sending environment.
debug1: Sending env LANG = C.UTF-8

As you can see, Local connections to LOCALHOST:10000 forwarded to remote address socks:0 ... channel 0: new [port listener] happens first. Then Entering interactive session happens. This would normally call firejail etc., and it would be too late: whatever is done before happens on the host network namespace. But actually in your case, since you're using -N:

-N Do not execute a remote command. This is useful for just forwarding ports.

no shell is ever run, firejail is not run and no alternate network namespace is ever touched. This wouldn't matter anyway even without -N as explained above.

So if you really want to do that, you'll need a separate SOCKS5 proxy, and you can't use the -D option anymore. You can run an instance of sshd or any other SOCKS5-able tool (eg: dante-server) inside the network namespace and do port redirections, using iptable, nftables, or the classic -L option of ssh to redirect to veth0b's IP and this SOCKS5 proxy's listening port, but this will probably require more than just firejail and a normal user. At this point you'd better run the whole setup from systemd, or with a full-blown LXC (or LXD or Docker etc.) container. ip netns features are even often sufficient for this (see: Namespace management with ip netns (iproute2)).

  • Thanks, this is very helpful and explains exactly why what I'm attempting is not working. With that said is it possible to call firejail at authentication or to force all traffic for a specific user to always go through a specific network namespace? Mar 7, 2020 at 23:50
  • You never explained what's your goal. You present what you want to do, but don't tell why you want this. You want to isolate a user, and what this user is allowed to do with tunnels from remote? But why?
    – A.B
    Mar 8, 2020 at 0:10
  • My goal is to have multiple users use this SSH server as a socks5 proxy using dynamic port forwarding. Each user would be assigned a different network namespace which would allow me to monitor and log network traffic for each user separately. Mar 8, 2020 at 2:56
  • You should forget about firejail and use an other method to log socks activity. Even sshd itself when set to loglevel DEBUG will log forwarded ports.
    – A.B
    Mar 8, 2020 at 3:52
  • I am currently using tshark to log traffic which works well. For example if I monitor one end of the veth pair with tshark it gives me all traffic from within the namespace. Firejail aside I guess my question is: is it possible to assign each user with their own default network namespace so that all traffic for a given user always goes through that namespace? Mar 8, 2020 at 4:00

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .