Is there a way for non-root processes to bind to “privileged” ports on Linux?

后端 未结 24 1265
予麋鹿
予麋鹿 2020-11-22 02:04

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

24条回答
  •  灰色年华
    2020-11-22 02:49

    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:

    • The daemon listens on 5333, but connections are successfully accepted on 53 thanks to iptables
    • We can include the commands in the unit file itself, and thus we save people headaches. systemd cleans up the firewall rules for us, making sure to remove them when the daemon isn't running.
    • We never run as root, and we make privilege escalation impossible (at least 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.

提交回复
热议问题