It\'s very annoying to have this limitation on my development box, when there won\'t ever be any users other than me.
I\'m aware of the standard workarounds, but non
At startup:
iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080
Then you can bind to the port you forward to.
Bind port 8080 to 80 and open port 80:
sudo iptables -t nat -A OUTPUT -o lo -p tcp --dport 80 -j REDIRECT --to-port 8080
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
and then run program on port 8080 as a normal user.
you will then be able to access http://127.0.0.1
on port 80
Since the OP is just development/testing, less than sleek solutions may be helpful:
setcap can be used on a script's interpreter to grant capabilities to scripts. If setcaps on the global interpreter binary is not acceptable, make a local copy of the binary (any user can) and get root to setcap on this copy. Python2 (at least) works properly with a local copy of the interpreter in your script development tree. No suid is needed so the root user can control to what capabilities users have access.
If you need to track system-wide updates to the interpreter, use a shell script like the following to run your script:
#!/bin/sh
#
# Watch for updates to the Python2 interpreter
PRG=python_net_raw
PRG_ORIG=/usr/bin/python2.7
cmp $PRG_ORIG $PRG || {
echo ""
echo "***** $PRG_ORIG has been updated *****"
echo "Run the following commands to refresh $PRG:"
echo ""
echo " $ cp $PRG_ORIG $PRG"
echo " # setcap cap_net_raw+ep $PRG"
echo ""
exit
}
./$PRG $*
TLDR: For "the answer" (as I see it), jump down to the >>TLDR<< part in this answer.
OK, I've figured it out (for real this time), the answer to this question, and this answer of mine is also a way of apologizing for promoting another answer (both here and on twitter) that I thought was "the best", but after trying it, discovered that I was mistaken about that. Learn from my mistake kids: don't promote something until you've actually tried it yourself!
Again, I reviewed all the answers here. I've tried some of them (and chose not to try others because I simply didn't like the solutions). I thought that the solution was to use systemd
with its Capabilities=
and CapabilitiesBindingSet=
settings. After wrestling with this for some time, I discovered that this is not the solution because:
Capabilities are intended to restrict root processes!
As the OP wisely stated, it is always best to avoid that (for all your daemons if possible!).
You cannot use the Capabilities related options with User=
and Group=
in systemd
unit files, because capabilities are ALWAYS reset when execev
(or whatever the function is) is called. In other words, when systemd
forks and drops its perms, the capabilities are reset. There is no way around this, and all that binding logic in the kernel is basic around uid=0, not capabilities. This means that it is unlikely that Capabilities will ever be the right answer to this question (at least any time soon). Incidentally, setcap
, as others have mentioned, is not a solution. It didn't work for me, it doesn't work nicely with scripts, and those are reset anyways whenever the file changes.
In my meager defense, I did state (in the comment I've now deleted), that James' iptables suggestion (which the OP also mentions), was the "2nd best solution". :-P
>>TLDR<<
The solution is to combine systemd
with on-the-fly iptables
commands, like this (taken from DNSChain):
[Unit]
Description=dnschain
After=network.target
Wants=namecoin.service
[Service]
ExecStart=/usr/local/bin/dnschain
Environment=DNSCHAIN_SYSD_VER=0.0.1
PermissionsStartOnly=true
ExecStartPre=/sbin/sysctl -w net.ipv4.ip_forward=1
ExecStartPre=-/sbin/iptables -D INPUT -p udp --dport 5333 -j ACCEPT
ExecStartPre=-/sbin/iptables -t nat -D PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
ExecStartPre=/sbin/iptables -A INPUT -p udp --dport 5333 -j ACCEPT
ExecStartPre=/sbin/iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
ExecStopPost=/sbin/iptables -D INPUT -p udp --dport 5333 -j ACCEPT
ExecStopPost=/sbin/iptables -t nat -D PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
User=dns
Group=dns
Restart=always
RestartSec=5
WorkingDirectory=/home/dns
PrivateTmp=true
NoNewPrivileges=true
ReadOnlyDirectories=/etc
# Unfortunately, capabilities are basically worthless because they're designed to restrict root daemons. Instead, we use iptables to listen on privileged ports.
# Capabilities=cap_net_bind_service+pei
# SecureBits=keep-caps
[Install]
WantedBy=multi-user.target
Here we accomplish the following:
iptables
systemd
cleans up the firewall rules for us, making sure to remove them when the daemon isn't running.systemd
claims to), supposedly even if the daemon is compromised and sets uid=0
.iptables
is still, unfortunately, quite an ugly and difficult-to-use utility. If the daemon is listening on eth0:0
instead of eth0
, for example, the commands are slightly different.
Use the privbind utility: it allows an unprivileged application to bind to reserved ports.
For some reason no one mention about lowering sysctl net.ipv4.ip_unprivileged_port_start to the value you need. Example: We need to bind our app to 443 port.
sysctl net.ipv4.ip_unprivileged_port_start=443
Some may say, there is a potential security problem: unprivileged users now may bind to the other privileged ports (444-1024). But you can solve this problem easily with iptables, by blocking other ports:
iptables -I INPUT -p tcp --dport 444:1024 -j DROP
iptables -I INPUT -p udp --dport 444:1024 -j DROP
Comparison with other methods. This method:
Depending on the situation, I would choose between sysctl, CAP, authbind and iptables-redirect. And this is great that we have so many options.