slingamn/namespaced-openvpn

protected namespace inherits a default iptables config that is wide open

69xDEADBEEF opened this issue · 22 comments

Hi

Thanks for writing this and for all the notes. I've integrated it into my script to connect to my VPN provider. It works really well.

Is there some way to automate the loading of my custom firewall script and overwrite the default one the protected name-space inherits?

At the moment I pull the ethernet cable out while openvpn is trying to connect, check the iptables rules... sometimes they have reverted to the wide-open default, other times they haven't... and if necessary flush the rules, and read my custom rules back in. After that it's fine. It only seems to happen when I open the first namespace. From then on it's fine.

But otherwise this works very well. As far as I am able to tell.

Glad you like it!

The apparently nondeterministic behavior here is odd. When you say "sometimes they have reverted to the wide-open default", are you talking about the root namespace, or the protected namespace?

Each network namespace is a fresh copy of the network stack, including independent iptables rules.
You can run arbitrary commands on tunnel start/stop using openvpn's scripting functionality, e.g., --up and --down. Probably the easiest thing is to have a --up script that flushes all the rules and then reapplies them. You'll need to explicitly force the script to execute in the protected namespace with ip netns exec protected.

(It's not totally clear that you need the same firewall rules in the protected namespace as in the root namespace, given the lack of LAN access --- you may want to write a different set of rules.)

Thanks for the prompt reply. And thanks too for pointing out the oddity of what I was describing. It conformed my suspicions and so I dug a little deeper and discovered there were was a problem with iptables pulling in a legacy iptables rule-set on reboot, and this behaviour being duplicated when making a namespace, such that the protected namespace opens with a default iptables rule-set, and root mischievously reverts to the legacy rule-set. It had my head spinning and I was overthinking it, and now have it fixed.

I'm having root with normal internet access and namespace with openvpn access but just for testing. As you advise in your main body of text, best to have root locked down in most use cases once the protected namespace is up and running..

Thanks for the pointers with regards firewalling the protected namespace. I'll spend some time getting that working. I'm enjoying the learning curve, having come to Bash a few months ago specifically to make a custom script to connect to a commercial VPN instead of using Network Manager, which doesn't support the latest OpenSSL.

Agree that the protected namespace requires more locked-down rules. I have that covered!

Thanks again.

Apologies if I haven't been lucid. I'm learning as I go.

I've run into a problem. If I lock down the root namespace (with an IPtables rool-set that only allows a connection to my local network) it blocks the protected namespace from establishing or maintaining a connection.

It's not a big problem but I would have preferred to have the protected namespace connecting only externally and a root namespace connecting only internally (specifically to access my router gui).

I've made an Iptables rule-set that locks down the root namespace, but it blocks the protected namespace connection.

It's not absolutely vital that I have a locked-down root namespace. Preferred, but not essential, as I came across your scripts after learning that it's not possible to firejail openvpn, and then looking for another solution. And yours is a solution to that problem.

Thanks once more. Apologies in advance if I'm making a naive error. Looks like I have some reading to do...

If I understood correctly, the openvpn process is still running in the root namespace, so it's failing to connect to the VPN server (which is on an external network)? Does openvpn actually start successfully? The openvpn process itself will typically need WAN access.

*filter
-P FORWARD DROP
-A INPUT -m state --state INVALID -j DROP
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -s 192.168.1.1 -j ACCEPT
-A OUTPUT -d 192.168.1.1 -j ACCEPT
-P INPUT DROP
-P OUTPUT DROP
COMMIT


METHOD 1
Lock down the root namespace with below IPTables rule-set, allowing access only to the router's config on the LAN. Next, launch a Namespaced-OpenVPN instance. Problem: can't connect to the WAN, I think because we're launching from root, and root is now locked down by the below rule-set.

METHOD 2
Launch Namespaced-OpenVPN. It connects to the WAN. Next, lock down the root namespace with below IPTables rule-set. The Namespaced-OpenVPN instance immediately loses its connection. I have no idea why the dominoes fall in reverse, so to speak.

I'm sure you've written somewhere about this issue. I confirm it happens!

Otherwise, everything works perfectly (setting aside METHODS 1 & 2) and I apply the hardening techniques you recommend in the README.

e00E commented

I would also like to automatically apply firewall settings in the new namespace. In my case this is nftables and not iptables.

Each network namespace is a fresh copy of the network stack, including independent iptables rules.
You can run arbitrary commands on tunnel start/stop using openvpn's scripting functionality, e.g., --up and --down. Probably the easiest thing is to have a --up script that flushes all the rules and then reapplies them. You'll need to explicitly force the script to execute in the protected namespace with ip netns exec protected.

Thank you for this suggestion. The nitpick I have is that I would like to apply my rules before the vpn connects (before anything can possible communicate over it) which is not the case using an up-script. So the namespace would be created first, then the firewall rules are applied, then the vpn connects.
I can probably create the namespace manually first to apply my rules and then run namespaces-openvpn so it is an easy problem to overcome but maybe you still want to include a pre-vpn script.

@e00E sorry about the delay. Looking at the "scripting" section in man 8 openvpn, my understanding is that the --up script is run by openvpn before --route-up. And namespaced-openvpn already calls setup_namespace, which ensures that the protected namespace exists and has no adapters, before it execs openvpn. So it seems to me like it should be safe to set up the firewall rules with an --up script, because the adapter will only be assigned an address and routes during --route-up execution. The order of events should be:

  1. namespaced-openvpn calls setup_namespace. If the namespace does not exist, it is created. If it exists and is nonempty (i.e., already has connectivity), it fails and exits.
  2. namespaced-openvpn execs openvpn
  3. openvpn creates the tun adapter (in the root namespace) and runs the --up script
  4. openvpn authenticates to the remote server and receives pushed options
  5. openvpn runs its --route-up script, which in our case is namespaced-openvpn again: this transfers the tun adapter into the protected namespace and assigns the pushed address and route

Thoughts?

e00E commented

You're right. I misunderstood when the up script runs.

I now have this working. It was trial and error to get a solution. If there is a reference to --up in an openvpn config file you are using, it'll cause a conflict. Apparently only one --up command is permitted.

After commenting out the --up command in the config file it worked.

I then add an --up command calling a bash script that flushes the firewall in the protected namespace, and reads in some custom rules from a file.

That bash script looks like this:

#!/bin/sh
sudo ip netns exec protected sudo -u person sudo iptables -F
sudo ip netns exec protected sudo -u person sudo iptables -X
sudo ip netns exec protected sudo -u person sudo iptables -t nat -F
sudo ip netns exec protected sudo -u person sudo iptables -t nat -X
sudo ip netns exec protected sudo -u person sudo iptables -t mangle -F
sudo ip netns exec protected sudo -u person sudo iptables -t mangle -X
sudo ip netns exec protected sudo -u person sudo iptables-restore -c < /path/to/custom_firewall_rules
sudo ip netns exec protected sudo -u person sudo iptables -P INPUT DROP
sudo ip netns exec protected sudo -u person sudo iptables -P FORWARD DROP

The protected namespace is thus established with a a set of custom firewall rules.

Thanks. I think that pretty much closes this issue.

I added a note about firewalling with --up to the readme.

I now have this working. It was trial and error to get a solution. If there is a reference to --up in an openvpn config file you are using, it'll cause a conflict. Apparently only one --up command is permitted.

After commenting out the --up command in the config file it worked.

I then add an --up command calling a bash script that flushes the firewall in the protected namespace, and reads in some custom rules from a file.

That bash script looks like this:

#!/bin/sh
sudo ip netns exec protected sudo -u person sudo iptables -F
sudo ip netns exec protected sudo -u person sudo iptables -X
sudo ip netns exec protected sudo -u person sudo iptables -t nat -F
sudo ip netns exec protected sudo -u person sudo iptables -t nat -X
sudo ip netns exec protected sudo -u person sudo iptables -t mangle -F
sudo ip netns exec protected sudo -u person sudo iptables -t mangle -X
sudo ip netns exec protected sudo -u person sudo iptables-restore -c < /path/to/custom_firewall_rules
sudo ip netns exec protected sudo -u person sudo iptables -P INPUT DROP
sudo ip netns exec protected sudo -u person sudo iptables -P FORWARD DROP

The protected namespace is thus established with a a set of custom firewall rules.

Thanks. I think that pretty much closes this issue.

Out of curiosity, what custom rules are you applying? I'm debating on what I need to firewall in the namespace.

Personally I don't think any firewall rules are required in the namespace, so I'm not using any. (The attack surface of the protected namespace is much reduced relative to the root namespace of a normal system, because programs only run there when explicitly opted in.) But at least two people have been interested in adding them, so I thought it was worth documenting.

I now have this working. It was trial and error to get a solution. If there is a reference to --up in an openvpn config file you are using, it'll cause a conflict. Apparently only one --up command is permitted.
After commenting out the --up command in the config file it worked.
I then add an --up command calling a bash script that flushes the firewall in the protected namespace, and reads in some custom rules from a file.
That bash script looks like this:
#!/bin/sh
sudo ip netns exec protected sudo -u person sudo iptables -F
sudo ip netns exec protected sudo -u person sudo iptables -X
sudo ip netns exec protected sudo -u person sudo iptables -t nat -F
sudo ip netns exec protected sudo -u person sudo iptables -t nat -X
sudo ip netns exec protected sudo -u person sudo iptables -t mangle -F
sudo ip netns exec protected sudo -u person sudo iptables -t mangle -X
sudo ip netns exec protected sudo -u person sudo iptables-restore -c < /path/to/custom_firewall_rules
sudo ip netns exec protected sudo -u person sudo iptables -P INPUT DROP
sudo ip netns exec protected sudo -u person sudo iptables -P FORWARD DROP
The protected namespace is thus established with a a set of custom firewall rules.
Thanks. I think that pretty much closes this issue.

Out of curiosity, what custom rules are you applying? I'm debating on what I need to firewall in the namespace.

I set the firewall to block all incoming traffic to tun0 that was not requested from the inside.
If you don`t trust the connected vpn network, then this is a good solution to prevent attacks to your computer from the connected vpn network.

I have 1. REQUEST:
When i am using my --up firewall script, each time the vpn did a reconnect i am forced to enter the sudo password again for the --up firewall script. It seems the openvpn instance does not need a new sudo authentification for the reconnect only the called --up firewall script.
Has anybody an idea how to solve this?
I tried to start:
sudo -i ./namespaced-openvpn
( to keep the sudo active, until i close the shell, but that is not working )

That's weird --- as long as namespaced-openvpn and openvpn are running as root, all their scripts should continue to run as root.

-i means "interactive" sudo, i.e., start a shell. Are you sure that's what you want?

That's weird --- as long as namespaced-openvpn and openvpn are running as root, all their scripts should continue to run as root.

-i means "interactive" sudo, i.e., start a shell. Are you sure that's what you want?

I used sudo because this options keeps sudo alive untill the shell window is closed.
But i think it is conflicting with sudo -u.
sudo -i is not working for different user :-(

I am trying to find a solution, that in the executed ./namespaced-openvpn shell window, the system is not asking for sudo password for the firewall script (which is executed in the namespace), when openvpn connection did a reconnect and launches the firewall script again. In this situation i am asked for the sudo password again before openvpn reconnects. Automatic reconnect of openvpn is not working without sudo password for the firewall script.

I agree that allowing tun0 incoming is a good approach to firewalling.

I also get the password request when openvpn goes down and comes back up. This is not a problem for me personally. One work around might be increasing the log in time of sudo sessions. Although doing that perhaps opens up another can of worms.

@69xDEADBEEF could you post your exact command line? (Also, to clarify: this is a password request from sudo, not from openvpn for auth credentials, right?)

Yes, to confirm, I get a password request from sudo. openvpn doesn't complain at all.

My command line is within a bash script, which performs all sorts of shenanigans before finally launching namespaced openvpn with something like this

bash -c "$(echo "sudo ~/Documents/namespaced/namespaced-openvpn --config ~/Documents/crypto/northkorea.ovpn")"

Just to add that prior to calling iptables via the --up command I was not getting password requests. It was only when I moved over to this method that sudo started requesting passwords.

For me, this behaviour was a bonus, as previously if I lost a connection I had to close namespaced openvpn, and start again from the beginning.

I can't reproduce this in my setup. My command line is:

sudo namespaced-openvpn --config ./conf_ip_noscripts --up "/usr/bin/touch /etc/touchtest"

When I SIGHUP the openvpn process, it successfully restarts and reruns the --up command (which requires root), updating the mtime of /etc/touchtest, without prompting me for a sudo password (even after the original sudo grace period has expired).

If you guys get a good reproduction case, could you open a new issue? I don't want to reopen this one, because what we're discussing is no longer related to firewalling.

From discussion on the successor ticket #21, the trick to getting this to work is:

  1. The --up script should invoke ip netns exec without any additional sudo (because openvpn is already running as root), e.g., ip netns exec protected ufw enable
  2. Relative paths (to executables or config files) may cause problems, which can be fixed by switching to absolute paths