> Squid中文手册 > 第6章 访问控制


6.访问控制



6.1 访问控制元素

ACL元素是Squid的访问控制的基础。这里告诉你如何指定包括IP地址,端口号,主机名,和URL匹配等变量。每个ACL元素有个名字,在编写访问控制规则时需要引用它们。基本的ACL元素语法如下:

acl name type value1 value2 ...

例如:

acl Workstations src 10.0.0.0/16

在多数情况下,你能对一个ACL元素列举多个值。你也可以有多个ACL行使用同一个名字。例如,下列两行配置是等价的:

acl Http_ports port 80 8000 8080
    
    
acl Http_ports port 80
    
acl Http_ports port 8000
    
acl Http_ports port 8080

6.1.1 一些基本的ACL类型

Squid大约有25个不同的ACL类型,其中的一些有通用基本类型。例如,src和dst ACL使用IP地址作为它们的基本类型。为避免冗长,我首先描述基本类型,然后在接下来章节里描述每种ACL类型。

6.1.1.1 IP地址

使用对象:src,dst,myip

squid在ACL里指定IP地址时,拥有强有力的语法。你能以子网,地址范围,域名等形式编写地址。squid支持标准IP地址写法(由”.”连接的4个小于256的数字)和无类域间路由规范。另外,假如你忽略掩码,squid会自动计算相应的掩码。例如,下例中的每组是相等的:

acl Foo src 172.16.44.21/255.255.255.255
    
acl Foo src 172.16.44.21/32
    
acl Foo src 172.16.44.21
    
    
acl Xyz src 172.16.55.32/255.255.255.248
    
acl Xyz src 172.16.55.32/28
    
    
acl Bar src 172.16.66.0/255.255.255.0
    
acl Bar src 172.16.66.0/24
    
acl Bar src 172.16.66.0

当你指定掩码时,squid会检查你的工作。如果你的掩码在IP地址的非零位之外,squid会告警。例如,下列行导致告警:

acl Foo src 127.0.0.1/8
    
    
aclParseIpData: WARNING: Netmask masks away part of the specified IP in 'Foo'

这里的问题是/8掩码(255.0.0.0)在最后三个字节里都是零值,但是IP地址127.0.0.1不是这样的。squid警告你这个问题,以便你消除歧义。正确的写法是:

acl Foo src 127.0.0.1/32

or:

acl Foo src 127.0.0.0/8

有时候你可能想列举多个相邻子网,在这样的情况下,通过指定地址范围很容易做到。例如:

acl Bar src 172.16.10.0-172.16.19.0/24

这等价但高效于下面的行:

acl Foo src 172.16.10.0/24
    
acl Foo src 172.16.11.0/24
    
acl Foo src 172.16.12.0/24
    
acl Foo src 172.16.13.0/24
    
acl Foo src 172.16.14.0/24
    
acl Foo src 172.16.15.0/24
    
acl Foo src 172.16.16.0/24
    
acl Foo src 172.16.18.0/24
    
acl Foo src 172.16.19.0/24

注意使用IP地址范围,掩码只能取一个。你不能为范围里的地址设置多个不同掩码。 你也能在IP ACL里指定主机名,例如:

acl Squid dst www.squid-cache.org

squid在启动时,将主机名转换成IP地址。一旦启动,squid不会对主机名的地址发起第二次DNS查询。这样,假如在squid运行中地址已改变,squid不会注意到。

假如主机名被解析成多个IP地址,squid将每一个增加到ACL里。注意你也可以对主机名使用网络掩码。

在基于地址的ACL里使用主机名通常是坏做法。squid在初始化其他组件之前,先解析配置文件,所以这些DNS查询不使用squid的非阻塞IP缓存接口。代替的,它们使用阻塞机制的gethostbyname()函数。这样,将ACL主机名转换到IP地址的过程会延缓squid的启动。除非绝对必要,请在src,dst,和myip ACL里避免使用主机名。

squid以一种叫做splay tree的数据结构在内存里存储IP地址ACL(请见http://www.link.cs.cmu.edu/splay/)。splay tree有一些有趣的自我调整的特性,其中之一是在查询发生时,列表会自动纠正它自己的位置。当某个匹配元素在列表里发现时,该元素变成新的树根。在该方法中,最近参考的条目会移动到树的顶部,这减少了将来查询的时间。

属于同一ACL元素的所有的子网和范围不能重迭。如果有错误,squid会警告你。例如,如下不被允许:

acl Foo src 1.2.3.0/24
    
acl Foo src 1.2.3.4/32

它导致squid在cache.log里打印警告:

WARNING: '1.2.3.4' is a subnetwork of '1.2.3.0/255.255.255.0'
    
WARNING: because of this '1.2.3.4' is ignored to keep splay tree searching
         
         predictable

WARNING: You should probably remove '1.2.3.4' from the ACL named 'Foo'

在该情形下,你需要修正这个问题,可以删除其中一个ACL值,或者将它们放置在不同的ACL列表中。

6.1.1.2 域名

使用对象:srcdomain,dstdomain,和cache_host_domain指令

域名简单的就是DNS名字或区域。例如,下面是有效的域名:

www.squid-cache.org
    
squid-cache.org
    
org

域名ACL有点深奥,因为相对于匹配域名和子域有点微妙的差别。当ACL域名以"."开头,squid将它作为通配符,它匹配在该域的任何主机名,甚至域名自身。相反的,如果ACL域名不以"."开头,squid使用精确的字符串比较,主机名同样必须被严格检查。

表6-1显示了squid的匹配域和主机名的规则。第一列显示了取自URL请求的主机名(或者srcdomain ACL的客户主机名)。第二列指明是否主机名匹配lrrr.org。第三列显示是否主机名匹配.lrrr.org ACL。你能看到,唯一的不同在第二个实例里。


Table 6-1. Domain name matching

URL hostname Matches ACL lrrr.org? Matches ACL .lrrr.org?
lrrr.org Yes Yes
i.am.lrrr.org No Yes
iamlrrr.org No No

域名匹配可能让人迷惑,所以请看第二个例子以便你能真正理解它。如下是两个稍微不同的ACL:

acl A dstdomain foo.com
    
acl B dstdomain .foo.com

用户对http://www.foo.com/的请求匹配ACL B,但不匹配A。ACL A要求严格的字符串匹配,然而ACL B里领头的点就像通配符。

另外,用户对http://foo.com/的请求同时匹配A和B。尽管在URL主机名里的foo.com前面没有字符,但ACL B里领头的点仍然导致一个匹配。

squid使用splay tree的数据结构来存储域名ACL,就像它处理IP地址一样。然而,squid的域名匹配机制给splay tree提供了一个有趣的问题。splay tree技术要求唯一键去匹配任意特定搜索条目。例如,让我们假设搜索条目是i.am.lrrr.org。该主机名同时匹配.lrrr.org和.am.lrrr.org。事实上就是两个ACL值匹配同一个主机名扰乱了splay机制。换句话说,在配置文件里放置如下语句是错误的:

acl Foo dstdomain .lrrr.org .am.lrrr.org

假如你这样做,squid会产生如下警告信息:

WARNING: '.am.lrrr.org' is a subdomain of '.lrrr.org'
    
WARNING: because of this '.am.lrrr.org' is ignored to keep splay tree searching predictable
    
WARNING: You should probably remove '.am.lrrr.org' from the ACL named 'Foo'

在该情况下你应遵循squid的建议。删除其中一条相关的域名,以便squid明确知道你的意图。注意你能在不同的ACL里任意使用这样的域名:

acl Foo dstdomain .lrrr.org
    
acl Bar dstdomain .am.lrrr.org

这是允许的,因为每个命名ACL使用它自己的splay tree.

6.1.1.3 用户名

使用对象:ident,proxy_auth

该类型的ACL被设计成匹配用户名。squid可能通过RFC 1413 ident协议或者通过HTTP验证头来获取用户名。用户名必须被严格匹配。例如,bob不匹配bobby。squid也有相关的ACL对用户名使用正则表达式匹配(ident_regex和proxy_auth_regex)。

你可以使用单词"REQUIRED"作为特殊值去匹配任意用户名。假如squid不能查明用户名,ACL不匹配。当使用基于用户名的访问控制时,squid通常这样配置。

6.1.1.4 正则表达式

使用对象:srcdom_regex, dstdom_regex, url_regex, urlpath_regex, browser, referer_regex, ident_regex, proxy_auth_regex, req_mime_type, rep_mime_type

大量的ACL使用正则表达式来匹配字符串(完整的正则表达式参考,请见O'Reilly的Mastering Regular Expressions一书)。对squid来说,最常使用的正则表达式功能用以匹配字符串的开头或结尾。例如,^字符是特殊元字符,它匹配行或字符串的开头:

^http://

该正则表达式匹配任意以http://开头的URL。$也是特殊的元字符,因为它匹配行或字符串的结尾:

.jpg$

实际上,该示例也有些错误,因为.字符也是特殊元字符。它是匹配任意单个字符的通配符。我们实际想要的应该是:

\.jpg$

反斜杠对这个"."进行转义。该正则表达式匹配以.jpg结尾的任意字符串。假如你不使用^或$字符,正则表达式的行为就象标准子串搜索。它们匹配在字符串里任何位置出现的单词或词组。

对所有的squid正则表达式类,你可以使用大小写敏感的选项。匹配是默认大小写敏感的。为了大小写不敏感,在ACL类型后面使用-i选项。例如:

acl Foo url_regex -i ^http://www
6.1.1.5 TCP端口号

使用对象:port,myport

该类型是相对的。值是个别的端口号或端口范围。回想一下TCP端口号是16位值,这样它的值必须大于0和小于65536。如下是一些示例:

acl Foo port 123
    
acl Bar port 1-1024
6.1.1.6 自主系统号

使用对象:src_as,dst_as

Internet路由器使用自主系统(AS)号来创建路由表。基本上,某个AS号指向被同一组织管理的IP网络范围。例如,我的ISP分配了如下网络块:134.116.0.0/16, 137.41.0.0/16, 206.168.0.0/16,和其他更多。在Internet路由表里,这些网络被公布为属于AS 3404。当路由器转发包时,它们典型的选择经过最少AS的路径。假如这些对你不重要,请不必关注它们。AS基础的ACL仅仅被网络gurus使用。

如下是基于AS的类型如何工作的:当squid首先启动时,它发送一条特殊的查询到某个whois服务器。查询语句基本是:“告诉我哪个IP网络属于该AS号”。这样的信息被RADB收集和管理。一旦Squid接受到IP网络列表,它相似的将它们作为IP基础的ACL对待。基于AS的类型仅仅在ISP将他们的RADB信息保持与日更新时才工作良好。某些ISP更新RADB比其他人做得更好;而许多根本不更新它。请注意squid仅仅在启动或者reconfigure时才将AS号转换为网络地址。假如ISP更新了它的RADB接口,除非你重启或者重配置squid,squid不会知道这个改变。

另外的情况是,在你的squid启动时,RADB可能不可到达。假如Squid不能联系上RADB服务器,它从访问控制配置里删除AS接口。默认的whois服务器是whois.ra.net,对许多用户来说太遥远了而不可信赖。


6.1.2 ACL类型

现在我们能把焦点放在ACL类型自身上。我在这里按照重要性的降序来列举它们。

6.1.2.1 src

IP地址在访问控制元素里是最普遍使用的。大部分站点使用IP地址来控制客户允许或不允许访问Squid。src类型指客户源IP地址。也就是说,当src ACL出现在访问控制列表里时,squid将它与发布请求的客户IP地址进行比较。

正常情况下你允许来自内网中主机的请求,并阻塞其他的。例如,假如你的单位使用192.168.0.0子网,你可以这样指定ACL:

acl MyNetwork src 192.168.0.0

假如你有许多子网,你能在同一个acl行里面列举它们:

acl MyNetwork src 192.168.0.0 10.0.1.0/24 10.0.5.0/24 172.16.0.0/12

squid有许多其他ACL类型用以检查客户地址。srcdomain类型比较客户的完整可验证域名。它要求反向DNS查询,这可能会延缓处理该请求。srcdom_regex ACL是类似的,但它允许你使用正则表达式来匹配域名。最后,src_as类型比较客户的AS号。

6.1.2.2 dst

dst类型指向原始服务器(目标)IP地址。在某些情况下,你能使用该类型来阻止你的用户访问特定web站点。然而,在使用dst ACL时你须谨慎。大部分squid接受到的请求有原始服务器主机名。例如:

GET http://www.web-cache.com/ HTTP/1.0

这里,www.web-cache.com是主机名。当访问列表规则包含了dst元素时,squid必须找到该主机名的IP地址。假如squid的IP缓存包含了该主机名的有效接口,这条ACL被立即检测。否则,在DNS查询忙碌时,squid会延缓处理该请求。这对某些请求来说会造成延时。为了避免延时,你该尽可能的使用dstdomain ACL类型来代替dst。

如下是简单的dst ACL示例:

acl AdServers dst 1.2.3.0/24

请注意,dst ACL存在的问题是,你试图允许或拒绝访问的原始服务器可能会改变它的IP地址。假如你不关心这样的改变,那就不必麻烦去升级squid.conf。你可以在acl行里放上主机名,但那样会延缓启动速度。假如你的ACL需要许多主机名,你也许该预处理配置文件,将主机名转换成IP地址。

6.1.2.3 myip

myip类型指Squid的IP地址,它被客户连接。当你在squid机上运行netstat -n时,你见到它们位于本地地址列。大部分squid安装不使用该类型。通常所有的客户连接到同一个IP地址,所以该ACL元素仅仅当系统有多个IP地址时才有用。

为了理解myip为何有用,考虑某个有两个子网的公司网络。在子网1的用户是程序员和工程师。子网2包括会计,市场和其他管理部门。这样情况下的squid有三个网络接口:一个连接子网1,一个连接子网2,第三个连接到外部因特网。


Figure 6-1. An application of the myip ACL



当正确的配置时,所有在子网1的用户连接到squid位于该子网的IP地址,类似的,子网2的用户连接到squid的第二个IP地址。这样你就可以给予子网1的技术部员工完全的访问权,然而限制管理部门的员工仅仅能访问工作相关的站点。ACL可能如下:

acl Eng myip 172.16.1.5
    
acl Admin myip 172.16.2.5

然而请注意,使用该机制你必须特别小心,阻止来自某个子网的用户连接squid位于另一子网的IP地址。否则,在会计和市场子网的聪明的用户,能够通过技术部子网进行连接,从而绕过你的限制。

6.1.2.4 dstdomain

在某些情况下,你发现基于名字的访问控制非常有用。你可以使用它们去阻塞对某些站点的访问,去控制squid如何转发请求,以及让某些响应不可缓存。dstdomain之所以非常有用,是因为它检查请求url里的主机名。然而首先我想申明如下两行的不同:

acl A dst www.squid-cache.org
    
acl B dstdomain www.squid-cache.org

A实际上是IP地址ACL。当Squid解析配置文件时,它查询www.squid-cache.org的IP地址,并将它们存在内存里。它不保存名字。假如在squid运行时IP地址改变了,squid会继续使用旧的地址。

然而dstdomain ACL以域名形式存储,并非IP地址。当squid检查ACL B时,它对URL的主机名部分使用字符串比较功能。在该情形下,它并不真正关心是否www.squid-cache.org的IP地址改变了。

使用dstdomain ACL的主要问题是某些URL使用IP地址代替主机名。假如你的目标是使用dstdomain ACL来阻塞对某些站点的访问,聪明的用户能手工查询站点的IP地址,然后将它们放在URL里。例如,下面的2行URL带来同样的页面:

http://www.squid-cache.org/docs/FAQ/

http://206.168.0.9/docs/FAQ/

第一行能被dstdomain ACL轻易匹配,但第二行不能。这样,假如你依靠dstdomain ACL,你也该同样阻塞所有使用IP地址代替主机名的请求。请见6.3.8章节。

6.1.2.5 srcdomain

srcdomain ACL也有点麻烦。它要求对每个客户IP地址进行所谓的反向DNS查询。技术上,squid请求对该地址的DNS PTR记录。DNS的响应--完整可验证域名(FQDN)--是squid匹配ACL值的东西。(请参考O'Reilly's DNS and BIND找到更多关于DNS PTR记录的信息)使用dst ACL,FQDN查询会导致延时。请求会被延缓处理直到FQDN响应返回。FQDN响应被缓存下来,所以srcdomain查询通常仅在客户首次请求时延时。

不幸的是,srcdomain查询有时不能工作。许多组织并没有保持他们的反向查询数据库与日更新。假如某地址没有PTR记录,ACL检查失败。在该情形下,请求可能会延时非常长时间(例如2分钟)直到DNS查询超时。假如你使用srcdomain ACL,请确认你自己的DNS in-addr.arpa区域配置正确并且在工作中。假如这样,你可以使用如下的ACL:

acl LocalHosts srcdomain .users.example.com
6.1.2.6 port

你很可能想使用port ACL来限制对某些原始服务器端口号的访问。就像我即将讲到的,squid其实不连接到某些服务,例如email和IRC服务。port ACL允许你定义单独的端口或端口范围。例如:

acl HTTPports port 80 8000-8010 8080

HTTP在设计上与其他协议类似,例如SMTP。这意味着聪明的用户通过转发email消息到SMTP服务器能欺骗squid。Email转发是垃圾邮件的主要原因之一,我们必须处理它们。历史上,垃圾邮件有真正的邮件服务器。然而近来,越来越多的垃圾邮件制造者使用开放HTTP代理来隐藏他们的踪迹。你肯定不想Squid被当成垃圾邮件转发器。假如是这样,你的IP地址很可能被许多邮件转发黑名单冻结(MAPS,ORDB,spamhaus等)。除email之外,还有其他许多TCP/IP服务是squid不与其通信的。这些包括IRC,Telnet,POP,和NNTP。你的针对端口的策略必须被配置成拒绝已知危险端口,并允许剩下的;或者允许已知安全端口,并拒绝剩下的。

我的态度比较保守,仅仅允许安全的端口。默认的squid.conf包含了下面的安全端口ACL:

acl Safe_ports port 80          # http
    
acl Safe_ports port 21          # ftp
    
acl Safe_ports port 443 563     # https, snews
    
acl Safe_ports port 70          # gopher
   
acl Safe_ports port 210         # wais
    
acl Safe_ports port 1025-65535  # unregistered ports
    
acl Safe_ports port 280         # http-mgmt
    
acl Safe_ports port 488         # gss-http
    
acl Safe_ports port 591         # filemaker
    
acl Safe_ports port 777         # multiling http
    

http_access deny !Safe_ports

这是个较明智的配置。它允许用户连接到任何非特权端口(1025-65535),但仅仅指定的特权端口可以被连接。假如你的用户试图访问某个URL如下:http://www.lrrr.org:123/,squid会返回访问拒绝错误消息。在某些情形下,为了让你的用户满意,你可能需要增加另外的端口号。

宽松的做法是,拒绝对特别危险的端口的访问。Squid FAQ包括了如下示例:

acl Dangerous_ports 7 9 19 22 23 25 53 109 110 119
    
    
http_access deny Dangerous_ports

使用Dangerous_ports的弊端是squid对几乎每个请求都要搜索整个列表。这对CPU造成了额外的负担。大多数情况下,99%到达squid的请求是对80端口的,它不出现在危险端口列表里。所有请求对该表的搜索不会导致匹配。当然,整数比较是快速的操作,不会显然影响性能。

(译者注:这里的意思是,两者都要对列表进行搜索和匹配。在第一种情况下,它搜索安全端口列表并匹配80,显然第一个元素就匹配成功了。而第二种情况中,会搜索危险端口列表并试图匹配80,当然危险端口不会包括80,所以每次对80的请求都要搜索完整个列表,这样就会影响性能。)

6.1.2.7 myport

squid也有myport ACL。port ACL指向原始服务器的端口号,myport指向squid自己的端口号,用以接受客户请求。假如你在http_port指令里指定不止一个端口号,那么squid就可以在不同的端口上侦听。

假如你将squid作为站点HTTP加速器和用户代理服务器,那么myport ACL特别有用。你可以在80上接受加速请求,在3128上接受代理请求。你可能想让所有人访问加速器,但仅仅你自己的用户能以代理形式访问squid。你的ACL可能如下:

acl AccelPort myport 80
    
acl ProxyPort myport 3128
    
acl MyNet src 172.16.0.0/22
    
    
    
http_access allow AccelPort         # anyone
    
http_access allow ProxyPort MyNet   # only my users
    
http_access deny ProxyPort          # deny others
6.1.2.8 method

method ACL指HTTP请求方法。GET是典型的最常用方法,接下来是POST,PUT,和其他。下例说明如何使用method ACL:

acl Uploads method PUT POST

Squid知道下列标准HTTP方法:GET, POST, PUT, HEAD, CONNECT, TRACE, OPTIONS和DELETE。另外,squid了解下列来自WEBDAV规范,RFC 2518的方法:PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK。某些Microsoft产品使用非标准的WEBDAV方法,所以squid也了解它们:BMOVE, BDELETE, BPROPFIND。最后,你可以在extension_methods指令里配置squid去理解其他的请求方法。请见附录A。

注意CONNECT方法非常特殊。它是用于通过HTTP代理来封装某种请求的方法(请见RFC 2817:Upgrading to TLS Within HTTP/1.1)。在处理CONNECT方法和远程服务器的端口号时应特别谨慎。就像前面章节讲过的一样,你不希望squid连接到某些远程服务。你该限制CONNECT方法仅仅能连接到HTTPS/SSL或NNTPS端口(443和563)。默认的squid.conf这样做:

acl CONNECT method CONNECT
    
acl SSL_ports 443 563
    
    
http_access allow CONNECT SSL_ports
    
http_access deny CONNECT

在该配置里,squid仅仅允许加密请求到端口443(HTTPS/SSL)和563(NNTPS)。CONNECT方法对其他端口的请求都被拒绝。

PURGE是另一个特殊的请求方法。它是Squid的专有方法,没有在任何RFC里定义。它让管理员能强制删除缓存对象。既然该方法有些危险,squid默认拒绝PURGE请求,除非你定义了ACL引用了该方法。否则,任何能访问cache者也许能够删除任意缓存对象。我推荐仅仅允许来自localhost的PURGE:

acl Purge method PURGE
    
acl Localhost src 127.0.0.1
    
    
http_access allow Purge Localhost
    
http_access deny Purge

关于从squid的缓存里删除对象,请见7.6章。

6.1.2.9 proto

该类型指URI访问(或传输)协议。如下是有效值:http, https (same as HTTP/TLS), ftp, gopher, urn, whois, 和cache_object。也就是说,这些是被squid支持的URL机制名字。例如,假如你想拒绝所有的FTP请求,你可以使用下列指令:

acl FTP proto FTP
    
http_access deny FTP

cache_object机制是squid的特性。它用于访问squid的缓存管理接口,我将在14.2章讨论它。不幸的是,它并非好名字,可能会被改变。默认的squid.conf文件有许多行限制缓存管理访问:

acl Manager proto cache_object
    
acl Localhost src 127.0.0.1
    
    
http_access allow Manager Localhost
    
http_access deny Manager

这些配置行仅允许来自本机地址的缓存管理请求,所有其他的缓存管理请求被拒绝。这意味着在squid机器上有帐号的人,能访问到潜在的敏感缓存管理信息。你也许想修改缓存管理访问控制,或对某些页面使用密码保护。我将在14.2.2章里谈论到。

6.1.2.10 time

time ACL允许你控制基于时间的访问,时间为每天中的具体时间,和每周中的每天。日期以单字母来表示,见如下表。时间以24小时制来表示。开始时间必须小于结束时间,这样在编写跨越0点的time ACL时可能有点麻烦。


Table 6-2. Day codes for the time ACL

Code Day
S Sunday
M Monday
T Tuesday
W Wednesday
H Thursday
F Friday
A Saturday
D All weekdays (M-F)

日期和时间由localtime()函数来产生。请确认你的计算机位于正确的时区,你也该让你的时钟与标准时间同步。

为了编写time ACL来匹配你的工作时间,你可以这样写:

acl Working_hours MTWHF 08:00-17:00

or:

acl Working_hours D 08:00-17:00

让我们看一个麻烦的例子。也许你是某个ISP,在下午8点到早上4点这段不忙的时间内放松访问。既然该时间跨越子夜,你不能编写“20:00-04:00”。代替的,你需要把它们分成两个ACL来写,或者使用否定机制来定义非忙时。例如:

acl Offpeak1 20:00-23:59
    
acl Offpeak2 00:00-04:00


http_access allow Offpeak1 ...

http_access allow Offpeak2 ...

另外,你可以这样写:

acl Peak 04:00-20:00
    
http_access allow !Peak ...

尽管squid允许,你也不应该在同一个time ACL里放置多个日期和时间范围列表。对这些ACL的解析不一定是你想象的那样。例如,假如你输入:

acl Blah time M 08:00-10:00 W 09:00-11:00

实际能做到的是:

acl Blah time MW 09:00-11:00

解析仅仅使用最后一个时间范围。正确的写法是,将它们写进两行:

acl Blah time M 08:00-10:00
    
acl Blah time W 09:00-11:00
6.1.2.11 ident

ident ACL匹配被ident协议返回的用户名。这是个简单的协议,文档是RFC 1413。它工作过程如下:

  • 1.用户代理(客户端)对squid建立TCP连接。

  • 2.squid连接到客户系统的ident端口(113)。

  • 3.squid发送一个包括两个TCP端口号的行。squid端的端口号可能是3128(或者你在squid.conf里配置的端口号),客户端的端口号是随机的。

  • 4.客户端的ident服务器返回打开第一个连接的进程的用户名。

  • 5.squid记录下用户名用于访问控制目的,并且记录到access.log。

当squid遇到对特殊请求的ident ACL时,该请求被延时,直到ident查询完成。这样,ident ACL可以对你的用户请求造成延时。

我们推荐仅仅在本地局域网中,并且大部分客户工作站运行ident服务时,才使用ident ACL。假如squid和客户工作站连在一个局域网里,ident ACL工作良好。跨广域网使用ident难以成功。

ident协议并非很安全。恶意的用户能替换他们的正常ident服务为假冒服务,并返回任意的他们选择的用户名。例如,假如我知道从administrator用户的连接总是被允许,那么我可以写个简单的程序,在回答每个ident请求时都返回这个用户名。

你可以使用ident ACL拦截cache(请见第9章)。当squid被配置成拦截cache时,操作系统假设它自己是原始服务器。这意味着用于拦截TCP连接的本地socket地址有原始服务器的IP地址。假如你在squid上运行netstat -n时,你可以看到大量的外部IP地址出现在本地地址栏里。当squid发起一个ident查询时,它创建一个新的TCP套接字,并绑定本地终点到同一个IP地址上,作为客户TCP连接的本地终点。既然本地地址并非真正是本地的(它可能与原始服务器IP地址相距遥远),bind()系统调用失败。squid将这个作为失败的ident查询来处理。

注意squid也有个特性,对客户端执行懒惰ident查询。在该情形下,在等待ident查询时,请求不会延时。在HTTP请求完成时,squid记录ident信息,假如它可用。你能使用ident_lookup_access指令来激活该特性,我将在本章后面讨论。

6.1.2.12 proxy_auth

squid有一套有力的,在某种程度上有点混乱的特性,用以支持HTTP代理验证功能。使用代理验证,客户的包括头部的http请求包含了验证信用选项。通常,这简单的是用户名和密码。squid解密信用选项,并调用外部验证程序以发现该信用选项是否有效。

squid当前支持三种技术以接受用户验证:HTTP基本协议,数字认证协议,和NTLM。基本认证已经发展了相当长时间。按今天的标准,它是非常不安全的技术。用户名和密码以明文同时发送。数字认证更安全,但也更复杂。基本和数字认证在RFC 2617文档里被描述。NTLM也比基本认证更安全。然而,它是Microsoft发展的专有协议。少数squid开发者已经基本完成了对它的反向工程。

为了使用代理验证,你必须配置squid使用大量的外部辅助程序。squid源代码里包含了一些程序,用于对许多标准数据库包括LDAP,NTLM,NCSA类型的密码文件,和标准Unix密码数据库进行认证。auth_param指令控制对所有辅助程序的配置。我将在12章里讨论这些细节。

auth_param指令和proxy_auth ACL是少数在配置文件里顺序重要的实例。你必须在proxy_auth ACL之前定义至少一个验证辅助程序(使用auth_param)。假如你没有这样做,squid打印出错误消息,并且忽略proxy_auth ACL。这并非致命错误,所以squid可以启动,但所有你的用户的请求可能被拒绝。

proxy_auth ACL取用户名作为值。然而,大部分安装里简单的使用特殊值REQUIRED:

auth_param ...
    
acl Auth1 proxy_auth REQUIRED

在该情况中,任何具有有效信用选项的请求会匹配该ACL。假如你需要细化控制,你可以指定独立的用户名:

auth_param ...
    
acl Auth1 proxy_auth allan bob charlie
    
acl Auth2 proxy_auth dave eric frank

代理验证不支持HTTP拦截,因为用户代理不知道它在与代理服务器,而非原始服务器通信。用户代理不知道在请求里发送Proxy-Authorization头部。见9.2章更多细节。

6.1.2.13 src_as

该类型检查客户源IP地址所属的具体AS号(见6.1.1.6关于squid如何将AS号映射到IP地址的信息)。作为示例,我们虚构某ISP使用AS 64222并且通告使用10.0.0.0/8,172.16.0.0/12,192.168.0.0/16网络。你可以编写这样的ACL,它允许来自该ISP地址空间的任何主机请求:

acl TheISP src 10.0.0.0/8
    
acl TheISP src 172.16.0.0/12
    
acl TheISP src 192.168.0.0/16
    
    
http_access allow TheISP

当然,你还可以这样写:

acl TheISP src_as 64222
    
    
http_access allow TheISP

第二种写法不但更短,而且假如ISP增加了新的网络,你不必更新ACL配置。

6.1.2.14 dst_as

dst_as ACL经常与cache_peer_access指令一起使用。在该方法中,squid使用与IP路由一致的方式转发cache丢失。考虑某ISP,它比其他ISP更频繁的更换路由。每个ISP处理他们自己的cache代理,这些代理能转发请求到其他代理。理论上,ISP A将ISP B网络里主机的cache丢失转发到ISP B的cache代理。使用AS ACL和cache_peer_access指令容易做到这点:

acl ISP-B-AS dst_as 64222
    
acl ISP-C-AS dst_as 64333
    
cache_peer proxy.isp-b.net parent 3128 3130
    
cache_peer proxy.isp-c.net parent 3128 3130
    
cache_peer_access proxy.isb-b.net allow ISP-B-AS
    
cache_peer_access proxy.isb-c.net allow ISP-C-AS

我将在第10章里讨论更多关于cache协作。

6.1.2.15 snmp_community

snmp_community ACL对SNMP查询才有意义,后者被snmp_access指令控制。例如,你可以这样写:

acl OurCommunityName snmp_community hIgHsEcUrItY
    
acl All src 0/0
    
    
snmp_access allow OurCommunityName

snmp_access deny All

在该情况中,假如community名字设置为hIgHsEcUrItY,SNMP查询才被允许。

6.1.2.16 maxconn

maxconn ACL指来自客户IP地址的大量同时连接。某些squid管理员发现这是个有用的方法,用以阻止用户滥用代理或者消耗过多资源。

maxconn ACL在请求超过指定的数量时,会匹配这个请求。因为这个理由,你应该仅仅在deny规则里使用maxconn。考虑如下例子:

acl OverConnLimit maxconn 4
    
    
http_access deny OverConnLimit

在该情况中,squid允许来自每个IP地址的同时连接数最大为4个。当某个客户发起第五个连接时,OverConnLimit ACL被匹配,http_access规则拒绝该请求。

6.1.2.17 arp

arp ACL用于检测cache客户端的MAC地址(以太网卡的物理地址)。地址解析协议(ARP)是主机查找对应于IP地址的MAC地址的方法。某些大学学生发现,在Microsoft Windows下,他们可以改变系统的IP地址到任意值,然后欺骗squid的基于地址的控制。这时arp功能就派上用场了,聪明的系统管理员会配置squid检查客户的以太网地址。

不幸的是,该特性使用非移植性代码。假如你运行Solaris或Linux,你能使用arp ACL。其他系统不行。当你运行./configure时增加--enable-arp-acl选项,就可以激活该功能。

arp ACL有另一个重要限制。ARP是数据链路层协议,假如客户主机和squid在同一子网,它才能工作。你不容易发现不同子网主机的MAC地址。假如在squid和你的用户之间有路由器存在,你可能不能使用arp ACL。

现在你知道何时去使用它们,让我们看看arp ACL实际上是怎样的。它的值是以太网地址,当使用ifconfig和arp时你能看到以太网地址。例如:

acl WinBoxes arp 00:00:21:55:ed:22
    
acl WinBoxes arp 00:00:21:ff:55:38
6.1.2.18 srcdom_regex

srcdom_regex ACL允许你使用正则表达式匹配客户域名。这与srcdomain ACL相似,它使用改进的的子串匹配。相同的限制是:某些客户地址不能反向解析到域名。作为示例,下面的ACL匹配以dhcp开头的主机名:

acl DHCPUser srcdom_regex -i ^dhcp

因为领头的^符号,该ACL匹配主机名dhcp12.example.com,但不匹配host12.dhcp.example.com。

6.1.2.19 dstdom_regex

dstdom_regex ACL也与dstdomain相似。下面的例子匹配以www开头的主机名:

acl WebSite dstdom_regex -i ^www\.

如下是另一个有用的正则表达式,用以匹配在URL主机名里出现的IP地址:

acl IPaddr dstdom_regex [0-9]$

这样可以工作,因为squid要求URL主机名完全可验证。既然全局顶级域名中没有以数字结尾的,该ACL仅仅匹配IP地址,它以数字结尾。

6.1.2.20 url_regex

url_regex ACL用于匹配请求URL的任何部分,包括传输协议和原始服务器主机名。例如,如下ACL匹配从FTP服务器的MP3文件请求:

acl FTPMP3 url_regex -i ^ftp://.*\.mp3$
6.1.2.21 urlpath_regex

urlpath_regex与url_regex非常相似,不过传输协议和主机名不包含在匹配条件里。这让某些类型的检测非常容易。例如,假设你必须拒绝URL里的"sex",但仍允许在主机名里含有"sex"的请求,那么这样做:

acl Sex urlpath_regex sex

另一个例子,假如你想特殊处理cgi-bin请求,你能这样捕获它们:

acl CGI1 urlpath_regex ^/cgi-bin

当然,CGI程序并非总在/cgi-bin/目录下,这样你应该编写其他的ACL来捕获它们。

6.1.2.22 browser

大部分HTTP请求包含了User-Agent头部。该头部的值典型如下:

Mozilla/4.51 [en] (X11; I; Linux 2.2.5-15 i686)

browser ACL对user-agent头执行正则表达式匹配。例如,拒绝不是来自Mozilla浏览器的请求,可以这样写:

acl Mozilla browser Mozilla
    
    
http_access deny !Mozilla

在使用browser ACL之前,请确认你完全理解cache接受到的User-Agent字符串。某些user-agent与它们的来源相关。甚至squid可以重写它转发的请求的User-Agent头部。某些浏览器例如Opera和KDE的Konqueror,用户可以对不同的原始服务器发送不同的user-agent字串,或者干脆忽略它们。

6.1.2.23 req_mime_type

req_mime_type ACL指客户HTTP请求里的Content-Type头部。该类型头部通常仅仅出现在请求消息主体里。POST和PUT请求可能包含该头部,但GET从不。你能使用该类型ACL来检测某些文件上传,和某些类型的HTTP隧道请求。req_mime_type ACL值是正则表达式。你可以这样编写ACL去捕获音频文件类型:

acl AuidoFileUploads req_mime_type -i ^audio/
6.1.2.24 rep_mime_type

该类型ACL指原始服务器的HTTP响应里的Content-Type头部。它仅在使用http_reply_access规则时才有用。所有的其他访问控制形式是基于客户端请求的。该ACL基于服务器响应。假如你想使用squid阻塞Java代码,你可以这样写:

acl JavaDownload rep_mime_type application/x-java
    
http_reply_access deny JavaDownload
6.1.2.25 ident_regex

在本节早些时讲过ident ACL。ident_regex允许你使用正则表达式,代替严格的字符串匹配,这些匹配是对ident协议返回的用户名进行。例如,如下ACL匹配包含数字的用户名:

acl NumberInName ident_regex [0-9]
6.1.2.26 proxy_auth_regex

该ACL允许对代理认证用户名使用正则表达式。例如,如下ACL匹配admin,administrator和administrators:

acl Admins proxy_auth_regex -i ^admin

6.1.3 外部ACL

Squid 2.5版本介绍了一个新特性:外部ACL。你可以指示squid发送某些信息片断到外部进程,然后外部的辅助程序告诉squid,数据匹配或不匹配。

squid附带着大量的外部ACL辅助程序;大部分用于确定命名用户是不是某个特殊组的成员。请见12.5章关于这些程序的描述,以及关于如何编写你自己的程序的信息。现在,我解释如何定义和使用外部ACL类型。

external_acl_type指令定义新的外部ACL类型。如下是通用语法:

external_acl_type type-name [options] format helper-command

type-name是用户定义的字串。你也可以在acl行里引用它。Squid当前支持如下选项(options):

ttl=n

时间数量,单位是秒,用以缓存匹配值的时间长短。默认是3600秒,或1小时。

negative_ttl=n

时间数量,单位是秒,用以缓存不匹配值的时间长短。默认是3600秒,或1小时。

concurrency=n

衍生的辅助程序的数量,默认是5。

cache=n

缓存结果的最大数量。默认是0,即不限制cache大小。格式是以%字符开始的一个或多个关键字。squid当前支持如下格式:

%LOGIN

从代理验证信用选项里获取的用户名。

%IDENT

从RFC 1413 ident获取的用户名。

%SRC

客户端IP地址。

%DST

原始服务器IP地址。

%PROTO

传输协议(例如HTTP,FTP等)

%PORT

原始服务器的TCP端口。

%METHOD

HTTP请求方法。

%{Header}

HTTP请求头部的值;例如,%{User-Agent}导致squid发送这样的字串到验证器:

"Mozilla/4.0 (compatible; MSIE 6.0; Win32)"
%{Hdr:member}

选择某些数量的基于列表的HTTP头部,例如Caceh-Control;例如,给出如下HTTP头部:

X-Some-Header: foo=xyzzy, bar=plugh, foo=zoinks

对%{X-Some-Header:foo}的取值,squid发送这样的字串到外部ACL进程:

foo=xyzzy, foo=zoinks
%{Hdr:;member}

与%{Hdr:member}相同,除了";"是列表分隔符外。你能使用任何非字母数字的字符作为分隔符。辅助命令是squid为辅助程序衍生的命令。你也可以在这里包含命令参数。例如,整条命令可能类似如此:

/usr/local/squid/libexec/my-acl-prog.pl -X -5 /usr/local/squid/etc/datafile

将这些放在一个长行里。squid不支持如下通过反斜杠分隔长行的技术,所以请记住所有这些必须放在单行里:

external_acl_type MyAclType cache=100 %LOGIN %{User-Agent} \
        
    /usr/local/squid/libexec/my-acl-prog.pl -X -5 \
        
    /usr/local/squid/share/usernames \
        
    /usr/local/squid/share/useragents

现在你知道如何定义外部ACL,下一步是编写引用它的acl行。这相对容易,语法如下:

acl acl-name external type-name [args ...]

如下是个简单示例:

acl MyAcl external MyAclType

squid接受在type-name后面的任意数量的参数。这些在每个请求里被发送到辅助程序。请见12.5.3章,我描述了unix_group辅助程序,作为该功能的示例。


6.1.4 处理长ACL列表

ACL列表某些时候非常长。这样的列表在squid.conf文件里难以维护。你也可能想从其他资源里自动产生squid ACL列表。在如此情况下,你可以从外部文件里包含ACL列表。语法如下:

acl name "filename"

这里的双引号指示squid打开filename,并且将它里面的内容分配给ACL。例如,如下的ACL太长了:

acl Foo BadClients 1.2.3.4 1.2.3.5 1.2.3.6 1.2.3.7 1.2.3.9 ...

你可以这样做:

acl Foo BadClients "/usr/local/squid/etc/BadClients"

将IP地址放在BadClients文件里:

1.2.3.4
    
1.2.3.5
    
1.2.3.6
    
1.2.3.7
    
1.2.3.9
    
...

文件可以包含以#开头的注释。注意在该文件里的每个IP地址必须是一个单独的行。acl行里的任何地方,以空格来分隔值,新行是包含ACL值的文件的分界。


6.1.5 Squid如何匹配访问控制元素

理解squid如何搜索ACL元素去匹配是很重要的。当ACL元素有多个值时,任何单个值能导致匹配。换句话说,squid在检查ACL元素值时使用OR逻辑。当squid找到第一个值匹配时,它停止搜索。这意味着把最可能匹配的值放在列表开头处,能减少延时。

让我们看一个特殊的例子,考虑如下ACL定义:

acl Simpsons ident Maggie Lisa Bart Marge Homer

当squid在访问列表里遇到Simpsons ACL时,它执行ident查询。让我们看一下,当用户ident服务返回Marge时,会发生什么呢?squid的ACL代码在成功匹配Marge前,会先后将这个值与Maggie,Lisa,和Bart对比。当搜索完成时,我们认为Simpsons ACL匹配了这个请求。

实际上,这有点欺骗。ident ACL值并非存储在无序列表里。它们存储在splay tree中。这意味着,在非匹配事件中,squid不会搜索完所有的名字。对一个splay tree搜索N个条目需要记录N个比较。许多其他的ACL类型也使用splay tree。然而,基于正则表达式的类型不使用。

既然正则表达式不能这样存储,它们以链表形式存储。这使得在大链表里它们特别低效,特别是不匹配链表里任何正则表达式的请求。为了改进这个形式,当匹配发生时,squid将正则表达式移到列表的顶部。实际上,因为ACL匹配代码的天然特性,squid将匹配的条目移到列表的第二个位置。这样,普通的匹配值自然移到ACL列表的顶部,这样会减少比较数量。

让我们看另一个简单示例:

acl Schmever port 80-90 101 103 107 1 2 3 9999

该ACL匹配到原始服务器80-90端口,和其他独立端口的请求。对80端口的请求,squid通过查看第一个值就匹配了该ACL。对9999端口,其他每个值都先被检查。对某个不在列表里的端口,squid要检查所有值才宣布它不匹配。就像我已经讲过的,将最常用的值放在第一位能优化ACL匹配。



6.2 访问控制规则

前面提过,ACL元素是建立访问控制的第一步。第二步是访问控制规则,用来允许或拒绝某些动作。在早先的例子里,你已见过http_access规则。squid有大量其他的访问控制列表:

http_access

这是最重要的访问控制列表。它决定哪些客户HTTP请求被允许,和哪些被拒绝。假如http_access配置错误,squid cache容易遭受攻击或被不当利用。

http_reply_access

http_reply_access与http_access类似。不同之处是前者在squid接受到来自原始服务器或上级代理的响应时,才会被检测。大部分访问控制基于客户请求的方式,对这些使用http_access就够了。然而,某些人喜欢基于响应内容类型来允许或拒绝请求。更多信息请见6.3.9章。

icp_access

假如你的squid被配置来服务ICP响应(见10.6章),那么该使用icp_access列表。大部分情况下,你该仅仅允许来自邻居cache的ICP请求。

no_cache

你能使用no_cache访问列表来指示squid,它不必存储某些响应(在磁盘或内存里)。该列表典型的与dst,dstdomain,url_regex ACL结合使用。

对no_cache使用"否"条件,这样的双重否定会导致某些混乱。被no_cache列表拒绝的请求不被缓存。换句话说,no_cache deny...是让目标不被缓存。见6.3.10章的示例。

miss_access

miss_access列表主要用于squid的邻居cache。它决定squid怎样处理cache丢失的请求。如果squid使用集群技术,那么该功能必需。见6.3.7的示例。

redirector_access

该访问列表决定哪个请求被发送到重定向进程(见11章)。默认情况下,假如你使用重定向器,那么所有的请求都通过重定向器。你可以使用redirector_access列表来阻止某些请求被重写。这点特别有用,因为这样的访问列表,使重定向器相对于访问控制系统,接受的请求信息要少一些。

ident_lookup_access

ident_lookup_access列表与redirector_access类似。它允许你对某些请求执行懒惰ident查询。squid默认不发布ident查询。假如请求被ident_lookup_access规则(或ident ACL)允许,那么squid才会进行ident查询。

always_direct

该访问列表影响squid怎样处理与邻居cache转发cache丢失。通常squid试图转发cache丢失到父cache,和/或squid使用ICP来查找临近cache响应。然而,当请求匹配always_direct规则时,squid直接转发请求到原始服务器。

使用该规则,对"allow"规则的匹配导致squid直接转发请求,见10.4.4章的更多细节和示例。

never_direct

never_direct与always_direct相反。匹配该列表的cache丢失请求必须发送到邻居cache。这点对在防火墙之后的代理特别有用。使用该列表,对"allow"规则的匹配导致squid转发请求到邻居cache。见10.4.3章的更多细节和示例。

snmp_access

该访问列表应用到发送给squid的SNMP端口的查询。你能配合该列表使用的ACL是snmp_community和src。假如你确实想使用它,那也能使用srcdomain,srcdom_regex,和src_as。见14.3章的示例。

broken_posts

该访问列表影响squid处理某些POST请求的方法。某些老的用户代理在请求主体的结尾处发送一个特别的回车换行符。那就是说,消息主体比content-length头部指示的长度要多2个字节。更糟糕的是,某些老的HTTP服务器实际上依赖于这种不正确的行为。当请求匹配该访问列表时,squid模拟这种客户端并且发送特殊的回车换行符。Squid有大量的使用ACL元素的其他配置指令。它们中的某些过去是全局配置,后被修改来使用ACL以提供更灵活的控制。

cache_peer_access

该访问列表控制发送到邻居cache的HTTP请求和ICP/HTCP查询。见10.4.1章的更多信息和示例。

reply_body_max_size

该访问列表限制对HTTP响应主体的最大可接受size。见附录A的更多信息。

delay_access

该访问规则列表控制是否延时池被应用到某个请求的cache丢失响应。见附录C。

tcp_outgoing_address

该访问列表绑定服务端TCP连接到指定的本地IP地址。见附录A。

tcp_outgoing_tos

该访问列表能设置到原始服务器和邻居cache的TCP连接的不同TOS/Diffserv值,见附录A。

header_access

使用该指令,你能配置squid从它转发的请求里删除某些HTTP头部。例如,你也许想过滤掉发送到某些原始服务器的请求里的Cookie头部。见附录A。

header_replace

该指令允许你替换,而不是删除,HTTP头部的内容。例如,你能设置user-agent头部为假值,满足某些原始服务器的要求,但仍保护你的隐私。见附录A。


6.2.1 访问规则语法

访问控制规则的语法如下:

access_list allow|deny [!]ACLname ...

例如:

http_access allow MyClients
    
http_access deny !Safe_Ports
    
http_access allow GameSites AfterHours

当读取配置文件时,squid仅仅扫描一遍访问控制行。这样,在访问列表里引用ACL元素之前,你必须在acl行里定义它们。甚至,访问列表规则的顺序也非常重要。你以怎样的顺序编写访问列表,那么squid就按怎样的顺序来检查它们。将最常用的ACL放在列表的开始位置,可以减少squid的CPU负载。

对大部分访问列表,deny和allow的意义明显。然而,它们中的某些,却并非如此含义清楚。请谨慎的编写always_direct,never_direct,和no_cache规则。在always_direct中,allow规则意味着匹配的请求直接转发到原始服务器。always_direct deny规则意味着匹配的请求不强迫发送到原始服务器,但假如邻居cache不可到达,那可能还是会这么做。no_cache规则也有点麻烦。这里,你必须对不必被cache的请求使用deny。


6.2.2 Squid如何匹配访问规则

回想一下squid在搜索ACL元素时使用的“或”逻辑。在acl里的任何单值都可以导致匹配。

然而,访问规则恰好相反。对http_access和其他规则设置,squid使用“与”逻辑。考虑如下示例:

access_list allow ACL1 ACL2 ACL3

对该匹配规则来说,请求必须匹配ACL1,ACL2,ACL3中的任何一个。假如这些ACL中的任何一个不匹配请求,squid停止搜索该规则,并继续处理下一条。对某个规则来说,将最少匹配的ACL放在首位,能使效率最佳。考虑如下示例:

acl A method http
    
acl B port 8080
    
    
http_access deny A B

该http_access规则有点低效,因为A ACL看起来比B ACL更容易匹配。反转顺序应该更好,以便squid仅仅检查一个ACL,而不是两个:

http_access deny B A

人们易犯的典型错误是编写永不正确的规则。例如:

acl A src 1.2.3.4
    
acl B src 5.6.7.8
    
http_access allow A B

该规则永不正确,因为某个源IP地址不可能同时等同于1.2.3.4和5.6.7.8。这条规则的真正意图是:

acl A src 1.2.3.4 5.6.7.8
http_access allow A

对某个ACL值的匹配算法是,squid在访问列表里找到匹配规则时,搜索终止。假如没有访问规则导致匹配,默认动作是列表里最后一条规则的取反。例如,考虑如下简单访问配置:

acl Bob ident bob
    
http_access allow Bob

假如用户Mary发起请求,她会被拒绝。列表里最后的(唯一的)规则是allow规则,它不匹配用户名mary。这样,默认的动作是allow的取反,故请求被拒绝。类似的,假如最后的规则是deny规则,默认动作是允许请求。在访问列表的最后加上一条,明确允许或拒绝所有请求,是好的实际做法。为清楚起见,以前的示例应该如此写:

acl All src 0/0
    
acl Bob ident bob
    
http_access allow Bob
    
http_access deny All

src 0/0 ACL表示匹配每一个和任意类型的请求。


6.2.3 访问列表风格

squid的访问控制语法非常强大。大多数情况下,你可以使用两种或多种方法来完成同样的事。通常,你该将更具体的和受限制的访问列表放在首位。例如,如下语句并非很好:

acl All src 0/0
    
acl Net1 src 1.2.3.0/24
    
acl Net2 src 1.2.4.0/24
    
acl Net3 src 1.2.5.0/24
    
acl Net4 src 1.2.6.0/24
    
acl WorkingHours time 08:00-17:00
    
    
http_access allow Net1 WorkingHours
    
http_access allow Net2 WorkingHours
    
http_access allow Net3 WorkingHours
    
http_access allow Net4
    
http_access deny All

假如你这样写,访问控制列表会更容易维护和理解:

http_access allow Net4
    
http_access deny !WorkingHours
    
http_access allow Net1
    
http_access allow Net2
    
http_access allow Net3
    
http_access deny All

无论何时,你编写了一个带两个或更多ACL元素的规则,建议你在其后紧跟一条相反的,更广泛的规则。例如,默认的squid配置拒绝非来自本机IP地址的cache管理请求,你也许试图这样写:

acl CacheManager proto cache_object
    
acl Localhost src 127.0.0.1

http_access deny CacheManager !Localhost

然而,这里的问题是,你没有允许确实来自本机的cache管理请求。随后的规则可能导致请求被拒绝。如下规则就产生了问题:

acl CacheManager proto cache_object
    
acl Localhost src 127.0.0.1
    
acl MyNet 10.0.0.0/24
   
acl All src 0/0
    
    
http_access deny CacheManager !Localhost
    
http_access allow MyNet
    
http_access deny All

既然来自本机的请求不匹配MyNet,它被拒绝。编写本规则的更好方法是:

http_access allow CacheManager localhost
    
http_access deny CacheManager
    
http_access allow MyNet
    
        http_access deny All

6.2.4 延时检查

某些ACL不能在一个过程里被检查,因为必要的信息不可用。ident,dst,srcdomain和proxy_auth类型属于该范畴。当squid遇到某个ACL不能被检查时,它延迟决定并且发布对必要信息的查询(IP地址,域名,用户名等)。当信息可用时,squid再次在列表的开头位置检查这些规则。它不会从前次检查剩下的位置继续。假如可能,你应该将这些最可能被延时的ACL放在规则的顶部,以避免不必要的,重复的检查。

因为延时的代价太大,squid会尽可能缓存查询获取的信息。ident查询在每个连接里发生,而不是在每个请求里。这意味着,当你使用ident查询时,持续HTTP连接切实对你有利。DNS响应的主机名和IP地址也被缓存,除非你使用早期的外部dnsserver进程。代理验证信息被缓存,请见6.1.2.12章节的描述。


6.2.5 减缓和加速规则检查

Squid内部考虑某些访问规则被快速检查,其他的被减缓检查。区别是squid是否延迟它的决定,以等待附加信息。换句话说,在squid查询附加信息时,某个减缓检查会被延时,例如:
  • 反向DNS查询:客户IP地址的主机名

  • RFC 1413 ident查询:客户TCP连接的用户名

  • 验证器:验证用户信用

  • DNS转发查询:原始服务器的IP地址

  • 用户定义的外部ACL

某些访问规则使用快速检查。例如,icp_access规则被快速检查。为了快速响应ICP查询,它必须被快速检查。甚至,某些ACL类型例如proxy_auth,对ICP查询来说无意义。下列访问规则被快速检查:

header_access
    
reply_body_max_size
    
reply_access
    
ident_lookup
    
delay_access
    
miss_access
    
broken_posts
    
icp_access
    
cache_peer_access
   
redirector_access
    
snmp_access

下列ACL类型可能需要来自外部数据源(DNS,验证器等)的信息,这样与快速的访问规则不兼容:

srcdomain, dstdomain, srcdom_regex, dstdom_regex
    
dst, dst_as

proxy_auth

ident

external_acl_type

这意味着,例如,不能在header_access规则里使用ident ACL。



6.3 常见用法

因为访问控制可能很复杂,本节包含一些示例。它们描述了一些访问控制的普通用法。你可以在实际中调整它们。


6.3.1 仅仅允许本地客户

几乎每个squid安装后,都限制基于客户IP地址的访问。这是保护你的系统不被滥用的最好的方法之一。做到这点最容易的方法是,编写包含IP地址空间的ACL,然后允许该ACL的HTTP请求,并拒绝其他的。

acl All src 0/0
    
acl MyNetwork src 172.16.5.0/24 172.16.6.0/24
   
   
http_access allow MyNetwork
    
http_access deny All

也许该访问控制配置过于简单,所以你要增加更多行。记住http_access的顺序至关重要。不要在deny all后面增加任何语句。假如必要,应该在allow MyNetwork之前或之后增加新规则。


6.3.2 阻止恶意客户

因为某种理由,你也许有必要拒绝特定客户IP地址的访问。这种情况可能发生,例如,假如某个雇员或学生发起一个异常耗费网络带宽或其他资源的web连接,在根本解决这个问题前,你可以配置squid来阻止这个请求:

acl All src 0/0
    
acl MyNetwork src 172.16.5.0/24 172.16.6.0/24
    
acl ProblemHost src 172.16.5.9
    
    
http_access deny ProblemHost
    
http_access allow MyNetwork
    
        http_access deny All

6.3.3 内容过滤

阻塞对特定内容的访问是棘手的问题。通常,使用squid进行内容过滤最难的部分,是被阻塞的站点列表。你也许想自己维护一个这样的列表,或从其他地方获取一个。squid FAQ的“访问控制”章节有链接指向免费的可用列表。

使用这样的列表的ACL语法依赖于它的内容。假如列表包含正则表达式,你可能要这样写:

acl PornSites url_regex "/usr/local/squid/etc/pornlist"
    
    
http_access deny PornSites

另一方面,假如列表包含原始服务器主机名,那么简单的更改url_regex为dstdomain。


6.3.4 在工作时间的受限使用

某些公司喜欢在工作时间限制web使用,为了节省带宽,或者是公司政策禁止员工在工作时做某些事情。关于这个最难的部分是,所谓合适的和不合适的internet使用之间的区别是什么。不幸的是,我不能对这个问题作出回答。在该例子里,假设你已收集了一份web站点域名列表,它包含已知的不适合于你的站点名,那么这样配置squid:

acl NotWorkRelated dstdomain "/usr/local/squid/etc/not-work-related-sites"
    
acl WorkingHours time D 08:00-17:30
    
http_access deny !WorkingHours NotWorkRelated

请注意在该规则里首先放置!WorkingHours ACL。相对于字符串或列表,dstdomain ACL产生的性能代价较大,但time ACL检查却很简单。下面的例子,进一步理解如何结合如下方法和前面描述的源地址控制,来控制访问。

acl All src 0/0
    
acl MyNetwork src 172.16.5.0/24 172.16.6.0/24
    
acl NotWorkRelated dstdomain "/usr/local/squid/etc/not-work-related-sites"
    
acl WorkingHours time D 08:00-17:30
    
    
http_access deny !WorkingHours NotWorkRelated
    
http_access allow MyNetwork
    
http_access deny All

上面的方法可行,因为它实现了我们的目标,在工作时间内拒绝某些请求,并允许来自你自己网络的请求。然而,它也许有点低效。注意NotWorkRelated ACL在所有请求里被搜索,而不管源IP地址。假如那个列表非常长,在列表里对外部网络请求的搜索,纯粹是浪费CPU资源。所以,你该这样改变规则:

http_access deny !MyNetwork
    
http_access deny !WorkingHours NotWorkRelated
    
http_access Allow All

这里,将代价较大的检查放在最后。试图滥用squid的外部用户不会再浪费你的CPU资源。


6.3.5 阻止squid与非HTTP服务器会话

你必须尽可能不让squid与某些类型的TCP/IP服务器通信。例如,永不能够使用squid缓存来转发SMTP传输。我在前面介绍port ACL时提到过这点。然而,它是至关重要的,所以再强调一下。

首先,你必须关注CONNECT请求方法。使用该方法的用户代理,通过HTTP代理来封装TCP连接。它被创造用于HTTP/TLS请求,这是CONNECT方法的主要用途。某些用户代理也可以通过防火墙代理来封装NNTP/TLS传输。所有其他的用法应该被拒绝。所以,你的访问列表,应该仅仅允许到HTTP/TLS和NNTP/TLS端口的CONNECT请求。

第二,你应该阻止squid连接到某些服务,例如SMTP。你也可以开放安全端口和拒绝危险端口。我对这两种技术给出示例。让我们看看默认的squid.conf文件提供的规则:

acl Safe_ports port 80          # http
    
acl Safe_ports port 21          # ftp
    
acl Safe_ports port 443 563     # https, snews
    
acl Safe_ports port 70          # gopher
    
acl Safe_ports port 210         # wais
    
acl Safe_ports port 280         # http-mgmt
    
acl Safe_ports port 488         # gss-http
    
acl Safe_ports port 591         # filemaker
    
acl Safe_ports port 777         # multiling http
    
acl Safe_ports port 1025-65535  # unregistered ports
    
acl SSL_ports port 443 563
    
acl CONNECT method CONNECT
    
    
http_access deny !Safe_ports
    
http_access deny CONNECT !SSL_ports
    
<additional http_access lines as necessary...>

Safe_ports ACL列举了所有的squid有合法响应的特权端口(小于1024)。它也列举了所有非特权端口范围。注意Safe_ports ACL也包括了安全HTTP和NNTP端口(443和563),即使它们也出现在SSL_ports ACL里。这是因为Safe_ports在规则里首先被检查。假如你交换了两个http_access行的顺序,你也许能从Safe_ports列表里删除443和563,但没必要这么麻烦。与此相似的其他方法是,列举已知不安全的特权端口:

acl Dangerous_ports 7 9 19 22 23 25 53 109 110 119
    
acl SSL_ports port 443 563
    
acl CONNECT method CONNECT
    
    
http_access deny Dangerous_ports
    
http_access deny CONNECT !SSL_ports
    
<additional http_access lines as necessary...>

假如你不熟悉这些奇特的端口号,也不要担心。你可以阅读unix系统的/etc/services文件,或者阅读IANA的注册TCP/UDP端口号列表:

http://www.iana.org/assignments/port-numbers

6.3.6 授予某些用户特殊的访问

使用基于用户名进行访问控制的组织,通常需要授予某些用户特殊的权限。在该简单示例里,有三个元素:所有授权用户,管理员用户名,限制访问的web站点列表。正常的用户不允许访问受限站点,但管理员有维护这个列表的任务。他们必须连接到所有服务器,去验证某个特殊站点是否该放到受限站点列表里。如下显示如何完成这个任务:

auth_param basic program /usr/local/squid/libexec/ncsa_auth

    /usr/local/squid/etc/passwd

acl Authenticated proxy_auth REQUIRED

acl Admins proxy_auth Pat Jean Chris

acl Porn dstdomain "/usr/local/squid/etc/porn.domains"

acl All src 0/0


http_access allow Admins

http_access deny Porn

http_access allow Authenticated

http_access deny All

首先,有三个ACL定义。Authenticated ACL匹配任何有效的代理验证信用。Admins ACL匹配来自用户Pat,Jean,和Chris的有效信用。Porn ACL匹配某些原始服务器主机名,它们在porn.domains文件里找到。

该示例有四个访问控制规则。第一个仅仅检查Admins ACL,允许所有来自Pat,Jean,和Chris的请求。对其他用户,squid转移到下一条规则。对第二条规则,假如原始主机名位于porn.domains文件,那么该请求被拒绝。对不匹配Porn ACL的请求,squid转移到第三条规则。第三条规则里,假如请求包含有效的验证信用,那么该请求被允许。外部验证器(这里的ncsa_auth)决定是否信用有效。假如它们无效,最后的规则出现,该请求被拒绝。注意ncsa_auth验证器并非必需。你可以使用12章里描述的任何验证辅助程序。


6.3.7 阻止邻近cache的滥用

假如你使用了cache集群,你必须付出多余的小心。cache通常使用ICP来发现哪些对象被缓存在它们的邻居机器上。你仅该接受来自已知授权的邻居cache的ICP查询。

更进一步,通过使用miss_access规则列表,你能配置squid强制限制邻近关系。squid仅仅在cache丢失,没有cache命中时才检查这些规则。这样,在miss_access列表生效前,所有请求必须首先通过http_access规则。

在本示例里,有三个独立的ACL。一个是直接连接到cache的本地用户;另一个是子cache,它被允许来转发cache丢失的请求;第三个是邻近cache,它必须从不转发导致cache丢失的请求。如下是它们如何工作:

alc All src 0/0
    
acl OurUsers src 172.16.5.0/24

acl ChildCache src 192.168.1.1

acl SiblingCache src 192.168.3.3


http_access allow OurUsers

http_access allow ChildCache

http_access allow SiblingCache

http_access deny All

miss_access deny SiblingCache

icp_access allow ChildCache

icp_access allow SiblingCache

        icp_access deny All

6.3.8 使用IP地址拒绝请求

我在6.1.2.4章节里提过,dstdomain类型是阻塞对指定原始主机访问的好选择。然而,聪明的用户通过替换URL主机名成IP地址,能够绕过这样的规则。假如你想彻底阻止这样的请求,你可能得阻塞所有包含IP地址的请求。你可以使用重定向器,或者使用dstdom_regex ACL来完成。例如:

acl IPForHostname dstdom_regex ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$
    
    
        http_access deny IPForHostname

6.3.9 http_reply_access示例

回想一下,当squid检查http_reply_access规则时,响应的内容类型是唯一的可用新信息。这样,你能保持http_reply_access规则简单化。你只需检查rep_mime_type ACL。例如,如下示例告诉你如何拒绝某些内容类型的响应:

acl All src 0/0

acl Movies rep_mime_type video/mpeg

acl MP3s rep_mime_type audio/mpeg

http_reply_access deny Movies

http_reply_access deny MP3s

http_reply_access allow All

你不必在http_reply_access列表里重复http_access规则。这里的allow ALL规则不意味着所有对squid的请求被允许。任何被http_access拒绝的请求,从来不会再被http_reply_access检查。


6.3.10 阻止对本地站点的cache命中

假如你有许多原始服务器在本地网络中,你也许想配置squid,以便它们的响应永不被缓存。因为服务器就在附近,它们不会从cache命中里获益很多。另外,它释放存储空间给其他远程原始主机。

第一步是定义本地服务器的ACL。你可能使用基于地址的ACL,例如:

acl LocalServers dst 172.17.1.0/24

假如服务器不位于单一的子网,你也许该创建dstdomain ACL:

acl LocalServers dstdomain .example.com

接下来,你简单的使用no_cache access规则,拒绝这些服务器的cache:

no_cache deny LocalServers

no_cache规则不会阻止客户发送请求到squid。没有办法配置squid阻止这样的请求进来。代替的,你必须配置用户代理自身。

假如你在squid运行一段时间后增加no_cache规则,cache可能包含一些匹配新规则的对象。在squid2.5之前的版本,这些以前缓存的对象可能以cache命中返回。然而现在,squid清除掉所有匹配no_cache规则的缓存响应。



6.4 测试访问控制

访问控制配置越长,它就越复杂。强烈建议你在将它们用于产品环境之前,先测试访问控制。当然,首先做的事是确认squid能正确的解析配置文件。使用-k parse功能:

% squid -k parse

为了进一步测试访问控制,你需要安装一个用于测试的squid。容易做到的方法是,编译另一份squid到其他$prefix位置。例如:

% tar xzvf squid-2.5.STABLE4.tar.gz
    % cd squid-2.5.STABLE4
    % ./configure --prefix=/tmp/squid ...
    % make && make install

在安装完后,你必须编辑新的squid.conf文件,更改一些指令。假如squid已经运行在默认端口,那么请改变http_port。为了执行简单的测试,创建单一的小目录:

cache_dir ufs /tmp/squid/cache 100 4 4

假如你不想重编译squid,你也能创建一份新的配置文件。该方法的弊端是你必须设置所有的日志文件路径为临时目录,以便不会覆盖真正的文件。

你可以使用squidclient程序来轻松的测试某些访问控制。例如,假如你有一条规则,它依赖于原始服务器主机名(dstdomain ACL),或者某些URL部分(url_regex或urlpath_regex),简单的输入你期望被允许或拒绝的URI:

% squidclient -p 4128 http://blocked.host.name/blah/blah

or:

% squidclient -p 4128 http://some.host.name/blocked.ext

某些类型的请求难以控制。假如你有src ACL,它们阻止来自外部网络的请求,你也许需要从外部主机测试它们。测试time ACL也很困难,除非你能改变系统时钟,或者等待足够长时间。

你能使用squidclient的-H选项来设置任意请求头。例如,假如你需要测试browser ACL,那么这样做:

% squidclient -p 4128 http://www.host.name/blah  \
          
      -H 'User-Agent: Mozilla/5.0 (compatible; Konqueror/3)\r\n'

更多的复杂请求,包括多个头部,请参考16.4章中描述的技术。

你也许考虑制订一项cron,定期检查ACL,以发现期望的行为,并报告任何异常。如下是可以起步的示例shell脚本:

#!/bin/sh
    
set -e
    
TESTHOST="www.squid-cache.org"
    
    
# make sure Squid is not proxying dangerous ports
    
#
    
ST=`squidclient 'http://$TESTHOST:25/' | head -1 | awk '{print $2}'`
    
if test "$ST" != 403 ; then
    
        echo "Squid did not block HTTP request to port 25"
    
fi
    
    
# make sure Squid requires user authentication
    
#
    
ST=`squidclient 'http://$TESTHOST/' | head -1 | awk '{print $2}'`
    
if test "$ST" != 407 ; then
    
        echo "Squid allowed request without proxy authentication"
    
fi
    
    
# make sure Squid denies requests from foreign IP addresses
    
# elsewhere we already created an alias 192.168.1.1 on one of
    
# the system interfaces
    
#
    
EXT_ADDR=192.168.1.1
    
ST=`squidclient -l $EXT_ADDR 'http://$TESTHOST/' | head -1 | awk '{print $2}'`
    
if test "$ST" != 403 ; then
    
        echo "Squid allowed request from external address $EXT_ADDR"
    
fi
    
exit 0