FreeBSD Operating System

Firewalls, IP aliasing and proxies

Разбить на страницы
Показывать лекцию целиком

The Internet was developed by a relatively small community of computer scientists, who were for the most part responsible people who often did not take security issues very seriously. Since the Internet has been opened to the general public, three problems have become evident:

  • A large number of people have sought to abuse its relatively lax security.
  • The address space is no longer adequate for the number of machines connecting to the network.
  • Much bandwidth is used by people downloading the same web pages multiple times.
  • What do these problems have to do with each other? Nothing much, but the solutions are related, so we'll look at them together. More specifically, we'll consider:

  • How to set up an Internet firewall to keep intruders out of your network.
  • Security tools that ensure that nobody can steal your password from a node through which it passes.
  • Tools for IP aliasing, which translate IP addresses to make them appear to come from the gateway machine. The way this is done makes it impossible to set up connections from outside, so they also represent a kind of security device.
  • Caching proxy servers, which both address the multiple download issues and provide some additional security.
  • Security and firewalls

    Recall from Chapter 16 that incoming packets need to connect to an IP port, and that some process on the machine must accept them. By default, this process is inetd. You can limit the vulnerability of your machine by limiting the number of services it supports. Do you need to supply telnet and rlogin services? If not, don't enable the service. By default, /etc/inetd.conf no longer enables any services, so this should not be a problem. Obviously, careful system configuration can minimize your vulnerability, but it also reduces your accessibility: intruders can't get in, but neither can the people who need to access the machine.

    A better solution is a tool that passes authorized data and refuses to pass unauthorized data. Such a tool is called a firewall. In this section, we'll look at packet filtering firewalls: the firewall examines each incoming packet and uses a set of predefined walls to decide whether to pass it unchanged, change it, or simply discard it. An alternative approach is a proxy firewall, which analyzes each packet and creates new requests based on its content. On page 396 we'll look at squid, a caching proxy server that provides some of this functionality.

    FreeBSD supports three different firewalls, ipf, ipfilter and ipfw. We consider ipfw here; you can find details about ipf and ipfilter and in the respective man pages.

    The DEFAULT FreeBSD kernel does not include firewall support. If you wish, you can build a kernel with firewall support—see the file /usr/src/sys/conf/NOTES for a list of parameters—but you don't need to build a new kernel. You can load the KLD /boot/kernel/ipfw.ko instead:

    # kldload ipfw
    ipfw2 initialized, divert disabled, rule-based forwarding enabled, default to deny, logging disabled
    

    Before you do so, make sure you have direct local connection to the system. If you start this command remotely, you will instantly lose access to the system. Read the following section before loading the firewall.

    ipfw: defining access rules

    The program ipfw processes access rules for the firewall. Each rule relates to specific kinds of packet and describes what to do with them. On receiving a packet, ipfw examines each rule in a predetermined order until it finds one which matches the packet. It then performs the action that the rule specifies. In most cases, the rule accepts or denies the packet, so ipfw does not need to continue processing the remaining rules, though sometimes processing can continue after a match. If no rule matches, the default is to allow no traffic.

    ipfw packet types
    KeywordDescription
    ipAll IP packets.
    tcpTCP packets.
    udpUDP packets.
    icmpICMP packets.
    service name or numberA packet destined for one of the services described in /etc/services.
    src IP address rangeA packet with a source address that matches IP address. See below for the interpretation of IP address range.
    dst IP address rangeA packet with a destination address that matches IP address.
    via interfaceAll packets going by the specified interface. interface may be an interface name or an IP address associated with only one interface.
    recv interfaceAll packets arriving by the specified interface. interface may be an interface name or an IP address associated with only one interface.
    xmit interfaceAll packets going out by the specified interface. interface may be an interface name or an IP address associated with only one interface.
    IP addressThis is an IP address. It specifies a match for exactly this address.
    IP address/bitsbits is a value between 0 and 32. This form matches the first bits bits of IP address.
    IP address: maskmask is a 32-bit value. This form matches those bits of IP address specified in mask. This is the same concept as a net mask—see Chapter 16 , page 290, for a description of net masks.

    Table 22-1 shows the keywords you can use to define the packets and the forms that the IP address range can take:

    These options can be combined with a few restrictions:

  • The recv interface can be tested on either incoming or outgoing packets, while the xmit interface can be tested only on outgoing packets. This means that you must specify the keyword out (and you may not specify in) when you use xmit. You can't specify via together with xmit or recv.
  • A packet that originates from the local host does not have a receive interface. A packet destined for the local host has no transmit interface.
  • Actions

    When ipfw finds a rule which matches a packet, it performs the specified action. Table 22-2 shows the possibilities.

    Actions on packets
    KeywordDescription
    allowAllow a packet to pass. Stop processing the rules.
    denyDiscard the packet. Stop processing the rules.
    unreachDiscard the packet and send an ICMP host unreachable message to the sender. Stop processing the rules.
    resetDiscard the packet and send a TCP reset message. This can apply only to TCP packets. Stop processing the rules.
    countCount the packet and continue processing the rules.
    divert portDivert the packet to the divert socket bound to port port. See the man page ipfw(8) for more details. Stop processing the rules.
    tee portSend a copy of the packet to the divert socket bound to port port. Continue processing the rules.
    skipto ruleContinue processing the rules at rule number rule.

    Writing rules

    The sequence in which rules are applied is not necessarily the sequence in which they are read. Instead, each rule can have a line number between 1 and 65534. Rules are applied from the lowest to the highest line number. If you enter a rule without a line number, however, it receives a number 100 higher than the previous rule.

    The highest-numbered rule is number 65535, which is always present. Normally it has the form:

    65535 deny all from any to any
    

    In other words, if no other rules are present, or they don't match the packet, ipfw drops the packet. If you build a kernel with the option IPFIREWALL_DEFAULT_TO_ACCEPT, this rule changes to its opposite:

    65535 allow all from any to any
    

    These two rule sets implicitly illustrate two basic security strategies. You may note parallels to certain political systems:

  • The first takes the attitude "everything is forbidden unless explicitly allowed."
  • The second takes the attitude "everything is allowed unless explicitly forbidden."
  • It goes without saying that the first policy is safer. If you make a mistake with the first (more restrictive) rule set, you're more likely to lock people out of your system accidentally than you are to let them in when you don't want them.

    Configuration files

    The main configuration file is /etc/rc.firewall. It's unlikely to match your needs exactly. There are two possibilities:

  • You can edit it to match your requirements.
  • You can create your own configuration file with your rules.
  • Which do you choose? If you're making only minor modifications, it's easier to edit it. If you're taking things seriously, though, you'll end up with something that no longer bears much of a relationship with the original file. Upgrades are easier if you have your own file.

    If you create your own file, you can tell /etc/rc.conf its name, and /etc/rc.firewall will read it. Either way, the rules are the same. In the following section we'll go through the default /etc/rc.firewall file. There's nothing stopping you from copying them to another file and editing them to match your requirements.

    Reading the file is somewhat complicated by a number of environment variables that are set in the system startup scripts. We'll see the important ones below. It's also helpful to know that ${fwcmd} gets replaced by the name of the firewall program, /sbin/ipfw. The other ones are described in /etc/default/rc.conf:

    firewall_enable="NO"                 # Set to YES to enable firewall functionality
    firewall_script="/etc/rc.firewall"   # Which script to run to set up the firewall
    firewall_type="UNKNOWN"              # Firewall type (see /etc/rc.firewall)
    firewall_quiet="NO"                  # Set to YES to suppress rule display
    firewall_logging="NO"                # Set to YES to enable events logging
    firewall_flags=""                    # Flags passed to ipfw when type is a file
    

    To set up the firewall, first decide the kind of profile you need and set the variable firewall_type accordingly. The current version of /etc/rc.firewall defines four kinds of usage profile:

  • The open profile is effectively a disabled firewall. It allows all traffic. You might use this if you're having trouble setting up the firewall and need to disable it temporarily.
  • The client profile is a good starting point for a system that does not provide many publicly accessible services to the Net. We'll look at it in the next section.
  • The simple profile, despite its name, is intended for a system that does provide a number of publicly accessible services to the Net.
  • The closed profile allows only local traffic via the loopback interface.
  • In addition, you can set firewall_type to the name of a file describing the firewall configuration.

    All configurations start with a call to setup_loopback, which adds the following rules:

    ${fwcmd} add 100 pass all from any to any via lo0
    ${fwcmd} add 200 deny all from any to 127.0.0.0/8
    ${fwcmd} add 300 deny ip from 127.0.0.0/8 to any
    

    These rules allow all local traffic and stop traffic coming in with a fake local address.

    The client profile

    At the beginning of the client profile you'll find a number of variables you need to set. In the following example they're set to match http://freebie.example.org and our example network:

    [Cc][Ll][Ii][Ee][Nn][Tt])
       ############
       #This is a prototype setup that will protect your system somewhat against
       #people from outside your own network.
       ############
       #set these to your network and netmask and ip
       net="223.147.37.0"
       mask="255.255.255.0"
       ip="223.147.37.1"                freebie.example.org
    

    The first line matches the text client whether written in upper or lower case. Then we have:

    setup_loopback
    #Allow any traffic to or from my own net.
    ${fwcmd} add pass all from ${ip} to ${net}:${mask}
    ${fwcmd} add pass all from ${net}:${mask} to ${ip}
    

    These rules allow any traffic in the local network.

    #Allow TCP through if setup succeeded
    ${fwcmd} add pass tcp from any to any established
    

    If a TCP connection has already been established, allow it to continue. Establishing a TCP connection requires other rules, which we shall see below.

    #Allow IP fragments to pass through
    ${fwcmd} add pass all from any to any frag
    

    Fragmented packets are difficult to recognize, and if we block them, strange things might happen. They're usually not a significant security risk.

    #Allow setup of incoming email
    ${fwcmd} add pass tcp from any to ${ip} 25 setup
    #Allow setup of outgoing TCP connections only 
    ${fwcmd} add pass tcp from ${ip} to any setup
    #Disallow setup of all other TCP connections 
    ${fwcmd} add deny tcp from any to any setup
    

    The preceding three rules allow external systems to establish a TCP connection for delivering mail (first rule), but nothing else (third rule). The second rule allows setup of TCP connections to the outside world.

    #Allow DNS queries out in the world
    ${fwcmd} add pass udp from ${ip} to any 53 keep-state
    #Allow NTP queries out in the world
    ${fwcmd} add pass udp from ${ip} to any 123 keep-state #Everything else is denied as default.
    

    These two rules allow DNS and NTP queries. The keyword keep-state causes ipfw to build a short-lived dynamic rule matching this particular combination of end points and protocol. This means that we don't need to open traffic in the other direction. Previously, the rule set for DNS queries consisted of these two rules:

    $fwcmd add pass udp from any 53 to ${ip} 
    $fwcmd add pass udp from ${ip} to any 53
    

    This allows all DNS traffic in both directions. By contrast, keep-state allows only the reply traffic for specific queries to pass the firewall. You don't need this for TCP—the established keyword does the same thing—but UDP doesn't have the concept of a connection, so the firewall needs to keep track of the traffic.

    There are no more rules, so the default deny rule prevents any other kind of traffic.

    The simple profile

    Despite the name, the , so we'll use its addresses.

    #set these to your outside interface network and netmask and ip oif="tun0"
    onet="139.130.136.133"
    omask="255.255.255.255"
    oip="139.130.136.133"
    #set these to your inside interface network and netmask and ip iif="ep0"
    inet="223.147.37.0"
    imask="255.255.255.0"
    iip="223.147.37.0"
    

    These addresses and networks correspond to the PPP link and the local ethernet, respectively.

    #Stop spoofing
    ${fwcmd} add deny all from ${inet}:${imask} to any in via ${oif} 
    ${fwcmd} add deny all from ${onet}:${omask} to any in via ${iif}
    

    These two rules stop any packets purporting to come from the local network that arrive via the external network, and any packets purporting to come from the remote network that arrive via the local interface. These packets would have been faked, an action known as spoofing.

    #Stop RFC1918 nets on the outside interface 
    ${fwcmd} add deny all from any to 10.0.0.0/8 via ${oif} 
    ${fwcmd} add deny all from any to 172.16.0.0/12 via ${oif} 
    ${fwcmd} add deny all from any to 192.168.0.0/16 via ${oif}
    

    RFC 1918 defines networks that should not be routed. These rules enforce that requirement.

    At this point in the file there are also some other addresses that should not be routed. A check is made for address translation, because non-routed addresses are typically used by NAT environments.

    #Allow TCP through if setup succeeded
    ${fwcmd} add pass tcp from any to any established
    #Allow IP fragments to pass through 
    ${fwcmd} add pass all from any to any frag
    #Allow setup of incoming email
    ${fwcmd} add pass tcp from any to ${oip} 25 setup
    #Allow access to our DNS
    ${fwcmd} add pass tcp from any to ${oip} 53 setup
    ${fwcmd} add pass udp from any to ${oip} 53
    ${fwcmd} add pass udp from ${oip} 53 to any
    #Allow access to our WWW
    ${fwcmd} add pass tcp from any to ${oip} 80 setup
    

    These rules add to what we saw for the client profile: in addition to email, we allow incoming DNS and WWW connections.

    #RejectLog all setup of incoming connections from the outside 
    ${fwcmd} add deny log tcp from any to any in via ${oif} setup
    #Allow setup of any other TCP connection 
    ${fwcmd} add pass tcp from any to any setup
    

    Here, we don't just reject TCP setup requests from the outside world, we log them as well.

    #Allow DNS queries out in the world
    ${fwcmd} add pass udp from ${oip} to any 53 keep-state 
    #Allow NTP queries out in the world
    ${fwcmd} add pass udp from ${oip} to any 123 keep-state 
    #Everything else is denied as default.
    

    Finally, we allow DNS and NTP queries via UDP, and deny everything else from the outside world.

    user-defined profiles

    If the profile isn't one of the recognized keywords, /etc/rc.firewall checks if there's a file with that name. If so, it uses it as a command file to pass to ipfw:

    elif [ "${firewall}" != "NONE" -a -r "${firewall}" ]; 
       then ${fwcmd} ${firewall_flags} ${firewall_type}
    

    Note that you can't put comment lines in the file defined by ${firewall}.

    Entries in /etc/rc.conf

    When you have decided what kind of firewall configuration best suits your network, note that fact in /etc/rc.conf. Set the value of firewall_enable to YES to enable the firewall, and the value of firewall_type to indicate the type of firewall. For our example network, client is probably the most appropriate type:

    firewall_enable="YES"                # Set to YES to enable firewall functionality 
    firewall_script="/etc/rc.firewall"   # Which script to set up the firewall 
    firewall_type="client"               # Firewall type (see /etc/rc.firewall)
    

    If you have decided to write your own file rather than modify /etc/rc.firewall, set firewall_type to the name of the file.

    Trying it out

    You'll probably find that your first attempt at firewall configuration won't be the optimum. You'll probably discover requirements that you hadn't thought of and that are now being denied by the default rule. Be prepared to spend some time getting everything to work, and do this at the system console. There's no good alternative.

    IP aliasing

    In our reference network on page 294, we assumed that our local network had a valid assigned IP address. Sometimes, this isn't possible. In fact, in the Real World it's pretty well impossible to get a complete class C network for a system with only five systems on it. You have the alternative of getting a subset of a class C network (in this case, eight addresses would do) from your ISP, or using just one address and running software that makes all traffic from the network to the outside world look as if it's coming from that system. The latter approach, called network address translation (NAT) or IP aliasing, can be significantly cheaper: ISPs often charge good money for additional addresses. On the down side, NAT restricts you in some ways. Any connection between a machine on a NAT network and the global Internet must start from the machine on the NAT network, because the translation doesn't exist until the connection is set up. This also means that you can't connect two machines on different NAT networks.

    Network address translation involves three machines: one on the global Internet with real Internet addresses, one on a private subnet with unroutable addresses, and one in the middle that performs the translation. In our reference network (see page 294), let's consider connecting might look like this:

    (рис 22.1) Accessing the Web via NAT

    In this diagram, the IP addresses are above the boxes, the interface names above the connection lines, and the port numbers below the connection lines. The connection must be started by (216.136.204.117) and sends it out its default route, in this case the only interface, wi0. presto gets the packet on interface xl0 and routes it out through interface dc0.

    So far, that's nothing special—it's what any router does. The difference is that presto changes the source address and port number. On the wireless link, andante's address is 192.168.27.17, and the port number for this connection is 2731. On the remote link, the IP address becomes 223.147.37.2, presto's own address, and the port number in this case is 3312. Theoretically, the "changed" port address could be the same as the original, since there is no relationship. The destination IP address and port number can't change, of course, or the packet would never get to its destination.

    On return, the reverse happens: http://www.FreeBSD.org replies to presto, which recognizes the port number, converts it to andante's IP address and source port, and sends it to andante on the local network.

    IP aliasing software

    There are a number of ways to perform IP aliasing with FreeBSD. If you're connecting to the outside world via User PPP (see Chapter 20, page 348), you can use the -alias keyword to tell PPP to alias all packets coming from the network to the address of the tunnel interface. In our reference network, this would be the address 139.130.136.133.

    This particular form of IP aliasing has some limitations: it works only for a single User PPP connection to the outside world, and it's global in its functionality. One alternative is the Network Address Translation Daemon, or natd, which uses divert sockets to translate addresses. It works well in conjunction with the firewall software we looked at above.

    natd

    To set up natd for the example above, perform the following steps:

  • Even if you don't plan to run an IP firewall, build and boot a custom kernel with the following options:
    options IPFIREWALL
    options IPDIVERT
    

    If you are running a firewall, configure the firewall normally, but be sure to include the IPDIVERT option.

  • Make sure your interfaces are running. For example, if you're running Kernel PPP and you want to specify ppp0 as your interface, start pppd before starting natd.
  • What you do next differs a little depending on whether you are also running a firewall or not. If you're not, you're better off with a separate script, which you might call /etc/rc.nat, with the following content:
    /sbin/ipfw -f flush
    /sbin/ipfw add divert natd all from any to any via dc0
    /sbin/ipfw add pass all from any to any
    
  • If you want to combine NAT with real firewall rules, you need only the second line of the previous example. Set up the firewall as described above, and put the NAT line at the start of the section of /etc/rc.firewall that you have chosen, so that natd sees all packets before they are dropped by the firewall. After natd translates the IP addresses, the firewall rules are run again on the translated packet, with the exception of the divert rules. The client configuration is the most likely one to suit your needs if you're using NAT. After the example in listing 22-1 on 390, you might add:
    #set these to your network and netmask and ip
    net="192.0.2.0"
    mask="255.255.255.0"
    ip="192.0.2.1"
    setup_loopback
    /sbin/ipfw add divert natd all from any to any via dc0
    #Allow any traffic to or from my own net. 
    ${fwcmd} add pass all from ${ip} to ${net}:${mask} 
    ${fwcmd} add pass all from ${net}:${mask} to ${ip}
    
  • Add the following to /etc/rc.conf:
    firewall_enable=YES
    gateway_enable="YES"        # Set to YES if this host is a gateway.
    natd_enable="YES"
    natd_interface="dc0"
    firewall_script="/etc/rc.nat"  # script for NAT only
    firewall_type="client"           # firewall type if running a firewall
    

    The interface name in the second line, dc0, is the name of the external interface (the one with the real IP addresses).

    If you're using NAT but not a firewall, you don't need to specify a firewall_type, because that relates to /etc/rc.firewall. You do need to specify the name of the script to run, however.

  • Enable your firewall as shown above in the firewall section. If you don't intend to reboot now, just run /etc/rc.firewall (or /etc/rc.natd) by hand from the console, and then start natd:
    sh /etc/rc.nat                                            for NAT only
    firewall_type=client sh /etc/rc.firewall   for NAT and firewall
    natd dc0
    

    The expression firewall_type=client tells the Bourne shell to set the value of the variable firewall just for this command. If you're using csh or tcsh, use the following sequence:

    (setenv firewall_type=client; sh /etc/rc.firewall)
    

    Never start this script from an X terminal or across the network. If you do, you can lock yourself out of the session in the middle of the script, causing /etc/rc.firewall to stop at this point, blocking all accesses to the system.

  • Proxy servers

    For some purposes, a good alternative or adjunct to a packet filtering firewall and NAT is a proxy server that converts requests for specific protocols. In the example in the previous section, which was accessing a web server, we could also have run a proxy server on presto. Particularly in conjunction with web servers, a proxy server has the advantage that it can cache data locally, thus reducing network load.

    There are a couple of other differences between NAT and proxy servers: natd does not know much about the data it passes. Proxy servers know a lot about it. This makes proxy servers less suitable as a general security or address translation mechanism. In addition, the client must know about the proxy server, whereas it does not need to know anything about NAT and firewalls. A typical connection looks like this:

    (рис 22.2) Accessing the Web via a proxy server

    This looks very similar to Figure 22-1 . The only thing that appears to have changed is the port number on . Here it establishes a connection with http://presto.example.org.

    Installing squid

    A good choice of web proxy server is squid, which is available in the Ports Collection. Install it in the normal manner:

    cd /usr/ports/www/squid
    make install
    

    squid is not the easiest thing in the world to set up, and it's hampered by sub-standard documentation. The man page is squid(8), but most of the information is in the configuration file /usr/local/etc/squid/squid.conf. By default, it is set up to do nothing. It has over 3,000 lines of mostly comments. I suggest the following changes:

  • Set the value http_proxy to the number of the port you want to use. By default, squid uses port 3128, but many proxies use port 8080, and that's the port that most web browsers expect too. If you are not running a web server on the machine, you can also use the http port, 80. Add:
    http_port 8080 80
    
  • The variable http_access defines who can access the web server. By default, it denies all requests except from the local manager, so you must set it if you expect to get any results from the server. An appropriate setting might be:
    acl local src 192.168.27.0/255.255.255.0 
    acl exampleorg src 223.147.37.0/24
    http_access allow local 
    http_access allow exampleorg
    

    This defines two access control lists, one for the NAT network we looked at in the previous section (local), and one for the globally visible network 223.147.37.0 (exampleorg). The first acl statement specifies the network in the form address/netmask, while the second specifies it with the number of significant bits in the net mask. The http_access statements then allow access for each of them.

  • If you're using the ftp proxy, it's probably a good idea to change the default name with which squid performs anonymous ftp. By default it's Squid@, but that looks silly. Change it by setting:
    ftp_user squid@example.org
    
  • squid doesn't expect any line of the ftp file listing to be more than 32 characters long. That's pretty conservative. You can make it larger like this:
    ftp_list_width 120
    
  • By default, squid caches any object less than 4 MB in size on disk. If you're doing a lot of ftp work, this can seriously degrade the cache performance for http. You can reduce it to, say, 256 kB with:
    maximum_object_size 256 KB
    
  • The system starts squid as user root, which is not the best for security: proxy servers are a popular target for intruders on the Internet. You should change it to run as user and group www:
    cache_effective_user www
    cache_effective_group www
    
  • Starting squid

    Before you can start squid, you must first create the cache directories. If not, you can start it, and it doesn't complain, but it doesn't run either. Later you might find something like this in the log file /var/log/messages:

    Dec 21 15:26:51 presto squid[23800]: Squid Parent: child process 23802 started
    Dec 21 15:26:53 presto (squid):      Failed to verify one of the swap directories
    ,Check cache.log   for details.   Run 'squid -z' to create swap directories if needed, or if running Squid for the first time.
    Dec 21 15:26:53 presto kernel: pid 23802 (squid), uid 65534: exited on signal 6 
    Dec 21 15:26:53 presto squid[23800]: Squid Parent: child process 23802 exited due to signal 6
    Dec 21 15:26:56 presto squid[23800]: Squid Parent: child process 23805 started
    

    The log files are in /usr/local/squid/log, and the cache files should be in /usr/lo-cal/squid/cache. To create them, enter:

    # squid -z
    2002/12/21 15:30:35| Creating Swap Directories
    

    Finally, you can start squid:

    # squid
    

    On system restart, squid will be started automatically from the script in /usr/lo-cal/etc/rc.d/squid.sh.

    Browser proxy configuration

    As mentioned earlier, proxies aren't transparent to the application. You have to set up your software to talk to the proxy. To do that, you need to configure the web browser accordingly. For example, with galeon you select Settings —> Preferences —> Advanced —> Network and get the following screen:

    (рис 22.3) Galeon proxy settings

    squid understands the individual protocols that it supports, so it can tell the difference between, say, an http request on port 8080 and an ftp request on the same port. Nevertheless, consider whether it's a good idea to use squid for ftp. It doesn't speed up access the first time you fetch the file, and if you access each file only once, you don't have any gain through using squid. On the other hand, the ftp data can pollute the cache.

    Setting proxy information for ftp

    ftp understands proxies, and uses them for non-interactive connections only. Put the following statement in your .profile file:

    export http_proxy=presto.example.org:8080 
    export ftp_proxy=presto.example.org:8080
    
    Страницы:

    The Internet was developed by a relatively small community of computer scientists, who were for the most part responsible people who often did not take security issues very seriously. Since the Internet has been opened to the general public, three problems have become evident:

  • A large number of people have sought to abuse its relatively lax security.
  • The address space is no longer adequate for the number of machines connecting to the network.
  • Much bandwidth is used by people downloading the same web pages multiple times.
  • What do these problems have to do with each other? Nothing much, but the solutions are related, so we'll look at them together. More specifically, we'll consider:

  • How to set up an Internet firewall to keep intruders out of your network.
  • Security tools that ensure that nobody can steal your password from a node through which it passes.
  • Tools for IP aliasing, which translate IP addresses to make them appear to come from the gateway machine. The way this is done makes it impossible to set up connections from outside, so they also represent a kind of security device.
  • Caching proxy servers, which both address the multiple download issues and provide some additional security.
  • Security and firewalls

    Recall from Chapter 16 that incoming packets need to connect to an IP port, and that some process on the machine must accept them. By default, this process is inetd. You can limit the vulnerability of your machine by limiting the number of services it supports. Do you need to supply telnet and rlogin services? If not, don't enable the service. By default, /etc/inetd.conf no longer enables any services, so this should not be a problem. Obviously, careful system configuration can minimize your vulnerability, but it also reduces your accessibility: intruders can't get in, but neither can the people who need to access the machine.

    A better solution is a tool that passes authorized data and refuses to pass unauthorized data. Such a tool is called a firewall. In this section, we'll look at packet filtering firewalls: the firewall examines each incoming packet and uses a set of predefined walls to decide whether to pass it unchanged, change it, or simply discard it. An alternative approach is a proxy firewall, which analyzes each packet and creates new requests based on its content. On page 396 we'll look at squid, a caching proxy server that provides some of this functionality.

    FreeBSD supports three different firewalls, ipf, ipfilter and ipfw. We consider ipfw here; you can find details about ipf and ipfilter and in the respective man pages.

    The DEFAULT FreeBSD kernel does not include firewall support. If you wish, you can build a kernel with firewall support—see the file /usr/src/sys/conf/NOTES for a list of parameters—but you don't need to build a new kernel. You can load the KLD /boot/kernel/ipfw.ko instead:

    # kldload ipfw
    ipfw2 initialized, divert disabled, rule-based forwarding enabled, default to deny, logging disabled
    

    Before you do so, make sure you have direct local connection to the system. If you start this command remotely, you will instantly lose access to the system. Read the following section before loading the firewall.

    ipfw: defining access rules

    The program ipfw processes access rules for the firewall. Each rule relates to specific kinds of packet and describes what to do with them. On receiving a packet, ipfw examines each rule in a predetermined order until it finds one which matches the packet. It then performs the action that the rule specifies. In most cases, the rule accepts or denies the packet, so ipfw does not need to continue processing the remaining rules, though sometimes processing can continue after a match. If no rule matches, the default is to allow no traffic.

    ipfw packet types
    KeywordDescription
    ipAll IP packets.
    tcpTCP packets.
    udpUDP packets.
    icmpICMP packets.
    service name or numberA packet destined for one of the services described in /etc/services.
    src IP address rangeA packet with a source address that matches IP address. See below for the interpretation of IP address range.
    dst IP address rangeA packet with a destination address that matches IP address.
    via interfaceAll packets going by the specified interface. interface may be an interface name or an IP address associated with only one interface.
    recv interfaceAll packets arriving by the specified interface. interface may be an interface name or an IP address associated with only one interface.
    xmit interfaceAll packets going out by the specified interface. interface may be an interface name or an IP address associated with only one interface.
    IP addressThis is an IP address. It specifies a match for exactly this address.
    IP address/bitsbits is a value between 0 and 32. This form matches the first bits bits of IP address.
    IP address: maskmask is a 32-bit value. This form matches those bits of IP address specified in mask. This is the same concept as a net mask—see Chapter 16 , page 290, for a description of net masks.

    Table 22-1 shows the keywords you can use to define the packets and the forms that the IP address range can take:

    These options can be combined with a few restrictions:

  • The recv interface can be tested on either incoming or outgoing packets, while the xmit interface can be tested only on outgoing packets. This means that you must specify the keyword out (and you may not specify in) when you use xmit. You can't specify via together with xmit or recv.
  • A packet that originates from the local host does not have a receive interface. A packet destined for the local host has no transmit interface.
  • Actions

    When ipfw finds a rule which matches a packet, it performs the specified action. Table 22-2 shows the possibilities.

    Actions on packets
    KeywordDescription
    allowAllow a packet to pass. Stop processing the rules.
    denyDiscard the packet. Stop processing the rules.
    unreachDiscard the packet and send an ICMP host unreachable message to the sender. Stop processing the rules.
    resetDiscard the packet and send a TCP reset message. This can apply only to TCP packets. Stop processing the rules.
    countCount the packet and continue processing the rules.
    divert portDivert the packet to the divert socket bound to port port. See the man page ipfw(8) for more details. Stop processing the rules.
    tee portSend a copy of the packet to the divert socket bound to port port. Continue processing the rules.
    skipto ruleContinue processing the rules at rule number rule.

    Writing rules

    The sequence in which rules are applied is not necessarily the sequence in which they are read. Instead, each rule can have a line number between 1 and 65534. Rules are applied from the lowest to the highest line number. If you enter a rule without a line number, however, it receives a number 100 higher than the previous rule.

    The highest-numbered rule is number 65535, which is always present. Normally it has the form:

    65535 deny all from any to any
    

    In other words, if no other rules are present, or they don't match the packet, ipfw drops the packet. If you build a kernel with the option IPFIREWALL_DEFAULT_TO_ACCEPT, this rule changes to its opposite:

    65535 allow all from any to any
    

    These two rule sets implicitly illustrate two basic security strategies. You may note parallels to certain political systems:

  • The first takes the attitude "everything is forbidden unless explicitly allowed."
  • The second takes the attitude "everything is allowed unless explicitly forbidden."
  • It goes without saying that the first policy is safer. If you make a mistake with the first (more restrictive) rule set, you're more likely to lock people out of your system accidentally than you are to let them in when you don't want them.

    Configuration files

    The main configuration file is /etc/rc.firewall. It's unlikely to match your needs exactly. There are two possibilities:

  • You can edit it to match your requirements.
  • You can create your own configuration file with your rules.
  • Which do you choose? If you're making only minor modifications, it's easier to edit it. If you're taking things seriously, though, you'll end up with something that no longer bears much of a relationship with the original file. Upgrades are easier if you have your own file.

    If you create your own file, you can tell /etc/rc.conf its name, and /etc/rc.firewall will read it. Either way, the rules are the same. In the following section we'll go through the default /etc/rc.firewall file. There's nothing stopping you from copying them to another file and editing them to match your requirements.

    Reading the file is somewhat complicated by a number of environment variables that are set in the system startup scripts. We'll see the important ones below. It's also helpful to know that ${fwcmd} gets replaced by the name of the firewall program, /sbin/ipfw. The other ones are described in /etc/default/rc.conf:

    firewall_enable="NO"                 # Set to YES to enable firewall functionality
    firewall_script="/etc/rc.firewall"   # Which script to run to set up the firewall
    firewall_type="UNKNOWN"              # Firewall type (see /etc/rc.firewall)
    firewall_quiet="NO"                  # Set to YES to suppress rule display
    firewall_logging="NO"                # Set to YES to enable events logging
    firewall_flags=""                    # Flags passed to ipfw when type is a file
    

    To set up the firewall, first decide the kind of profile you need and set the variable firewall_type accordingly. The current version of /etc/rc.firewall defines four kinds of usage profile:

  • The open profile is effectively a disabled firewall. It allows all traffic. You might use this if you're having trouble setting up the firewall and need to disable it temporarily.
  • The client profile is a good starting point for a system that does not provide many publicly accessible services to the Net. We'll look at it in the next section.
  • The simple profile, despite its name, is intended for a system that does provide a number of publicly accessible services to the Net.
  • The closed profile allows only local traffic via the loopback interface.
  • In addition, you can set firewall_type to the name of a file describing the firewall configuration.

    All configurations start with a call to setup_loopback, which adds the following rules:

    ${fwcmd} add 100 pass all from any to any via lo0
    ${fwcmd} add 200 deny all from any to 127.0.0.0/8
    ${fwcmd} add 300 deny ip from 127.0.0.0/8 to any
    

    These rules allow all local traffic and stop traffic coming in with a fake local address.

    The client profile

    At the beginning of the client profile you'll find a number of variables you need to set. In the following example they're set to match http://freebie.example.org and our example network:

    [Cc][Ll][Ii][Ee][Nn][Tt])
       ############
       #This is a prototype setup that will protect your system somewhat against
       #people from outside your own network.
       ############
       #set these to your network and netmask and ip
       net="223.147.37.0"
       mask="255.255.255.0"
       ip="223.147.37.1"                freebie.example.org
    

    The first line matches the text client whether written in upper or lower case. Then we have:

    setup_loopback
    #Allow any traffic to or from my own net.
    ${fwcmd} add pass all from ${ip} to ${net}:${mask}
    ${fwcmd} add pass all from ${net}:${mask} to ${ip}
    

    These rules allow any traffic in the local network.

    #Allow TCP through if setup succeeded
    ${fwcmd} add pass tcp from any to any established
    

    If a TCP connection has already been established, allow it to continue. Establishing a TCP connection requires other rules, which we shall see below.

    #Allow IP fragments to pass through
    ${fwcmd} add pass all from any to any frag
    

    Fragmented packets are difficult to recognize, and if we block them, strange things might happen. They're usually not a significant security risk.

    #Allow setup of incoming email
    ${fwcmd} add pass tcp from any to ${ip} 25 setup
    #Allow setup of outgoing TCP connections only 
    ${fwcmd} add pass tcp from ${ip} to any setup
    #Disallow setup of all other TCP connections 
    ${fwcmd} add deny tcp from any to any setup
    

    The preceding three rules allow external systems to establish a TCP connection for delivering mail (first rule), but nothing else (third rule). The second rule allows setup of TCP connections to the outside world.

    #Allow DNS queries out in the world
    ${fwcmd} add pass udp from ${ip} to any 53 keep-state
    #Allow NTP queries out in the world
    ${fwcmd} add pass udp from ${ip} to any 123 keep-state #Everything else is denied as default.
    

    These two rules allow DNS and NTP queries. The keyword keep-state causes ipfw to build a short-lived dynamic rule matching this particular combination of end points and protocol. This means that we don't need to open traffic in the other direction. Previously, the rule set for DNS queries consisted of these two rules:

    $fwcmd add pass udp from any 53 to ${ip} 
    $fwcmd add pass udp from ${ip} to any 53
    

    This allows all DNS traffic in both directions. By contrast, keep-state allows only the reply traffic for specific queries to pass the firewall. You don't need this for TCP—the established keyword does the same thing—but UDP doesn't have the concept of a connection, so the firewall needs to keep track of the traffic.

    There are no more rules, so the default deny rule prevents any other kind of traffic.

    The simple profile

    Despite the name, the , so we'll use its addresses.

    #set these to your outside interface network and netmask and ip oif="tun0"
    onet="139.130.136.133"
    omask="255.255.255.255"
    oip="139.130.136.133"
    #set these to your inside interface network and netmask and ip iif="ep0"
    inet="223.147.37.0"
    imask="255.255.255.0"
    iip="223.147.37.0"
    

    These addresses and networks correspond to the PPP link and the local ethernet, respectively.

    #Stop spoofing
    ${fwcmd} add deny all from ${inet}:${imask} to any in via ${oif} 
    ${fwcmd} add deny all from ${onet}:${omask} to any in via ${iif}
    

    These two rules stop any packets purporting to come from the local network that arrive via the external network, and any packets purporting to come from the remote network that arrive via the local interface. These packets would have been faked, an action known as spoofing.

    #Stop RFC1918 nets on the outside interface 
    ${fwcmd} add deny all from any to 10.0.0.0/8 via ${oif} 
    ${fwcmd} add deny all from any to 172.16.0.0/12 via ${oif} 
    ${fwcmd} add deny all from any to 192.168.0.0/16 via ${oif}
    

    RFC 1918 defines networks that should not be routed. These rules enforce that requirement.

    At this point in the file there are also some other addresses that should not be routed. A check is made for address translation, because non-routed addresses are typically used by NAT environments.

    #Allow TCP through if setup succeeded
    ${fwcmd} add pass tcp from any to any established
    #Allow IP fragments to pass through 
    ${fwcmd} add pass all from any to any frag
    #Allow setup of incoming email
    ${fwcmd} add pass tcp from any to ${oip} 25 setup
    #Allow access to our DNS
    ${fwcmd} add pass tcp from any to ${oip} 53 setup
    ${fwcmd} add pass udp from any to ${oip} 53
    ${fwcmd} add pass udp from ${oip} 53 to any
    #Allow access to our WWW
    ${fwcmd} add pass tcp from any to ${oip} 80 setup
    

    These rules add to what we saw for the client profile: in addition to email, we allow incoming DNS and WWW connections.

    #RejectLog all setup of incoming connections from the outside 
    ${fwcmd} add deny log tcp from any to any in via ${oif} setup
    #Allow setup of any other TCP connection 
    ${fwcmd} add pass tcp from any to any setup
    

    Here, we don't just reject TCP setup requests from the outside world, we log them as well.

    #Allow DNS queries out in the world
    ${fwcmd} add pass udp from ${oip} to any 53 keep-state 
    #Allow NTP queries out in the world
    ${fwcmd} add pass udp from ${oip} to any 123 keep-state 
    #Everything else is denied as default.
    

    Finally, we allow DNS and NTP queries via UDP, and deny everything else from the outside world.

    user-defined profiles

    If the profile isn't one of the recognized keywords, /etc/rc.firewall checks if there's a file with that name. If so, it uses it as a command file to pass to ipfw:

    elif [ "${firewall}" != "NONE" -a -r "${firewall}" ]; 
       then ${fwcmd} ${firewall_flags} ${firewall_type}
    

    Note that you can't put comment lines in the file defined by ${firewall}.

    Entries in /etc/rc.conf

    When you have decided what kind of firewall configuration best suits your network, note that fact in /etc/rc.conf. Set the value of firewall_enable to YES to enable the firewall, and the value of firewall_type to indicate the type of firewall. For our example network, client is probably the most appropriate type:

    firewall_enable="YES"                # Set to YES to enable firewall functionality 
    firewall_script="/etc/rc.firewall"   # Which script to set up the firewall 
    firewall_type="client"               # Firewall type (see /etc/rc.firewall)
    

    If you have decided to write your own file rather than modify /etc/rc.firewall, set firewall_type to the name of the file.

    Trying it out

    You'll probably find that your first attempt at firewall configuration won't be the optimum. You'll probably discover requirements that you hadn't thought of and that are now being denied by the default rule. Be prepared to spend some time getting everything to work, and do this at the system console. There's no good alternative.

    IP aliasing

    In our reference network on page 294, we assumed that our local network had a valid assigned IP address. Sometimes, this isn't possible. In fact, in the Real World it's pretty well impossible to get a complete class C network for a system with only five systems on it. You have the alternative of getting a subset of a class C network (in this case, eight addresses would do) from your ISP, or using just one address and running software that makes all traffic from the network to the outside world look as if it's coming from that system. The latter approach, called network address translation (NAT) or IP aliasing, can be significantly cheaper: ISPs often charge good money for additional addresses. On the down side, NAT restricts you in some ways. Any connection between a machine on a NAT network and the global Internet must start from the machine on the NAT network, because the translation doesn't exist until the connection is set up. This also means that you can't connect two machines on different NAT networks.

    Network address translation involves three machines: one on the global Internet with real Internet addresses, one on a private subnet with unroutable addresses, and one in the middle that performs the translation. In our reference network (see page 294), let's consider connecting might look like this:

    (рис 22.1) Accessing the Web via NAT

    In this diagram, the IP addresses are above the boxes, the interface names above the connection lines, and the port numbers below the connection lines. The connection must be started by (216.136.204.117) and sends it out its default route, in this case the only interface, wi0. presto gets the packet on interface xl0 and routes it out through interface dc0.

    So far, that's nothing special—it's what any router does. The difference is that presto changes the source address and port number. On the wireless link, andante's address is 192.168.27.17, and the port number for this connection is 2731. On the remote link, the IP address becomes 223.147.37.2, presto's own address, and the port number in this case is 3312. Theoretically, the "changed" port address could be the same as the original, since there is no relationship. The destination IP address and port number can't change, of course, or the packet would never get to its destination.

    On return, the reverse happens: http://www.FreeBSD.org replies to presto, which recognizes the port number, converts it to andante's IP address and source port, and sends it to andante on the local network.

    IP aliasing software

    There are a number of ways to perform IP aliasing with FreeBSD. If you're connecting to the outside world via User PPP (see Chapter 20, page 348), you can use the -alias keyword to tell PPP to alias all packets coming from the network to the address of the tunnel interface. In our reference network, this would be the address 139.130.136.133.

    This particular form of IP aliasing has some limitations: it works only for a single User PPP connection to the outside world, and it's global in its functionality. One alternative is the Network Address Translation Daemon, or natd, which uses divert sockets to translate addresses. It works well in conjunction with the firewall software we looked at above.

    natd

    To set up natd for the example above, perform the following steps:

  • Even if you don't plan to run an IP firewall, build and boot a custom kernel with the following options:
    options IPFIREWALL
    options IPDIVERT
    

    If you are running a firewall, configure the firewall normally, but be sure to include the IPDIVERT option.

  • Make sure your interfaces are running. For example, if you're running Kernel PPP and you want to specify ppp0 as your interface, start pppd before starting natd.
  • What you do next differs a little depending on whether you are also running a firewall or not. If you're not, you're better off with a separate script, which you might call /etc/rc.nat, with the following content:
    /sbin/ipfw -f flush
    /sbin/ipfw add divert natd all from any to any via dc0
    /sbin/ipfw add pass all from any to any
    
  • If you want to combine NAT with real firewall rules, you need only the second line of the previous example. Set up the firewall as described above, and put the NAT line at the start of the section of /etc/rc.firewall that you have chosen, so that natd sees all packets before they are dropped by the firewall. After natd translates the IP addresses, the firewall rules are run again on the translated packet, with the exception of the divert rules. The client configuration is the most likely one to suit your needs if you're using NAT. After the example in listing 22-1 on 390, you might add:
    #set these to your network and netmask and ip
    net="192.0.2.0"
    mask="255.255.255.0"
    ip="192.0.2.1"
    setup_loopback
    /sbin/ipfw add divert natd all from any to any via dc0
    #Allow any traffic to or from my own net. 
    ${fwcmd} add pass all from ${ip} to ${net}:${mask} 
    ${fwcmd} add pass all from ${net}:${mask} to ${ip}
    
  • Add the following to /etc/rc.conf:
    firewall_enable=YES
    gateway_enable="YES"        # Set to YES if this host is a gateway.
    natd_enable="YES"
    natd_interface="dc0"
    firewall_script="/etc/rc.nat"  # script for NAT only
    firewall_type="client"           # firewall type if running a firewall
    

    The interface name in the second line, dc0, is the name of the external interface (the one with the real IP addresses).

    If you're using NAT but not a firewall, you don't need to specify a firewall_type, because that relates to /etc/rc.firewall. You do need to specify the name of the script to run, however.

  • Enable your firewall as shown above in the firewall section. If you don't intend to reboot now, just run /etc/rc.firewall (or /etc/rc.natd) by hand from the console, and then start natd:
    sh /etc/rc.nat                                            for NAT only
    firewall_type=client sh /etc/rc.firewall   for NAT and firewall
    natd dc0
    

    The expression firewall_type=client tells the Bourne shell to set the value of the variable firewall just for this command. If you're using csh or tcsh, use the following sequence:

    (setenv firewall_type=client; sh /etc/rc.firewall)
    

    Never start this script from an X terminal or across the network. If you do, you can lock yourself out of the session in the middle of the script, causing /etc/rc.firewall to stop at this point, blocking all accesses to the system.

  • Proxy servers

    For some purposes, a good alternative or adjunct to a packet filtering firewall and NAT is a proxy server that converts requests for specific protocols. In the example in the previous section, which was accessing a web server, we could also have run a proxy server on presto. Particularly in conjunction with web servers, a proxy server has the advantage that it can cache data locally, thus reducing network load.

    There are a couple of other differences between NAT and proxy servers: natd does not know much about the data it passes. Proxy servers know a lot about it. This makes proxy servers less suitable as a general security or address translation mechanism. In addition, the client must know about the proxy server, whereas it does not need to know anything about NAT and firewalls. A typical connection looks like this:

    (рис 22.2) Accessing the Web via a proxy server

    This looks very similar to Figure 22-1 . The only thing that appears to have changed is the port number on . Here it establishes a connection with http://presto.example.org.

    Installing squid

    A good choice of web proxy server is squid, which is available in the Ports Collection. Install it in the normal manner:

    cd /usr/ports/www/squid
    make install
    

    squid is not the easiest thing in the world to set up, and it's hampered by sub-standard documentation. The man page is squid(8), but most of the information is in the configuration file /usr/local/etc/squid/squid.conf. By default, it is set up to do nothing. It has over 3,000 lines of mostly comments. I suggest the following changes:

  • Set the value http_proxy to the number of the port you want to use. By default, squid uses port 3128, but many proxies use port 8080, and that's the port that most web browsers expect too. If you are not running a web server on the machine, you can also use the http port, 80. Add:
    http_port 8080 80
    
  • The variable http_access defines who can access the web server. By default, it denies all requests except from the local manager, so you must set it if you expect to get any results from the server. An appropriate setting might be:
    acl local src 192.168.27.0/255.255.255.0 
    acl exampleorg src 223.147.37.0/24
    http_access allow local 
    http_access allow exampleorg
    

    This defines two access control lists, one for the NAT network we looked at in the previous section (local), and one for the globally visible network 223.147.37.0 (exampleorg). The first acl statement specifies the network in the form address/netmask, while the second specifies it with the number of significant bits in the net mask. The http_access statements then allow access for each of them.

  • If you're using the ftp proxy, it's probably a good idea to change the default name with which squid performs anonymous ftp. By default it's Squid@, but that looks silly. Change it by setting:
    ftp_user squid@example.org
    
  • squid doesn't expect any line of the ftp file listing to be more than 32 characters long. That's pretty conservative. You can make it larger like this:
    ftp_list_width 120
    
  • By default, squid caches any object less than 4 MB in size on disk. If you're doing a lot of ftp work, this can seriously degrade the cache performance for http. You can reduce it to, say, 256 kB with:
    maximum_object_size 256 KB
    
  • The system starts squid as user root, which is not the best for security: proxy servers are a popular target for intruders on the Internet. You should change it to run as user and group www:
    cache_effective_user www
    cache_effective_group www
    
  • Starting squid

    Before you can start squid, you must first create the cache directories. If not, you can start it, and it doesn't complain, but it doesn't run either. Later you might find something like this in the log file /var/log/messages:

    Dec 21 15:26:51 presto squid[23800]: Squid Parent: child process 23802 started
    Dec 21 15:26:53 presto (squid):      Failed to verify one of the swap directories
    ,Check cache.log   for details.   Run 'squid -z' to create swap directories if needed, or if running Squid for the first time.
    Dec 21 15:26:53 presto kernel: pid 23802 (squid), uid 65534: exited on signal 6 
    Dec 21 15:26:53 presto squid[23800]: Squid Parent: child process 23802 exited due to signal 6
    Dec 21 15:26:56 presto squid[23800]: Squid Parent: child process 23805 started
    

    The log files are in /usr/local/squid/log, and the cache files should be in /usr/lo-cal/squid/cache. To create them, enter:

    # squid -z
    2002/12/21 15:30:35| Creating Swap Directories
    

    Finally, you can start squid:

    # squid
    

    On system restart, squid will be started automatically from the script in /usr/lo-cal/etc/rc.d/squid.sh.

    Browser proxy configuration

    As mentioned earlier, proxies aren't transparent to the application. You have to set up your software to talk to the proxy. To do that, you need to configure the web browser accordingly. For example, with galeon you select Settings —> Preferences —> Advanced —> Network and get the following screen:

    (рис 22.3) Galeon proxy settings

    squid understands the individual protocols that it supports, so it can tell the difference between, say, an http request on port 8080 and an ftp request on the same port. Nevertheless, consider whether it's a good idea to use squid for ftp. It doesn't speed up access the first time you fetch the file, and if you access each file only once, you don't have any gain through using squid. On the other hand, the ftp data can pollute the cache.

    Setting proxy information for ftp

    ftp understands proxies, and uses them for non-interactive connections only. Put the following statement in your .profile file:

    export http_proxy=presto.example.org:8080 
    export ftp_proxy=presto.example.org:8080
    
    Вернуться к учебному плану