DNS is a distributed database system that translates hostnames to IP addresses and IP addresses to hostnames (for instance it might translate hostname miles.somewhere.example to IP address 192.168.244.34). DNS is also the standard Internet mechanism for storing and accessing several other kinds of information about hosts; it provides information about a particular host to the world at large. For example, if a host cannot receive mail directly, but another machine will receive mail for it and pass it on, that information is communicated with an MX record in DNS.
DNS clients include any program that needs to do any of the following:
Obtain other published information about a host (such as its MX record)
Other protocols may be used to provide this kind of information. For example, NIS and WINS are used to provide host information within a network. However, DNS is the service used for this purpose across the Internet, and clients that need to access Internet hosts will have to use DNS, directly or indirectly. On networks that use WINS, NIS, or other methods internally, the server for the other protocol usually acts as a DNS proxy for the client. Many clients can also be configured to use multiple services, so that if a host lookup fails, it will retry using another method. Thus, it might start by looking in NIS, which will show only local hosts but try DNS if that fails, or it might start by looking in DNS and then try a file on its own disk if that fails (so that you can put in personal favorite names, for example). We'll discuss this later in this chapter. (One debugging tool that is very useful in this situation is nslookup, which is a pure DNS client. If the information you get from nslookup is not the same as the information you get when you try to resolve a hostname in another program, you know that the other program is not getting its information from DNS.)
In Unix, DNS is implemented by the Berkeley Internet Name Domain (BIND). On the client side is the resolver, a library of routines called by network processes. On the server side is a daemon called named (also known as in.named on some systems). In Microsoft Windows, the client-side libraries are less localized because of the complex possibilities for mixing native Microsoft protocols for name resolution with DNS. The server side is a server called Microsoft DNS Server, which is designed to be highly interoperable with BIND. BIND and Microsoft DNS Server are never perfectly interoperable -- each one has its own special features and interpretations of the standards that are changed with each release -- but they have a large overlap and are rarely actually incompatible.
DNS is designed to forward queries and responses between clients and servers, so that servers may act on behalf of clients or other servers. This capability is very important to your ability to build a firewall that handles DNS services securely.
How does DNS work? Essentially, the most common procedure goes like this: When a client needs a particular piece of information (e.g., the IP address of host ftp.somewhere.example), it asks its local DNS server for that information. The local DNS server first examines its own cache to see if it already knows the answer to the client's query. If not, the local DNS server asks other DNS servers, in turn, to discover the answer to the client's query. When the local DNS server gets the answer (or decides that it can't for some reason), it caches any information it got[112] and answers the client. For example, to find the IP address for ftp.somewhere.example, the local DNS server first asks one of the public root name servers which machines are name servers for the example domain. It then asks one of those example name servers which machines are name servers for the somewhere.example domain and then it asks one of those name servers for the IP address of ftp.somewhere.example.
[112]On some types of failures, some servers will cache the fact that the query failed. Others cache only information retrieved on a successful query.This asking and answering is all transparent to the client. As far as the client is concerned, it has communicated only with the local server. It doesn't know or care that the local server may have contacted several other servers in the process of answering the original question.
The DNS protocol does not require servers to act like this. Servers do not have to maintain local caches, and they do not have to pass queries to other servers (they can refer the client to another server instead). In practice, however, all name servers in widespread use provide both caching and recursion (which is the term used in DNS circles to refer to the process where the server asks other servers if it cannot find the answer).
For performance reasons, DNS lookups are usually executed using UDP. If some of the data is lost in transit by UDP (remember that UDP doesn't guarantee delivery), the lookup may be redone using TCP. There may be other exceptions (nothing actually requires clients to try UDP first or to ever try TCP at all). Figure 20-1 shows a DNS name lookup.
If your primary and secondary servers both support DNS NOTIFY so that the primary server can notify the secondary server of changes, you only need to allow TCP between them. DNS NOTIFY is required to be able to use TCP when UDP is unavailable, although it defaults to UDP. If you cannot use DNS NOTIFY, you may have to allow UDP between the two servers because the secondary server must be able to query the primary server and is not required to use TCP to do so. Most servers will fail over to TCP if UDP is unavailable, but the only way to be sure is to check what the servers you are running actually do. Note that DNS NOTIFY is a relatively recent addition and not all servers support it.
Direction | SourceAddr. | Dest.Addr. | Protocol | SourcePort | Dest.Port | ACKSet | Notes |
---|---|---|---|---|---|---|---|
In | Ext | Int | UDP | >1023 | 53 |
[113]
|
Query via UDP, external client to internal server |
Out | Int | Ext | UDP | 53 | >1023 | [113] | Response via UDP, internal server to external client |
In | Ext | Int | TCP | >1023 | 53 |
[114]
|
Query via TCP, external client to internal server |
Out | Int | Ext | TCP | 53 | >1023 | Yes | Response via TCP, internal server to external client |
Out | Int | Ext | UDP | >1023 | 53 | [113] | Query via UDP, internal client to external server |
In | Ext | Int | UDP | 53 | >1023 | [113] | Response via UDP, external server to internal client |
Out | Int | Ext | TCP | >1023 | 53 | [114] | Query via TCP, internal client to external server |
In | Ext | Int | TCP | 53 | >1023 | Yes | Response via TCP, external server to internal client |
In | Ext | Int | UDP | 53 | 53 | [113] |
Query or response between two servers[115] via UDP
|
Out | Int | Ext | UDP | 53 | 53 | [113] | Query or response between two servers[115] via UDP |
In | Ext | Int | TCP | >1023 | 53 | [114] | Query or zone transfer request from external server to internal server via TCP |
Out | Int | Ext | TCP | 53 | >1023 | Yes | Response (including zone transfer response) from internal server to external server via TCP |
Out | Int | Ext | TCP | >1023 | 53 | [114] | Query or zone transfer request from internal server to external server via TCP |
In | Ext | Int | TCP | 53 | >1023 | Yes | Response (including zone transfer response) from external server to internal server via TCP |
[113]UDP has no ACK equivalent.
[114]ACK is not set on the first packet of this type (establishing connection) but will be set on the rest.
[115]Not all servers use 53 as a source port for UDP; some will use a port above 1023, like other clients.
In most implementations, it would be possible to modify the DNS libraries to use a modified-client proxy. On machines that do not support dynamic linking, using a modified-client proxy for DNS would require recompiling every network-aware program. Because users don't directly specify server information for DNS, modified-procedure proxies seem nearly impossible.
[116]For detailed information about DNS record types, what they mean, and how to use them, see the DNS and BIND book, referenced earlier in this chapter.
Record Type | Usage |
---|---|
A | Translates hostname to IP address |
PTR | Translates IP address to hostname |
CNAME | Translates host alias to hostname ("canonical" name) |
HINFO | Gives hardware/software information about a host |
NS | Delegates a zone of the DNS tree to some other server |
SOA | Denotes start of authority for a zone of the DNS tree |
X | Specifies another host to receive mail for this hostname (a mail exchanger) |
In fact, there are two separate DNS data trees: one for obtaining information by hostname (such as the IP address, CNAME record, HINFO record, or MX record that corresponds to a given hostname), and one for obtaining information by IP address (the hostname for a given address).
For example, the following is a sample of the DNS data for a fake domain somebody.example :
This domain would also need a corresponding set of PTR records to map IP addresses back to hostnames. To translate an IP address to a hostname, you reverse the components of the IP address, append .IN-ADDR.ARPA, and look up the DNS PTR record for that name. For example, to translate IP address 1.2.3.4, you would look up the PTR record for 4.3.2.1.IN-ADDR.ARPA.somebody.example. IN SOA tiger.somebody.example. root.tiger.somebody.example. ( 1001 ; serial number 36000 ; refresh (10 hr) 3600 ; retry (1 hr) 3600000 ; expire (1000 hr) 36000 ; default ttl (10 hr) ) IN NS tiger.somebody.example. IN NS lion.somebody.example. tiger IN A 192.168.2.34 IN MX 5 tiger.somebody.example. IN MX 10 lion.somebody.example. IN HINFO INTEL-486 BSDI ftp IN CNAME tiger.somebody.example. lion IN A 192.168.2.35 IN MX 5 lion.somebody.example. IN MX 10 tiger.somebody.example. IN HINFO SUN-3 SUNOS www IN CNAME lion.somebody.example. wais IN CNAME lion.somebody.example. alaska IN NS bear.alaska.somebody.example. bear.alaska IN A 192.168.2.81
2.168.192.IN-ADDR.ARPA. IN SOA tiger.somebody.example.root.tiger.somebody.example. ( 1001 ; serial number 36000 ; refresh (10 hr) 3600 ; retry (1 hr) 3600000 ; expire (1000 hr) 36000 ; default ttl (10 hr) ) IN NS tiger.somebody.example. IN NS lion.somebody.example. 34 IN PTR tiger.somebody.example. 35 IN PTR lion.somebody.example. 81 IN PTR bear.alaska.somebody.example.
TIP: Later versions of DNS for Unix (BIND 4.9 and later) check for bogus answers and are less susceptible to these problems. Earlier versions, and DNS clients and servers for other platforms, may still be susceptible.Some Unix DNS implementations will accept and cache answers even when they haven't made a query; some Microsoft implementations will crash if they receive an unrequested answer. Both of these behaviors are undesirable and have been eliminated by recent releases. Windows 2000 by default will only accept answers to queries but will accept those answers from any server. It can be configured to require the response to come from a queried server and should be on security-critical machines.
There are perfectly valid reasons for these checks to return inconsistent values; no rules require a forward and reverse lookup to return consistent information. In fact, when DNS is used for load balancing between servers, it is difficult to arrange for this consistency. In these situations, DNS is being used to determine the location of a service rather than the IP address of an individual host.
Any program that makes authentication or authorization decisions based on the hostname information it gets from DNS should be very careful to validate the data with this reverse lookup/double-reverse lookup method. In some operating systems (for example, SunOS 4.x and later), this check is automatically done for you by the gethostbyaddr( ) library function. In most other operating systems, you have to do the check yourself. Make sure that you know which approach your own operating system takes and that the daemons that are making such decisions in your system do the appropriate validation. (And be sure you're preserving this functionality if you modify or replace the vendor's libc.) Better yet, don't do any authentication or authorization based solely on hostname or even on IP address; there is no way to be sure that a packet comes from the IP address it claims to come from, unless some kind of cryptographic authentication is within the packet that only the true source could have generated.
Some implementations of double-reverse lookup fail on hosts with multiple addresses, (e.g., dual-homed hosts used for proxying). If both addresses are registered at the same name, a DNS lookup by name will return both of them, but many programs will read only the first. If the connection happened to come from the second address, the double-reverse will incorrectly fail even though the host is correctly registered. Although you should avoid using double-reverse implementations that have this flaw, you may also want to ensure that on your externally visible multi-homed hosts, lookup by address returns a different name for each address, and that those names have only one address returned when it is looked up. For example, for a host named "foo" with two interfaces named "e0" and "e1", have lookups of "foo" return both addresses, lookups of "foo-e0" and "foo-e1" return only the address of that interface, and lookups by IP address return "foo-e0" or "foo-e1" (but not simply "foo") as appropriate.
TIP: For internal multi-homed hosts, you probably don't want to set things up in the way we've described; if you do, you may end up needing to list them by multiple names anywhere you want to give them permissions, such as in /etc/exports files.
Without authentication, dynamic update of DNS is extremely risky. You can't keep hostile clients from stealing addresses from each other or swamping the server in changes. Therefore, dynamic updates in DNS can be used only inside networks where there is a very high degree of trust.
Even the simplest hostname information can be helpful to an attacker who wants to bluff his or her way into your site, physically or electronically. Using information in this way is an example of what is commonly called a social engineering attack. The attacker first examines the DNS data to determine the name of a key host or hosts at your site. Such hosts will often be listed as DNS servers for the domain or as MX gateways for lots of other hosts. Next, the attacker calls or visits your site, posing as a service technician, and claims to need to work on these hosts. The attacker will then ask for the passwords for the hosts (on the telephone) or ask to be shown to the machine room (in person). Because the attacker seems legitimate and seems to have inside information about the site -- after all, the machine names are right -- people will often grant access. Social engineering attacks like this takes a lot of brazenness on the part of the attacker, particularly if they're carried out in person, but you'd be amazed at how often such attacks succeed.
Besides internal hostnames, other information is often placed within the DNS -- information that is useful locally but you'd really rather an attacker not have access to. DNS HINFO and TXT resource records are particularly revealing:
The question to keep in mind when considering what DNS data to reveal is, "Why give attackers any more information than necessary?" The following sections provide some suggestions to help you reveal only the data you want people to have.
As far as this fake server on the bastion host is aware, it knows everything about your domain. In fact, though, all it knows about is whatever information you want revealed to the outside world. This information typically includes only basic hostname and IP address information about the following hosts:
You can create a wildcard MX record that redirects mail for any hostname that there is no other record for. It looks like a normal MX record for a host named "*". However, you should be aware that wildcard MX records do not apply to names that have other records of their own. Any host that has an A record will require an individual MX record, even if there is a wildcard MX record; the same holds true for names of subdomains that have NS records.
You will also need to publish fake information for any machines that can contact the outside world directly. Many servers on the Internet (for example, most major anonymous FTP servers) insist on knowing the hostname (not just the IP address) of any machines that contact them, even if they do nothing with the hostname but log it. In the DNS resource records, A (name-to-address mapping) records and PTR (address-to-name mapping) records handle lookups for names and addresses.
As we've mentioned earlier, machines that have IP addresses and need hostnames do reverse lookups. With a reverse lookup, the server starts with the remote IP address of the incoming connection and looks up the hostname that the connection is coming from. It takes the IP address (for example, 172.16.19.67), permutes it in a particular way (reverses the parts and adds .IN-ADDR.ARPA to get 67.19.16.172.IN-ADDR.ARPA), and looks up a PTR record for that name. The PTR record should return the hostname for the host with that address (e.g., mycroft.somewhere.example), which the server then uses for its logs or whatever.
How can you deal with these reverse lookups? If all these servers wanted was a name to log, you could simply create a wildcard PTR record. That record would indicate that a whole range of addresses belongs to an unknown host in a particular domain. For example, you might have a lookup for *.19.16.172.IN-ADDR.ARPA return unknown.somewhere.example. Returning this information would be fairly helpful; it would at least tell the server administrator whose machine it was (somewhere.example 's). Anyone who had a problem with the machine could pursue it through the published contacts for the somewhere.example domain.
There is a problem with doing only this, however. In an effort to validate the data returned by the DNS, more and more servers (particularly anonymous FTP servers) are now doing a double-reverse lookup and won't talk to you unless the double-reverse lookup succeeds. It is the same kind of lookup we mentioned previously; it's certainly necessary for people who provide a service where they use IP addresses to authenticate requests. Whether or not anonymous FTP is such a service is another question. Some people believe that once you put a file up for anonymous FTP, you no longer have reason to try to authenticate hosts; after all, you're trying to give information away. People running anonymous FTP servers that do double-reverse lookup argue that people who want services have a responsibility to be members of the network community and that requires being identifiable. Whichever side of the argument you're on, it is certainly true that the maintainers of several of the largest and best-known anonymous FTP servers are on the side that favors double-reverse lookup and will not provide service to you unless double-reverse lookup succeeds.
In a double-reverse lookup, a DNS client:
In order to make double-reverse lookups work, your fake server needs to provide consistent fake data for all hosts in your domain whose IP addresses are going to be seen by the outside world. For every IP address you own, the fake server must publish a PTR record with a fake hostname, as well as a corresponding A record that maps the fake hostname back to the IP address. For example, for the address 172.16.1.2, you might publish a PTR record with the name host-172-16-1-2.somewhere.example and a corresponding A record that maps host-172-16-1-2.somewhere.example back to the corresponding IP address (172.16.1.2). When you connect to some remote system that attempts to do a reverse lookup of your IP address (e.g., 172.16.1.2) to determine your hostname, that system will get back the fake hostname (e.g., host-172-16-1-2). If the system then attempts to do a double-reverse lookup to translate that hostname to an IP address, it will get back 172.16.1.2, which matches the original IP address and satisfies the consistency check.
If you are strictly using proxying to connect internal hosts to the external world, you don't need to set up the fake information for your internal hosts; you simply need to put up information for the host or hosts running the proxy server. The external world will see only the proxy server's address. For a large network, this by itself may make using proxy service for FTP worthwhile.
One way to accomplish this is to provide access to external DNS information by configuring your internal DNS server to query remote DNS servers directly, as appropriate, to resolve queries from internal clients about external hosts. Such a configuration, however, would require opening your packet filtering to allow your internal DNS server to talk to these remote DNS servers (which might be on any host on the Internet). This is a problem because DNS is UDP-based, and as we discuss in Chapter 8, "Packet Filtering", you need to block UDP altogether in order to block outside access to vulnerable RPC-based services like NFS and NIS.
Fortunately, the most common DNS server (the Unix named program) provides a solution to this dilemma: the forwarders directive in the /etc/named.boot server configuration file. The forwarders directive tells the server that, if it doesn't know the information itself already (either from its own zone information or from its cache), it should forward the query to a specific server and let this other server figure out the answer, rather than try to contact servers all over the Internet in an attempt to determine the answer itself. In the /etc/named.boot configuration file, you set up the forwarders line to point to the fake server on the bastion host; the file also needs to contain a "slave" line to tell it to use only the servers on the forwarders line, even if the forwarders are slow in answering.
The use of the forwarders mechanism doesn't really have anything to do with hiding the information in the internal DNS server; it has everything to do with making the packet filtering as strict as possible (i.e., applying the principle of least privilege), by making it so that the internal DNS server can talk only to the bastion host DNS server, not to DNS servers throughout the whole Internet.
If internal hosts can't contact external hosts, you may not want to bother setting things up so that they can resolve external hostnames. SOCKS proxy clients can be set up to use the external name server directly. This simplifies your name service configuration somewhat, but it complicates your proxying configuration, and some users may want to resolve hostnames even though they can't reach them (for example, they may be interested in knowing whether the hostname in an electronic mail address is valid).
Figure 20-4 shows how DNS works with forwarding; Figure 20-5 shows how it works without forwarding.
[117]These days, clients may also keep DNS configuration information elsewhere -- for instance, under IRIX 6.5 and above, DNS configuration parameters can also be put in the nsswitch file that will override the equivalent parameters in resolv.conf.
In this arrangement special care is needed when configuring internal SMTP servers. Because such services have access to address information, they may try to deliver mail directly rather than through your mail gateway.
DNS server and client configurations are completely separate. Many people assume that they must have configuration files in common, that the clients will automatically know about the local server, and that pointing them elsewhere will also point the server elsewhere. In fact, there is no overlap. Clients never read /etc/named.boot, which tells the server what to do, and the server never reads /etc/resolv.conf, which tells the clients what to do.
Again, there are two cases:
Since it is possible to configure a DNS client with multiple server names, it is tempting to configure DNS clients on the bastion host to query both the internal and the external server, in the hope that they will try one and then try the other if the first one returns "host unknown". In fact, this will not work; a DNS client will try a second server only if the first one does not respond. It will accept "host unknown" as a valid answer.
Rule | Direction | Source Addr. | Dest. Addr. | Protocol | SourcePort | Dest.Port | ACKSet | Action |
---|---|---|---|---|---|---|---|---|
A | Out | Internal server | Bastion host | UDP | 53, >1023 | 53 |
[118]
|
Permit |
B | Out | Internal server | Bastion host | TCP | >1023 | 53 | Any | Permit |
C | In | Bastion host | Internal server | UDP | 53 | 53, >1023 | [118] | Permit |
D | In | Bastion host | Internal server | TCP | 53 | >1023 | Yes | Permit |
E | In | Bastion host | Internal server | UDP | >1023 | 53 | [118] | Permit |
F | In | Bastion host | Internal server | TCP | >1023 | 53 | Any | Permit |
G | Out | Internal server | Bastion host | UDP | 53 | >1023 | [118] | Permit |
H | Out | Internal server | Bastion host | TCP | 53 | >1023 | Yes | Permit |
[118]UDP has no ACK equivalent.
There are a number of solutions to this problem, which are discussed in detail in DNS and BIND, by Paul Albitz and Cricket Liu, referenced earlier in this chapter. None of them are perfect. One option is to set up every server so that it knows about every subdomain; this removes many of the advantages of having the subdomains in the first place. Another is to set up the bastion host so that it has correct internal information but refuses to give it out to external queriers (you use the secure_zones directive for this). This removes much of the security of having a separate bastion host name server.
You can also avoid the problem by avoiding query forwarding. If there is no forwarding, normal resolution will take place, and all of the subdomains will work. On the other hand, internal machines will not be able to resolve external hostnames to IP addresses and will need to have carefully configured proxying solutions to reach external hosts.
You need to configure any packet filtering system between the bastion host and the internal server to allow the following (see the table for details):
Rule | Direction | Source Addr. | Dest. Addr. | Protocol | SourcePort | Dest.Port | ACKSet | Action |
---|---|---|---|---|---|---|---|---|
A | Out | Internal server | Bastion host | UDP | 53, >1023 | 53 |
[119]
|
Permit |
B | Out | Internal server | Bastion host | TCP | >1023 | 53 | Any | Permit |
C | In | Bastion host | Internal server | UDP | 53 | 53, >1023 | [119] | Permit |
D | In | Bastion host | Internal server | TCP | 53 | >1023 | Yes | Permit |
E | In | Bastion host | Internal server | UDP | 53, >1023 | 53 | [119] | Permit |
F | In | Bastion host | Internal server | TCP | >1023 | 53 | Any | Permit |
G | Out | Internal server | Bastion host | UDP | 53 | 53, >1023 | [119] | Permit |
H | Out | Internal server | Bastion host | TCP | 53 | >1023 | Yes | Permit |
[119]UDP has no ACK equivalent.
In particular, Windows 2000 consistently uses names that contain underscores. Traditionally, DNS names have been allowed to contain only letters, numbers, or hyphens (-). The underscore ( _ ) has been forbidden by the standard. In practice, most name servers did not enforce these name restrictions. Although the underscore has been theoretically forbidden for many years, name servers that do not allow underscores became popular fairly late in the development of Windows 2000. At about the same time, a push to loosen the restrictions on allowable names began. This situation has not been resolved as of this writing; a standard under discussion would allow almost any character in a DNS name, but it has not yet been accepted. In the meantime, some servers enforce strict name rules and reject names with underscores; some servers follow tradition and accept almost any ASCII character; and some servers accept even more characters, including those outside the ASCII range (for instance, accented letters). Windows 2000 minimally requires a DNS server that will allow underscores. Windows 2000 allows machine names that use other characters outside the normally permitted types, and those names may create mysterious failures unless you use a DNS server that accepts them.
Windows 2000 also uses a record type, the SRV record, which has not yet been standardized. A number of DNS servers support SRV, including versions of BIND starting with 4.9.6 (and all versions of BIND 8).
Windows 2000 relies on the ability to do dynamic updates of DNS information. When machines boot, they need to be able to register names in the DNS server. Dynamic updates are supported by BIND 8 but not with the authentication mechanisms used by Windows 2000. Using BIND 8 to support Windows 2000 therefore requires using unsecured dynamic updates.
Windows 2000 DNS can use Active Directory as a storage and replication method. In this configuration, the DNS information is stored in Active Directory, and DNS is simply used as a method of accessing it. A DNS server that is integrated with Active Directory must be a primary server; it can send updates to other DNS servers via normal zone transfers but cannot accept them because it is not controlling the data. However, when Windows 2000 DNS is integrated with Active Directory, it supports secure dynamic update using Kerberos for authentication.