Restrict httpd network connections to a specific network interface?

Mark Montague mark at catseye.org
Sun Mar 27 16:21:41 UTC 2011


  I was able to get a working solution.  Dan Walsh provided the clue I 
needed -- the domain attribute (thanks, Dan!)  I'm describing everything 
here in the hope that if someone else needs to use it, they'll be able 
to find it in the list archives.

The requirements are:

1. Allow httpd to serve any requests on ports 80 and 443 from any 
network interface.
2. Allow httpd and its scripts (CGIs) to access any resource (databases, 
back-end content web servers, etc.) on the local machine via the 
loopback interface.
3. Prohibit any other traffic from httpd.  Specifically, do not allow 
any access by either httpd or its scripts to resources on remote 
(untrusted) machines.
4. Do not affect any other service or user running on the machine.

I haven't been able to solve the problem by labeling interfaces, yet -- 
requirement 1 keeps getting in my way.

However, I was able to solve the problem by labeling packets.  First, 
the policy:

# ===== START POLICY =====#
policy_module(secmark, 1.0)

require {
         type httpd_t;
         type httpd_sys_script_t;
         type kernel_t;
         class packet { send recv };
         attribute domain;
}

type httpd_packet_t;
type default_packet_t;
type loopback_packet_t;

# Let httpd send and receive packets which have been labled for it:
# (Permitting kernel_t allows TCP reset packets back to web clients.)
allow httpd_t httpd_packet_t:packet { send recv };
allow kernel_t httpd_packet_t:packet { send recv };

# Let all services send and receive anything via the loopback device:
allow domain loopback_packet_t:packet { send recv };

# Let non-secmark-restricted services send and receive all other packets
# (assuming their targeted policies allow the necessary system calls, ports,
# interfaces, and nodes)
allow { domain -httpd_t -httpd_sys_script_t } default_packet_t:packet { 
send recv };
#===== END POLICY =====#


Next, allow httpd and scripts to make network connections by setting the 
appropriate boolean.  TE rules for this could easily be added to the 
above policy, but it'll hopefully be more obvious to others if we use 
the boolean:

setsebool -P httpd_can_network_connect=on


Finally, add the following iptables rules to label packets:

# set the default actions for the INPUT and OUTPUT chains in the 
"mangle" table
iptables -t mangle -P INPUT ACCEPT
iptables -t mangle -P OUTPUT ACCEPT

# First, label loopback traffic specially so that we can have our SELinux
# policy always accept it.
iptables -t mangle -N LOOPBACK_ACCEPT
iptables -t mangle -A INPUT -i lo -j LOOPBACK_ACCEPT
iptables -t mangle -A OUTPUT -o lo -j LOOPBACK_ACCEPT
iptables -t mangle -A LOOPBACK_ACCEPT -j SECMARK --selctx 
system_u:object_r:loopback_packet_t:s0
iptables -t mangle -A LOOPBACK_ACCEPT -j ACCEPT

# Rules common to multiple services to copy connection labels to established
# and related packets for a connection:
iptables -t mangle -N SECMARK_RESTORE
iptables -t mangle -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED 
-j SECMARK_RESTORE
iptables -t mangle -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED 
-j SECMARK_RESTORE
iptables -t mangle -A SECMARK_RESTORE -j CONNSECMARK --restore
iptables -t mangle -A SECMARK_RESTORE -j ACCEPT

# Allow and label new incoming connections for httpd:
iptables -t mangle -N HTTPD_SECMARK
iptables -t mangle -A INPUT -p tcp -m multiport --dports 80,443 -m 
conntrack --ctstate NEW -j HTTPD_SECMARK
iptables -t mangle -A HTTPD_SECMARK -j SECMARK --selctx 
system_u:object_r:httpd_packet_t:s0
iptables -t mangle -A HTTPD_SECMARK -j CONNSECMARK --save
iptables -t mangle -A HTTPD_SECMARK -j ACCEPT

#...add rules to label packets for other services here...

# Anything else gets the default packet type:
-A INPUT -j SECMARK --selctx system_u:object_r:default_packet_t:s0
-A OUTPUT -j SECMARK --selctx system_u:object_r:default_packet_t:s0


Of course, you'll also need to have rules in the filter table to allow 
incoming traffic to httpd, filter out illegal stuff, and so on -- but 
these are the same filter rules you'd need on any machine, regardless of 
whether you're running SELinux or not, so I won't list them here.

One downside to this solution is that some programs that I used in 
scripts (CGIs) to test the functionality will not notice the AVC denials 
and will endlessly retry, eating CPU and filling the audit log with lots 
of duplicate denials.  curl is one example of such a program.  So far, 
I've been able to work around the endless-retry problem by specifying a 
timeout in either the program or the web server; this isn't ideal, but 
it helps.

If anyone has improvements or alternate ways of meeting the four 
requirements above, that would be welcome.  Otherwise I hope this 
writeup is useful to someone else at some point.

--
   Mark Montague
   mark at catseye.org



More information about the selinux mailing list