初始化cache # /s/squid/sbin/squid -z 启动squid # /s/squid/etc/rc.d/squid.sh start @ 设置ipfilter 为了实现透明的功能,需要将所有对80端口的请求转到squid的3128端口上,以实现对客户端透明的功能。在/etc/ipnat.rules中加入: rdr fxp1 0.0.0.0/0 port 80 -> 127.0.0.1 3128 刷新ipnat配置: # ipnat -Cf /etc/ipnat.rules 一切完成,客户端无需任何设置,根本感觉不到代理的存在,而且访问速度明显变快
透明代理 httpd_accel_host virtual httpd_accel_port 80 httpd_accel_with_proxy on httpd_accel_user_host_header on 在Linux上,可以用iptables/ipchains直接将对Web端口80的请求直接转发到Squid端口3128,由Squid接手,而用户浏览器仍然认为它访问的是对方的80端口。例如以下这条命令:
iptables -t nat -A PREROUTING -s 192.168.0.200/32 -p tcp --dport 80 -j REDIRECT 3128 就是将192.168.0.200的所有针对80端口的访问重定向到3128端口。
普通代理服务器 在这个例子中,代理服务器同时也是网关,内部网络接口eth0的IP地址为192.168.0.1,外部网络接口eth1的IP地址为202.103.x.x。下面是一个基本的代理所需要配置选项: http_port 192.168.0.1:3128 默认端口是3128,当然也可以是任何其它端口,只要不与其它服务发生冲突即可。为了安全起见,在前面加上IP地址,Squid就不会监听外部的网络接口。 下面的配置选项是服务器管理者的电子邮件,当错误发生时,该地址会显示在错误页面上,便于用户联系: cache_mgr
以下这些参数告诉Squid缓存的文件系统、位置和缓存策略: cache_dir ufs /var/squid cache_mem 32MB cache_swap_low 90 cache_swap_high 95
在这里,Squid会将/var/squid目录作为保存缓存数据的目录,每次处理的缓存大小是32兆字节,当缓存空间使用达到95%时,新的内容将取代旧的而不直接添加到目录中,直到空间又下降到90%才停止这一活动。如果不想Squid缓存任何文件,如某些存储空间有限的专有系统,可以使用null文件系统(这样不需要那些缓存策略): cache_dir null /tmp
下面的几个关于缓存的策略配置中,较主要的是第一行,即用户的访问记录,通过分析它来了解所有用户访问的详尽地址 cache_access_log /var/squid/access.log cache_log /var/squid/cache.log cache_store_log /var/squid/store.log
下面这行配置是在较新版本中出现的参数,告诉Squid在错误页面中显示的服务器名称: visible_hostname No1.proxy
以下配置告诉Squid如何处理用户,对每个请求的IP地址作为单独地址处理: client_mask 255.255.255.255
################################################################################ 第4章 快速配置向导
4.1 squid.conf 语法
Squid 的配置文件相对规范。它与其他许多unix 程序相似。每行以配置指令开始,后面跟着数字值或关键字。在读取配置文件时,squid 忽略空行和注释掉的行(以#开始)。如下是一些配置行示例: cache_log /squid/var/cache.log # define the localhost ACL acl Localhost src 127.0.0.1/32 connect_timeout 2 minutes log_fqdn on
某些指令取唯一值。在这些情形下,重复赋予该指令不同的值,将覆盖前面的值。例如, 下面是一个连接超时值。第一行无效,因为第二行覆盖了它: connect_timeout 2 minutes connect_timeout 1 hour
另外,某些指令取列表值。在这些情形下,每一个新增的值都有效。"扩展方式"指令以这种方法工作: extension_methods UNGET extension_methods UNPUT extension_methods UNPOST
对这些基于列表的指令,你通常能在同一行中赋予多个值: extension_methods UNGET UNPUT UNPOST
许多指令有通用类型。例如,连接超时值是一个时间规范,在数字后面跟着时间单元。例如: connect_timeout 3 hours client_lifetime 4 days negative_ttl 27 minutes
类似的,大量的指令指向文件大小或者内存额度。例如,你可以这样编写大小规范:十进制数字后面跟bytes,KB,MB 或GB.例如: minimum_object_size 12 bytes request_header_max_size 10 KB maximum_object_size 187 MB
另一种值得提起的类型是触发器,它的值是on 或者off。许多指令使用该类型。例如: server_persistent_connections on strip_query_terms off prefer_direct on
通常,配置文件指令能以任何顺序出现。然而,如果某个指令指向的值被其他指令所定义,那么顺序就很重要。访问控制列表是个好的例子。acl 被用在http_access 规则之前必须被定义: acl Foo src 1.2.3.4 http_access deny Foo
squid.conf 文件里的许多东西是大小写敏感的,例如指令名。你不能将http_port 写成HTTP_port。
默认的squid.conf 文件包含了对每个指令的大量注释,以及指令的默认值。例如: # TAG: persistent_request_timeout # How long to wait for the next HTTP request on a persistent # connection after the previous request completes. # #Default: # persistent_request_timeout 1 minute
每次安装squid 后,当前默认配置文件存放在$prefix/etc 目录下的squid.conf.default。既然指令每次都有所改变,你能参考该文档,以获取最近的更新。
该章剩下的部分是关于在开始运行squid 之前,你必须知道的少数指令。
4.2 User IDs
你可能知道,unix 进程和文件拥有文件和组属主的属性。你必须选择某个用户和组给squid。该用户和组的组合,必须对大部分squid 相关的文件和目录有读和写的权限。
我高度推荐创建名为"squid"的用户和组。这避免了某人利用squid 来读取系统中的其他文件。假如不止一个人拥有对squid 的管理权限,你可以将他们加到squid 组里。
unix 进程继承了它们父进程的属主属性。那就是说,假如你以joe 用户来启动squid,squid 也以joe 来运行。假如你不想以joe 来运行squid,你需要预先改变你的用户ID。这是su 命令的典型功能。例如: joe% su - squid squid% /usr/local/squid/sbin/squid
不幸的是,运行squid 并非总是如此简单。在某些情况下,你必须以root 来启动squid,这依赖于你的配置。例如,仅仅root 能绑定TCP 套接字到特权端口上,如80。假如你必须以root 来启动squid,你必须设置cache_effective_user 指令。它告诉squid,在执行完需要特别权限的任务后,变成哪个用户。例如: cache_effective_user squid
你提供的该名字必须是有效用户(在/etc/passwd 文件里)。请注意仅仅当你以root 来启动squid 时,你才需要用到该指令。仅仅root 有能力来随意改变用户身份。假如你以joe 来启动squid,它不能改变到squid 用户。
你可能尝试不设置cache_effective_user,直接以root 来运行squid。假如你试过,你会发现squid 拒绝运行。这违背了安全规则。假如外部攻击者有能力危及或利用squid,他能获取对系统的全部访问权。尽管我们努力使squid 安全和少bug,但还是稳重点好。
假如你没有设置cache_effective_user,以root 来启动squid,squid 使用nobody 作为默认值。不管你选择什么用户ID , 请确认它有对下面目录的读访问权:$prefix/etc,$prefix/libexec,$prefix/share。该用户ID 也必须有对日志文件和缓存目录的写访问 权。
squid 也有一个cache_effective_group 指令,但你也许不必设置它。默认的,squid 使用cache_effective_user 的默认组(从/etc/passwd 文件读取)。
4.3 端口号
http_port 指令告诉squid 在哪个端口侦听HTTP 请求。默认端口是3128: http_port 3128
假如你将squid 作为加速器运行(见15 章),你也许该将它设为80。
你能使用附加的http_port 行,来指示squid 侦听在多个端口上。假如你必须支持客户组(它们被配置得不一致),这点就经常有用。例如,来自某个部门的浏览器发送请求到3128, 然而另一个部门使用80 端口。简单的将两个端口号列举出来: http_port 3128 http_port 8080
你也能使用http_port 指令来使squid 侦听在指定的接口地址上。当squid 作为防火墙运行时,它有两个网络接口:一个内部的和一个外部的。你可能不想接受来自外部的http 请求。为了使squid 仅仅侦听在内部接口上,简单的将IP 地址放在端口号前面: http_port 192.168.1.1:3128
4.4 日志文件路径
我将在第13 章讨论所有squid 的日志细节。你现在你关注的唯一事情是,squid 将它的日志放在何处。默认的日志目录是squid 安装位置下的logs 目录。例如,假如你在./configure 时没有使用--prefix=选项,那么默认的日志文件路径是/usr/local/squid/var/logs。
你必须确认日志文件所存放的磁盘位置空间足够。在squid 写日志时如果接受到错误,它会退出和重启。该行为的主要理由应引起你的注意。squid 想确认你不会丢失任何重要的日志信息,特别是你的系统被滥用或者被攻击时。
squid 有三个主要的日志文件:cache.log,access.log,store.log.第一个文件即cache.log,包含状态性的和调试性的消息。当你刚开始运行squid 时,你应密切的关注该文件。假如squid拒绝运行,理由也许会出现在cache.log 文件的结尾处。在正常条件下,该文件不会变得很大。也请注意,假如你以-s 选项来运行squid,重要的cache.log 消息也可被送到你的syslog进程。通过使用cache_log 指令,你可以改变该日志文件的路径: cache_log /squid/logs/cache.log
access.log 文件包含了对squid 发起的每个客户请求的单一行。每行平均约150 个字节。也就是说,在接受一百万条客户请求后,它的体积约是150M。请使用cache_access_log 指令来改变该日志文件的路径: cache_access_log /squid/logs/access.log
假如因为某些理由,你不想squid 记录客户端请求日志,你能指定日志文件的路径为/dev/null.
store.log 文件对大多数cache 管理员来说并非很有用。它包含了进入和离开缓存的每个目标的记录。平均记录大小典型的是175-200 字节。然而,squid 不在store.log 里对cache点击创建接口,所以它比access.log 包含少得多的记录。请使用cache_store_log 指令来改变它的位置: cache_store_log /squid/logs/store.log
通过指定路径为none,你能轻易的完全禁止store.log 日志: cache_store_log none
假如你不小心,squid 的日志文件增加没有限制。某些操作系统对单个文件强制执行2G的大小限制,即使你有充足的磁盘空间。超过该限制会导致写错误,这样squid 就会退出。为了保证日志文件大小合理,你应创建任务来有规律的重命名和打包日志。squid 有内建功能来使这个容易做到。请见13.7 章关于日志轮循的解释。
4.5 访问控制
在第6 章里有更多的关于访问控制的描述。现在,我只讲述少量的访问控制方法,以使热心的读者能快速开始使用squid。
squid 默认的配置文件拒绝每一个客户请求。在任何人能使用代理之前,你必须在squid.conf 文件里加入附加的访问控制规则。最简单的方法就是定义一个针对客户IP 地址的ACL 和一个访问规则,告诉squid 允许来自这些地址的HTTP 请求。squid 有许多不同的ACL类型。src 类型匹配客户IP 地址,squid 会针对客户HTTP 请求检查http_access 规则。这样,你需要增加两行: acl MyNetwork src 192.168.0.0/16 http_access allow MyNetwork
请将这些行放在正确的位置。http_access 的顺序非常重要,但是acl 行的顺序你不必介意。你也该注意默认的配置文件包含了一些重要的访问控制,你不应该改变或删除它们,除非你完全理解它们的意义。在你第一次编辑squid.conf 文件时,请看如下注释: # INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS
在该注释之后,以及"http_access deny all"之前插入你自己的新规则。
为了彻底说明,如下是一个合理的初始访问控制配置,包括推荐的默认控制和早先的例子: acl All src 0/0 acl Manager proto cache_object acl Localhost src 127.0.0.1/32 acl Safe_ports port 80 21 443 563 70 210 280 488 591 777 1025-65535 acl SSL_ports 443 563 acl CONNECT method CONNECT acl MyNetwork src 192.168.0.0/16 http_access allow Manager Localhost http_access deny Manager http_access deny !Safe_ports http_access deny CONNECT !SSL_ports http_access allow MyNetwork http_access deny All
4.6 可见主机名
希望你不必担心visible_hostname 指令。然而,假如squid 不能发现它所运行的机器的主机名,你就必须设置它。如果发生这样的事,squid 抱怨和拒绝运行: % squid -Nd1 FATAL: Could not determine fully qualified hostname. Please set 'visible_hostname'
有大量的理由使squid 需要知道主机名: + 主机名出现在squid 的错误消息里,这帮助用户验证潜在问题的源头。 + 主机名出现在squid 转发的cache 单元的HTTP Via 头里。当请求到达原始主机时,Via头包含了在传输过程中涉及的代理列表。squid 也使用Via 头来检测转发环路。我将在第10章里讨论转发环路。
squid 对特定事务使用内部URL,例如FTP 目录列表的图标。当squid 对FTP 目录产生HTML 页面时,它插入小图标用以指明该目录中的文件类型。图标URL 包含了cache 的主机名,以便web 浏览器能直接从squid 请求它们。
每个从squid 响应的HTTP 回复包含了X-Cache 头。这并非官方HTTP 头。它是一个扩展头,用以指明该响应是cache 点击还是cache 丢失。既然请求和响应可能经过多个cache,每个X-Cache 头包含了cache 报告点击或丢失的名字。如下是一个通过2 个cache 的响应示例: HTTP/1.0 200 OK Date: Mon, 29 Sep 2003 22:57:23 GMT Content-type: text/html Content-length: 733 X-Cache: HIT from bo2.us.ircache.net X-Cache: MISS from bo1.us.ircache.net
squid 在启动时试图自动获取主机名。首先它调用gethostname()函数,这通常能返回正确的主机名。接着,squid 调用gethostbyname()函数尝试对主机名进行DNS 查询。该函数典型的返回IP 地址和系统的规范名。假如gethostbyname()成功,squid 在错误消息里,Via 头里等地方使用这个规范名。
因为大量的理由,squid 可能不能检测到它的规范主机名,包括: + 主机名可能未设置。 + 主机名可能从DNS 区域或/etc/hosts 文件里丢失。
squid 系统的DNS 客户端配置可能不正确或丢失。在unix 系统上,你该检查/etc/resolv.conf 和/etc/host.conf 文件。
假如你看到上述的致命错误,你必须修正主机名和DNS 信息,或者显式的给squid 指明主机名。在大多数情况下,请确认"hostname"命令返回一个完全规范的主机名,并且在/etc/hosts 文件里增加这个接口。假如这样不成功,请在squid.conf 里设置可见主机名: visible_hostname squid.packet-pushers.net
4.7 管理联系信息
你应该设置cache_mgr 指令作为对用户的帮助。它是一个email 地址,假如问题发生,用户能写信给它。cache_mgr 地址默认出现在squid 的错误消息里。例如: cache_mgr
4.8 下一步
在创建了初步的配置文件后,你多少准备首次运行squid 了。请遵循下面章节的建议。
当你掌握了启动和停止squid 后,你该花费一些时间来改善配置文件。你可能想增加更高级的访问控制,这在第6 章里有描述。既然我在这里没有讨论磁盘cache,你该花些时间阅读第7 和第8 章。
第5章 运行Squid
5.1 squid 命令行选项
在开始其他事情之前,让我们先看一下squid 的命令行选项。这里的许多选项你从不会使用,另外有些仅仅在调试问题时有用。
-a port 指定新的http_port 值。该选项覆盖了来自squid.conf 的值。然而请注意,你能在squid.conf里指定多个值。-a 选项仅仅覆盖配置文件里的第一个值。(该选项使用字母a 是因为在Harvest cache 里,HTTP 端口被叫做ASCII 端口)
-d level 让squid 将它的调试信息写到标准错误(假如配置了,就是cache.log 和syslog)。level参数指定了显示在标准错误里的消息的最大等级。在多数情况下,d1 工作良好。请见16.2章关于调试等级的描述。
-f file 指定另一个配置文件。
-h 显示用法。
-k function 指示squid 执行不同的管理功能。功能参数是下列之一:reconfigure,rotate,shutdown,interrupt,kill,debug,check,or parse。 + reconfigure 导致运行中的squid 重新读取配置文件。 + rotate导致squid 滚动它的日志,这包括了关闭日志,重命名,和再次打开它们。 + shutdown 发送关闭squid 进程的信号。 + interrupt 立刻关闭squid,不必等待活动会话完成。 + kill 发送KILL 信号给squid,这是关闭squid 的最后保证。 + debug 将squid 设置成完全的调试模式,假如你的cache 很忙,它能迅速的用完你的磁盘空间。 + check 简单的检查运行中的squid 进程,返回的值显示squid 是否在运行。 + 最后,parse 简单的解析squid.conf 文件,如果配置文件包含错误,进程返回非零值。
-s 激活将日志记录到syslog 进程。squid 使用LOCAL4 syslog 设备。0 级别调试信息以优先级LOG_WARNING 被记录,1 级别消息以LOG_NOTICE 被记录。更高级的调试信息不会被发送到syslogd.你可以在/etc/syslogd.conf 文件里使用如下接口: local4.warning /var/log/squid.log
-u port 指定另一个ICP 端口号,覆盖掉squid.conf 文件里的icp_port。
-v 打印版本信息。
-z 初始化cache,或者交换,目录。在首次运行squid,或者增加新的cache 目录时,你必须使用该选项。
-C 阻止安装某些信号句柄,它们捕获特定的致命信号例如SIGBUS 和SIGSEGV。正常的,这些信号被squid 捕获,以便它能干净的关闭。然而,捕获这些信号可能让以后调试问题困难。使用该选项,致命的信号导致它们的默认动作,通常是coredump。
-D 禁止初始化DNS 测试。正常情况下,squid 直到验证它的DNS 可用才能启动。该选项阻止了这样的检测。你也能在squid.conf 文件里改变或删除dns_testnames 选项。
-F 让squid 拒绝所有的请求,直到它重新建立起存储元数据。假如你的系统很忙,该选项可以减短重建存储元数据的时间。然而,如果你的cache 很大,重建过程可能会花费很长的时间。
-N 阻止squid 变成后台服务进程。
-R 阻止squid 在绑定HTTP 端口之前使用SO_REUSEADDR 选项。
-V 激活虚拟主机加速模式。类似于squid.conf 文件里的httpd_accel_host virtual 指令。
-X 强迫完整调试模式,如你在squid.conf 文件里指定debug_options ALL,9 一样。
-Y 在重建存储元数据时,返回ICP_MISS_NOFETCH 代替ICP_MISS.忙碌的父cache 在重建时,该选项可以导致最少的负载。请见10.6.1.2 章。
5.2 对配置文件查错
在开启squid 之前,你应该谨慎的验证配置文件。这点容易做到,运行如下命令即可: %squid -k parse
假如你看不到输出,配置文件有效,你能继续后面的步骤。
然而,如果配置文件包含错误,squid 会告诉你: squid.conf line 62: http_access allow okay2 aclParseAccessLine: ACL name 'okay2' not found.
这里你可以看到,62 行的http_access 指令指向的ACL 不存在。有时候错误信息很少: FATAL: Bungled squid.conf line 76: memory_pools
在这个情形里,我们忘记了在76 行的memory_pools 指令后放置on 或off。
建议你养成习惯:在每次修改配置文件后,使用squid -k parse。假如你不愿麻烦,并且你的配置文件有错误,squid 会告诉你关于它们而且拒绝启动。假如你管理着大量的cache,也许你会编辑脚本来自动启动,停止和重配置squid。你能在脚本里使用该功能,来确认配置文件是有效的。
5.3 初始化cache 目录
在初次运行squid 之前,或者无论何时你增加了新的cache_dir,你必须初始化cache 目录。命令很简单: %squid –z
对UFS 相关的存储机制(ufs,aufs,and diskd;见第8 章),该命令在每个cache_dir 下面创建了所需的子目录。你不必担心squid 会破坏你的当前cache 目录(如果有的话)。
在该阶段属主和许可权是通常遇到的问题。squid 在特定的用户ID 下运行,这在squid.conf 文件里的cache_effective_user 里指定。用户ID 必须对每个cache_dir 目录有读和写权限。否则,你将看到如下信息: Creating Swap Directories FATAL: Failed to make swap directory /usr/local/squid/var/cache/00: (13) Permission denied
在这样的情形下,你该确认/usr/local/squid/var/cache 目录的所有组成都可被squid.conf给定的用户ID 访问。最终的组件--cache 目录--必须对该用户ID 可写。
cache 目录初始化可能花费一些时间,依赖于cache 目录的大小和数量,以及磁盘驱动器的速度。假如你想观察这个过程,请使用-X 选项: %squid –zX
5.4 在终端窗口里测试squid
一旦你已经初始化cache 目录,就可以在终端窗口里运行squid,将日志记录到标准错误。这样,你能轻易的定位任何错误或问题,并且确认squid 是否成功启动。使用-N 选项来保持squid 在前台运行,-d1 选项在标准错误里显示1 级别的调试信息。 %squid -N -d1
你将看到类似于以下的输出: 2003/09/29 12:57:52| Starting Squid Cache version 2.5.STABLE4 for i386-unknown-freebsd4.8... 2003/09/29 12:57:52| Process ID 294 2003/09/29 12:57:52| With 1064 file descriptors available 2003/09/29 12:57:52| DNS Socket created on FD 4 2003/09/29 12:57:52| Adding nameserver 206.107.176.2 from /etc/resolv.conf 2003/09/29 12:57:52| Adding nameserver 205.162.184.2 from /etc/resolv.conf 2003/09/29 12:57:52| Unlinkd pipe opened on FD 9 2003/09/29 12:57:52| Swap maxSize 102400 KB, estimated 7876 objects 2003/09/29 12:57:52| Target number of buckets: 393 2003/09/29 12:57:52| Using 8192 Store buckets 2003/09/29 12:57:52| Max Mem size: 8192 KB 2003/09/29 12:57:52| Max Swap size: 102400 KB 2003/09/29 12:57:52| Rebuilding storage in /usr/local/squid/var/cache (DIRTY) 2003/09/29 12:57:52| Using Least Load store dir selection 2003/09/29 12:57:52| Set Current Directory to /usr/local/squid/var/cache 2003/09/29 12:57:52| Loaded Icons. 2003/09/29 12:57:52| Accepting HTTP connections at 0.0.0.0, port 3128, FD 11. 2003/09/29 12:57:52| Accepting ICP messages at 0.0.0.0, port 3130, FD 12. 2003/09/29 12:57:52| WCCP Disabled. 2003/09/29 12:57:52| Ready to serve reques
假如你看到错误消息,你该首先修正它。请检查输出信息的开始几行以发现警告信息。最普通的错误是文件/目录许可问题,和配置文件语法错误。假如你看到一条不引起注意的错误消息,请见16 章中关于squid 故障处理的建议和信息。如果还不行,请检查squid FAQ,或查找邮件列表来获得解释。
一旦你见到"Ready to serve requests"消息,就可用一些HTTP 请求来测试squid。配置你的浏览器使用squid 作为代理,然后打开某个web 页面。假如squid 工作正常,页面被迅速载入,就象没使用squid 一样。另外,你可以使用squidclient 程序,它随squid 发布: % squidclient
假如它正常工作,squid 的主页html 文件会在你的终端窗口里滚动。一旦确认squid 工作正常,你能中断squid 进程(例如使用ctrl-c)并且在后台运行squid。
5.5 将squid 作为服务进程运行
正常情况下你想将squid 以后台进程运行(不出现在终端窗口里)。最容易的方法是简单执行如下命令: %squid –s
-s 选项导致squid 将重要的状态和警告信息写到syslogd。squid 使用LOCAL4 设备,和LOG_WARNING 和LOG_NOTICE 优先权。syslog 进程实际可能会或不会记录squid 的消息,这依赖于它被如何配置。同样的消息被写进cache.log 文件,所以假如你愿意,忽略-s 选项也是安全的。
当你不使用-N 选项来启动squid,squid 自动在后台运行并且创建父/子进程对。子进程做所有的实际工作。父进程确认子进程总在运行。这样,假如子进程意外终止,父进程启动另外一个子进程以使squid 正常工作。通过观察syslog 消息,你能看到父/子进程交互作用。 Jul 31 14:58:35 zapp squid[294]: Squid Parent: child process 296 started
这里显示的父进程ID 是294,子进程是296。当你查看ps 的输出,你可以看到子进程以(squid)形式出现: %ps ax | grep squid 294 ?? Is 0:00.01 squid -sD 296 ?? S 0:00.27 (squid) -sD (squid)
假如squid 进程意外终止,父进程启动另一个。例如: Jul 31 15:02:53 zapp squid[294]: Squid Parent: child process 296 exited due to signal 6 Jul 31 15:02:56 zapp squid[294]: Squid Parent: child process 359 started
在某些情形下,squid 子进程可能立即终止。为了防止频繁的启动子进程,假如子进程连续5 次没有运行至少10 秒钟,父进程会放弃。 Jul 31 15:13:48 zapp squid[455]: Squid Parent: child process 474 exited with status 1 Jul 31 15:13:48 zapp squid[455]: Exiting due to repeated, frequent failures
如果发生这样的事,请检查syslog 和squid 的cache.log 以发现错误。
5.5.1 squid_start 脚本
当squid 以后台进程运行时,它查找squid 执行程序目录下的名为squid_start 的文件。假如发现,该程序在父进程创建子进程之前被执行。你能使用该脚本完成特定的管理任务,例如通知某人squid 在运行,管理日志文件等。除非squid_start 程序存在,squid 不会创建子进程。
squid_start 脚本在你使用绝对或相对路径启动squid 时才开始工作。换句话说,squid 不使用PATH 环境变量来定位squid_start.这样,你应该养成习惯这样启动squid: % /usr/local/squid/sbin/squid –sD
而不要这样: %squid –sD
5.6 启动脚本
通常你希望squid 在每次计算机重启后自动启动。对不同的操作系统,它们的启动脚本如何工作也很不同。我在这里描述一些通用的环境,但对你自己的特殊操作系统,也许该有特殊的处理方法。
5.6.1 /etc/rc.local
最容易的机制之一是/etc/rc.local 脚本。这是个简单的shell 脚本,在每次系统启动时以root 运行。使用该脚本来启动squid 非常容易,增加一行如下: /usr/local/squid/sbin/squid –s
当然你的安装位置可能不同,还有你可能要使用其他命令行选项。不要在这里使用-N选项。
假如因为某些理由,你没有使用cache_effective_user 指令,你可以尝试使用su 来让squid以非root 用户运行: /usr/bin/su nobody -c '/usr/local/squid/sbin/squid -s'
5.6.2 init.d 和rc.d
init.d 和rc.d 机制使用独立的shell 脚本来启动不同的服务。这些脚本通常在下列目录之中:/sbin/init.d, /etc/init.d, /usr/local/etc/rc.d.脚本通常获取单一命令行参数,是start 或stop。某些系统仅仅使用start 参数。如下是启动squid 的基本脚本: #!/bin/sh # this script starts and stops Squid case "$1" in start) /usr/local/squid/sbin/squid -s echo -n ' Squid' ;; stop) /usr/local/squid/sbin/squid -k shutdown ;; esac
Linux 用户可能在启动squid 之前需要设置文件描述符限制。例如: echo 8192 >; /proc/sys/fs/file-max limit -HSn 8192
为了使用该脚本,先找到脚本存放的目录。给它一个有意义的名字,类似于其他的系统启动脚本。可以是S98squid 或squid.sh。通过重启计算机来测试该脚本,而不要假想它会正常工作。
5.6.3 /etc/inittab
某些操作系统支持另一种机制,是/etc/inittab 文件。在这些系统中,init 进程启动和停止基于运行等级的服务。典型的inittab 接口类似如此: sq:2345nce:/usr/local/squid/sbin/squid –s
使用该接口,init 进程启动squid 一次并且随后忘记它。squid 确认它驻留在运行状态,象前面描述的一样。或者,你能这样做: sq:2345:respawn:/usr/local/squid/sbin/squid –Ns
这里我们使用了respawn 选项,假如进程不存在init 会重启squid。假如使用respawn,请确认使用-N 选项。
在编辑完inittab 文件后,使用下面的命令来使init 重新读取它的配置文件和启动squid: # init q
5.7 chroot 环境
某些人喜欢在chroot 环境运行squid。这是unix 的功能,给予进程新的root 文件系统目录。在squid 受安全威胁时,它提供额外等级的安全保护。假如攻击者在某种程度上通过squid获取了对操作系统的访问权,她仅仅能访问在chroot 文件系统中的文件。在chroot 树之外的系统文件,她不可访问。
最容易在chroot 环境里运行squid 的方法是,在squid.conf 文件里指定新的root 目录,如下: chroot /new/root/directory
chroot()系统调用需要超级用户权限,所以你必须以root 来启动squid。
chroot 环境不是为unix 新手准备的。它有点麻烦,因为你必须在新的root 目录里重复放置大量的文件。例如,假如默认的配置文件正常在/usr/local/squid/etc/squid.conf,并且你使用chroot 指令,那么文件必须位于/new/root/directory/usr/local/squid/etc/squid.conf.你必须将位于$prefix/etc,$prefix/share,$prefix/libexec 下的所有文件拷贝到chroot 目录。请确认$prefix/var 和cache 目录在chroot 目录中存在和可写。
同样的,你的操作系统需要将大量的文件放在chroot 目录里,例如/etc/resolv.conf 和/dev/null.假如你使用外部辅助程序,例如重定向器(见11 章)或者验证器(见12 章),你也需要来自/usr/lib 的某些共享库。你可以使用ldd 工具来查找给定的程序需要哪些共享库: % ldd /usr/local/squid/libexec/ncsa_auth /usr/local/squid/libexec/ncsa_auth: libcrypt.so.2 =>; /usr/lib/libcrypt.so.2 (0x28067000) libm.so.2 =>; /usr/lib/libm.so.2 (0x28080000) libc.so.4 =>; /usr/lib/libc.so.4 (0x28098000)
你可以使用chroot 命令来测试辅助程序: # chroot /new/root/directory /usr/local/squid/libexec/ncsa_auth /usr/libexec/ld-elf.so.1: Shared object "libcrypt.so.2" not found
更多的关于chroot 的信息,请见你系统中chroot()的manpage。
5.8 停止squid
最安全的停止squid 的方法是使用squid -k shutdown 命令: %squid -k shutdown
该命令发送TERM 信号到运行中的squid 进程。在接受到TERM 信号后,squid 关闭进来的套接字以拒收新请求。然后它等待一段时间,用以完成外出请求。默认时间是30 秒,你可以在shutdown_lifetime 指令里更改它。 假如因为某些理由,squid.pid 文件丢失或不可读,squid -k 命令会失败。在此情形下,你可以用ps 找到squid 的进程ID,然后手工杀死squid。例如: %ps ax |grep squid
假如你看到不止一个squid 进程,请杀死以(squid)显示的那个。例如: % ps ax | grep squid 294 ?? Is 0:00.01 squid -sD 296 ?? S 0:00.27 (squid) -sD (squid) % kill -TERM 296
在发送TERM 信号后,你也许想查看日志,以确认squid 已关闭: % tail -f logs/cache.log 2003/09/29 21:49:30| Preparing for shutdown after 9316 requests 2003/09/29 21:49:30| Waiting 10 seconds for active connections to finish 2003/09/29 21:49:30| FD 11 Closing HTTP connection 2003/09/29 21:49:31| Shutting down... 2003/09/29 21:49:31| FD 12 Closing ICP connection 2003/09/29 21:49:31| Closing unlinkd pipe on FD 9 2003/09/29 21:49:31| storeDirWriteCleanLogs: Starting... 2003/09/29 21:49:32| Finished. Wrote 253 entries. 2003/09/29 21:49:32| Took 0.1 seconds (1957.6 entries/sec). 2003/09/29 21:49:32| Squid Cache (Version 2.5.STABLE4): Exiting normally.
假如你使用squid -k interrupt 命令,squid 立即关闭,不用等待完成活动请求。这与在kill 里发送INT 信号相同。
5.9 重配置运行中的squid 进程
在你了解了更多关于squid 的知识后,你会发现对squid.conf 文件做了许多改动。为了让新设置生效,你可以关闭和重启squid,或者在squid 运行时,重配置它。
重配置运行中的squid 最好的方法是使用squid -k reconfigure 命令: %squid -k reconfigure
当你运行该命令时,HUP 信号被发送到运行中的squid 进程。然后squid 读取和解析squid.conf 文件。假如操作成功,你可以在cache.log 里看到这些: 2003/09/29 22:02:25| Restarting Squid Cache (version 2.5.STABLE4)... 2003/09/29 22:02:25| FD 12 Closing HTTP connection 2003/09/29 22:02:25| FD 13 Closing ICP connection 2003/09/29 22:02:25| Cache dir '/usr/local/squid/var/cache' size remains unchanged at 102400 KB 2003/09/29 22:02:25| DNS Socket created on FD 5 2003/09/29 22:02:25| Adding nameserver 10.0.0.1 from /etc/resolv.conf 2003/09/29 22:02:25| Accepting HTTP connections at 0.0.0.0, port 3128, FD 9. 2003/09/29 22:02:25| Accepting ICP messages at 0.0.0.0, port 3130, FD 11. 2003/09/29 22:02:25| WCCP Disabled. 2003/09/29 22:02:25| Loaded Icons. 2003/09/29 22:02:25| Ready to serve requests.
在使用reconfigure 选项时你须谨慎,因为所做的改变可能会导致致命错误。例如,请注意squid 关闭和重新打开进来的HTTP 和ICP 套接字;假如你将http_port 改变为squid 不能打开的端口,它会发生致命错误并退出。
在squid 运行时,某些指令和和选项不能改变,包括: + 删除cache 目录(cache_dir 指令) + 改变store_log 指令 + 改变coss cache_dir 的块大小数值。事实上,无论何时你改变了该值,你必须重新初始化coss cache_dir。 + coredump_dir 指令在重配置过程中不被检查。所以,在squid 已经启动了后,你不能让squid 改变它的当前目录。
solaris 用户在重配置squid 过程中可能遇到其他问题。solaris 的stdio 执行组件里的fopen()调用要求使用小于256 的未用文件描述符。FILE 结构以8 位值存储该文件描述符。正常情况下这不构成问题,因为squid 使用底层I/O(例如open())来打开cache 文件。然而,在重配置过程中的某些任务使用fopen(),这就有可能失败,因为前面的256 个文件描述符已被分配出去。
5.10 滚动日志文件
除非你在squid.conf 里禁止,squid 会写大量的日志文件。你必须周期性的滚动日志文件,以阻止它们变得太大。squid 将大量的重要信息写入日志,假如写不进去了,squid 会发生错误并退出。为了合理控制磁盘空间消耗,在cron 里使用如下命令: %squid -k rotate
例如,如下任务接口在每天的早上4 点滚动日志: 0 4 * * * /usr/local/squid/sbin/squid -k rotate
该命令做两件事。首先,它关闭当前打开的日志文件。然后,通过在文件名后加数字扩展名,它重命名cache.log,store.log,和access.log。例如,cache.log 变成cache.log.0,cache.log.0变成cache.log.1,如此继续,滚动到logfile_rotate 选项指定的值。
squid 仅仅保存每个日志文件的最后logfile_rotate 版本。更老的版本在重命名过程中被删除。假如你想保存更多的拷贝,你需要增加logfile_rotate 限制,或者编写脚本用于将日志文件移动到其他位置。
请见13.7 章关于滚动日志的其他信息。
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
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 ( 请见)。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 名字或区域。例如,下面是有效的域名: 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
用户对的请求匹配ACL B,但不匹配A。ACL A 要求严格的字符串 匹配,然而ACL B 里领头的点就像通配符。
另外,用户对的请求同时匹配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 ^
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/1.0
这里, 是主机名。当访问列表规则包含了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,第三个连接到外部因特网。
当正确的配置时,所有在子网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 acl B dstdomain
A 实际上是IP 地址ACL。当Squid 解析配置文件时,它查询 的IP地址,并将它们存在内存里。它不保存名字。假如在squid 运行时IP 地址改变了,squid 会继续使用旧的地址。
然而dstdomain ACL 以域名形式存储,并非IP 地址。当squid 检查ACL B 时,它对URL的主机名部分使用字符串比较功能。在该情形下,它并不真正关心是否 的IP 地址改变了。
使用dstdomain ACL 的主要问题是某些URL 使用IP 地址代替主机名。假如你的目标是使用dstdomain ACL 来阻塞对某些站点的访问,聪明的用户能手工查询站点的IP 地址,然后将它们放在URL 里。例如,下面的2 行URL 带来同样的页面:
第一行能被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如下:,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 时可能有点麻烦。 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 头部。例如,你也许想Squid过滤掉发送到某些原始服务器的请求里的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
;
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
;
假如你不熟悉这些奇特的端口号,也不要担心。你可以阅读unix 系统的/etc/services 文件,或者阅读IANA 的注册TCP/UDP 端口号列表:
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 or: % squidclient -p 4128
某些类型的请求难以控制。假如你有src ACL,它们阻止来自外部网络的请求,你也许需要从外部主机测试它们。测试time ACL 也很困难,除非你能改变系统时钟,或者等待足够长时间。你能使用squidclient 的-H 选项来设置任意请求头。例如,假如你需要测试browser ACL,那么这样做: % squidclient -p 4128 \ -H 'User-Agent: Mozilla/5.0 (compatible; Konqueror/3)\r\n'
更多的复杂请求,包括多个头部,请参考16.4 章中描述的技术。
你也许考虑制订一项cron,定期检查ACL,以发现期望的行为,并报告任何异常。如下是可以起步的示例shell 脚本: #!/bin/sh set -e TESTHOST="" # make sure Squid is not proxying dangerous ports # ST=`squidclient '' | 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 '' | 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 '' | head -1 | awk '{print $2}'` if test "$ST" != 403 ; then echo "Squid allowed request from external address $EXT_ADDR" fi exit 0
7.磁盘缓存基础
7.1 cache_dir指令
cache_dir指令是squid.conf配置文件里最重要的指令之一。它告诉squid以何种方式存储cache文件到磁盘的什么位置。cache_dir指令取如下参数: cache_dir scheme directory size L1 L2 [options]
7.1.1 参数:Scheme
Squid支持许多不同的存储机制。默认的(原始的)是ufs。依赖于操作系统的不同,你可以选择不同的存储机制。在./configure时,你必须使用--enable-storeio=LIST选项来编译其他存储机制的附加代码。我将在8.7章讨论aufs,diskd,coss和null。现在,我仅仅讨论ufs机制,它与aufs和diskd一致。
7.1.2 参数:Directory
该参数是文件系统目录,squid将cache对象文件存放在这个目录下。正常的,cache_dir使用整个文件系统或磁盘分区。它通常不介意是否在单个文件系统分区里放置了多个cache目录。然而,我推荐在每个物理磁盘中,仅仅设置一个cache目录。例如,假如你有2个无用磁盘,你可以这样做: # newfs /dev/da1d # newfs /dev/da2d # mount /dev/da1d /cache0 # mount /dev/da2d /cache1
然后在squid.conf里增加如下行: cache_dir ufs /cache0 7000 16 256 cache_dir ufs /cache1 7000 16 256
假如你没有空闲硬盘,当然你也能使用已经存在的文件系统分区。选择有大量空闲空间的分区,例如/usr或/var,然后在下面创建一个新目录。例如: # mkdir /var/squidcache
然后在squid.conf里增加如下一行: cache_dir ufs /var/squidcache 7000 16 256
7.1.3 参数:Size 该参数指定了cache目录的大小。这是squid能使用的cache_dir目录的空间上限。计算出合理的值也许有点难。你必须给临时文件和swap.state日志,留出足够的自由空间(见13.6章)。我推荐挂载空文件系统,可以运行df: % df -k Filesystem 1K-blocks Used Avail Capacity Mounted on /dev/da1d 3037766 8 2794737 0% /cache0 /dev/da2d 3037766 8 2794737 0% /cache1
这里你可以看到文件系统有大约2790M的可用空间。记住,UFS保留了部分最小自由空间,这里约是8%,这就是squid为什么不能使用全部3040M空间的原因。
你也许试图分配2790M给cache_dir。如果cache不很繁忙,并且你经常轮转日志,那么这样做也许可行。然而,为安全起见,我推荐保留10%的空间。这些额外的空间用于存放squid的swap.state文件和临时文件。
注意cache_swap_low指令也影响了squid使用多少空间。我将在7.2章里讨论它的上限和下限。
底线是,你在初始时应保守的估计cache_dir的大小。将cache_dir设为较小的值,并允许写满cache。在squid运行一段时间后,cache目录会填满,这样你可以重新评估cache_dir的大小设置。假如你有大量的自由空间,就可以轻松的增加cache目录的大小了。
7.1.3.1 Inodes
Inodes(i节点)是unix文件系统的基本结构。它们包含磁盘文件的信息,例如许可,属主,大小,和时间戳。假如你的文件系统运行超出了i节点限制,就不能创造新文件,即使还有空间可用。超出i节点的系统运行非常糟糕,所以在运行squid之前,你应该确认有足够的i节点。
创建新文件系统的程序(例如,newfs或mkfs)基于总空间的大小,保留了一定数量的i节点。这些程序通常允许你设置磁盘空间的i节点比率。例如,请阅读newfs和mkfs手册的-i选项。磁盘空间对i节点的比率,决定了文件系统能实际支持的文件大小。大部分unix系统每4KB创建一个i节点,这对squid通常是足够的。研究显示,对大部分cache代理,实际文件大小大约是10KB。你也许能以每i节点8KB开始,但这有风险。 你能使用df -i命令来监视系统的i节点,例如: % df -ik Filesystem 1K-blocks Used Avail Capacity iused ifree %iused Mounted on /dev/ad0s1a 197951 57114 125001 31% 1413 52345 3% / /dev/ad0s1f 5004533 2352120 2252051 51% 129175 1084263 11% /usr /dev/ad0s1e 396895 6786 358358 2% 205 99633 0% /var /dev/da0d 8533292 7222148 628481 92% 430894 539184 44% /cache1 /dev/da1d 8533292 7181645 668984 91% 430272 539806 44% /cache2 /dev/da2d 8533292 7198600 652029 92% 434726 535352 45% /cache3 /dev/da3d 8533292 7208948 641681 92% 427866 542212 44% /cache4
如果i节点的使用(%iused)少于空间使用(Capacity),那就很好。不幸的是,你不能对已经存在的文件系统增加更多i节点。假如你发现运行超出了i节点,那就必须停止squid,并且重新创建文件系统。假如你不愿意这样做,那么请削减cache_dir的大小。
7.1.3.2 在磁盘空间和进程大小之间的联系
Squid的磁盘空间使用也直接影响了它的内存使用。每个在磁盘中存在的对象,要求少量的内存。squid使用内存来索引磁盘数据。假如你增加了新的cache目录,或者增加了磁盘cache大小,请确认你已有足够的自由内存。假如squid的进程大小达到或超过了系统的物理内存容量,squid的性能下降得非常块。
Squid的cache目录里的每个对象消耗76或112字节的内存,这依赖于你的系统。内存以StoreEntry, MD5 Digest, 和LRU policy node结构来分配。小指令(例如,32位)系统,象那些基于Intel Pentium的,取76字节。使用64位指令CPU的系统,每个目标取112字节。通过阅读cache管理的内存管理文档,你能发现这些结构在你的系统中耗费多少内存(请见14.2.1.2章)。
不幸的是,难以精确预测对于给定数量的磁盘空间,需要使用多少附加内存。它依赖于实际响应大小,而这个大小基于时间波动。另外,Squid还为其他数据结构和目的分配内存。不要假设你的估计正确。你该经常监视squid的进程大小,假如必要,考虑削减cache大小。
7.1.4 参数:L1和L2 对ufs,aufs,和diskd机制,squid在cache目录下创建二级目录树。L1和L2参数指定了第一级和第二级目录的数量。默认的是16和256。图7-1显示文件系统结构。 Figure 7-1. 基于ufs存储机制的cache目录结构 (略图) 某些人认为squid依赖于L1和L2的特殊值,会执行得更好或更差。这点听起来有关系,即小目录比大目录被检索得更快。这样,L1和L2也许该足够大,以便L2目录的文件更少。 例如,假设你的cache目录存储了7000M,假设实际文件大小是10KB,你能在这个cache_dir里存储700,000个文件。使用16个L1和256个L2目录,总共有4096个二级目录。700,000/4096的结果是,每个二级目录大约有170个文件。 如果L1和L2的值比较小,那么使用squid -z创建交换目录的过程,会执行更快。这样,假如你的cache文件确实小,你也许该减少L1和L2目录的数量。 Squid给每个cache目标分配一个唯一的文件号。这是个32位的整数,它唯一标明磁盘中的文件。squid使用相对简单的算法,将文件号转换位路径名。该算法使用L1和L2作为参数。这样,假如你改变了L1和L2,你改变了从文件号到路径名的映射关系。对非空的cache_dir改变这些参数,导致存在的文件不可访问。在cache目录激活后,你永不要改变L1和L2值。 Squid在cache目录顺序中分配文件号。文件号到路径名的算法(例如,storeUfsDirFullPath( )),用以将每组L2文件映射到同样的二级目录。Squid使用了参考位置来做到这点。该算法让HTML文件和它内嵌的图片更可能的保存在同一个二级目录中。某些人希望squid均匀的将cache文件放在每个二级目录中。然而,当cache初始写入时,你可以发现仅仅开头的少数目录包含了一些文件,例如: % cd /cache0; du -k 2164 ./00/00 2146 ./00/01 2689 ./00/02 1974 ./00/03 2201 ./00/04 2463 ./00/05 2724 ./00/06 3174 ./00/07 1144 ./00/08 1 ./00/09 1 ./00/0A 1 ./00/0B
这是完全正常的,不必担心。 7.1.5 参数:Options Squid有2个依赖于不同存储机制的cache_dir选项:read-only标签和max-size值。 7.1.5.1 read-only read-only选项指示Squid继续从cache_dir读取文件,但不往里面写新目标。它在squid.conf文件里看起来如下: cache_dir ufs /cache0 7000 16 256 read-only
假如你想把cache文件从一个磁盘迁移到另一个磁盘,那么可使用该选项。如果你简单的增加一个cache_dir,并且删除另一个,squid的命中率会显著下降。在旧目录是read-only时,你仍能从那里获取cache命中。在一段时间后,就可以从配置文件里删除read-only缓存目录。 7.1.5.2 max-size 使用该选项,你可以指定存储在cache目录里的最大目标大小。例如: cache_dir ufs /cache0 7000 16 256 max-size=1048576
注意值是以字节为单位的。在大多数情况下,你不必增加该选项。假如你做了,请尽力将所有cache_dir行以max-size大小顺序来存放(从小到大)。
7.2 磁盘空间基准 cache_swap_low和cache_swap_high指令控制了存储在磁盘上的对象的置换。它们的值是最大cache体积的百分比,这个最大cache体积来自于所有cache_dir大小的总和。例如: cache_swap_low 90 cache_swap_high 95
如果总共磁盘使用低于cache_swap_low,squid不会删除cache目标。如果cache体积增加,squid会逐渐删除目标。在稳定状态下,你发现磁盘使用总是相对接近cache_swap_low值。你可以通过请求cache管理器的storedir页面来查看当前磁盘使用状况(见14.2.1.39章)。
请注意,改变cache_swap_high也许不会对squid的磁盘使用有太大效果。在squid的早期版本里,该参数有重要作用;然而现在,它不是这样了。 7.3 对象大小限制 你可以控制缓存对象的最大和最小体积。比maximum_object_size更大的响应不会被缓存在磁盘。然而,它们仍然是代理方式的。在该指令后的逻辑是,你不想某个非常大的响应来浪费空间,这些空间能被许多小响应更好的利用。该语法如下: maximum_object_size size-specification
如下是一些示例: maximum_object_size 100 KB maximum_object_size 1 MB maximum_object_size 12382 bytes maximum_object_size 2 GB
Squid以两个不同的方法来检查响应大小。假如响应包含了Content-Length头部,squid将这个值与maximum_object_size值进行比较。假如前者大于后者,该对象立刻不可缓存,并且不会消耗任何磁盘空间。
不幸的是,并非每个响应都有Content-Length头部。在这样的情形下,squid将响应写往磁盘,把它当作来自原始服务器的数据。在响应完成后,squid再检查对象大小。这样,假如对象的大小达到 maximum_object_size限制,它继续消耗磁盘空间。仅仅当squid在做读取响应的动作时,总共cache大小才会增大。
换句话说,活动的,或者传输中的目标,不会对squid内在的cache大小值有影响。这点有好处,因为它意味着squid不会删除cache里的其他目标,除非目标不可缓存,并对总共cache大小有影响。然而,这点也有坏处,假如响应非常大,squid可能运行超出了磁盘自由空间。为了减少发生这种情况的机会,你应该使用reply_body_max_size指令。某个达到reply_body_max_size限制的响应立即被删除。
Squid也有一个minimum_object_size指令。它允许你对缓存对象的大小设置最低限制。比这个值更小的响应不会被缓存在磁盘或内存里。注意这个大小是与响应的内容长度(例如,响应body大小)进行比较,后者包含在HTTP头部里。
7.4 分配对象到缓存目录
当squid想将某个可缓存的响应存储到磁盘时,它调用一个函数,用以选择cache目录。然后它在选择的目录里打开一个磁盘文件用于写。假如因为某些理由,open()调用失败,响应不会被存储。在这样的情况下,squid不会试图在其他cache目录里打开另一个磁盘文件。
Squid有2个cache_dir选择算法。默认的算法叫做lease-load;替代的算法是round-robin。
least-load算法,就如其名字的意义一样,它选择当前工作负载最小的cache目录。负载概念依赖于存储机制。对aufs,coss和diskd机制来说,负载与挂起操作的数量有关。对ufs来说,负载是不变的。在cache_dir负载相等的情况下,该算法使用自由空间和最大目标大小作为附加选择条件。
该选择算法也取决于max-size和read-only选项。假如squid知道目标大小超出了限制,它会跳过这个cache目录。它也会跳过任何只读目录。
round-robin算法也使用负载作为衡量标准。它选择某个负载小于100%的cache目录,当然,该目录里的存储目标没有超出大小限制,并且不是只读的。
在某些情况下,squid可能选择cache目录失败。假如所有的cache_dir是满负载,或者所有目录的实际目标大小超出了max-size限制,那么这种情况可能发生。这时,squid不会将目标写往磁盘。你可以使用cache管理器来跟踪squid选择cache目录失败的次数。请见store_io页(14.2.1.41章),找到create.select_fail行。
7.5 置换策略
cache_replacement_policy指令控制了squid的磁盘cache的置换策略。Squid2.5版本提供了三种不同的置换策略:最少近来使用(LRU),贪婪对偶大小次数(GDSF),和动态衰老最少经常使用(LFUDA)。
LRU是默认的策略,并非对squid,对其他大部分cache产品都是这样。LRU是流行的选择,因为它容易执行,并提供了非常好的性能。在32位系统上,LRU相对于其他使用更少的内存(每目标12对16字节)。在64位系统上,所有的策略每目标使用24字节。
在过去,许多研究者已经提议选择LRU。其他策略典型的被设计来改善cache的其他特性,例如响应时间,命中率,或字节命中率。然而研究者的改进结果也可能在误导人。某些研究使用并不现实的小cache目标;其他研究显示当cache大小增加时,置换策略的选择变得不那么重要。
假如你想使用GDSF或LFUDA策略,你必须在./configure时使用--enable-removal-policies选项(见3.4.1章)。Martin Arlitt和HP实验室的John Dilley为squid写了GDSF和LFUDA算法。你可以在线阅读他们的文档:
我在O'Reilly出版的书"Web Caching",也讨论了这些算法。
cache_replacement_policy指令的值是唯一的,这点很重要。不象squid.conf里的大部分其他指令,这个指令的位置很重要。cache_replacement_policy指令的值在squid解析cache_dir指令时,被实际用到。通过预先设置替换策略,你可以改变cache_dir的替换策略。例如: cache_replacement_policy lru cache_dir ufs /cache0 2000 16 32 cache_dir ufs /cache1 2000 16 32
cache_replacement_policy heap GDSF cache_dir ufs /cache2 2000 16 32 cache_dir ufs /cache3 2000 16 32
在该情形中,头2个cache目录使用LRU置换策略,接下来2个cache目录使用GDSF。请记住,假如你已决定使用cache管理器的config选项(见14.2.1.7章),这个置换策略指令的特性就非常重要。cache管理器仅仅输出最后一个置换策略的值,将它置于所有的cache目录之前。例如,你可能在squid.conf里有如下行: cache_replacement_policy heap GDSF cache_dir ufs /tmp/cache1 10 4 4 cache_replacement_policy lru cache_dir ufs /tmp/cache2 10 4 4
但当你从cache管理器选择config时,你得到: cache_replacement_policy lru cache_dir ufs /tmp/cache1 10 4 4 cache_dir ufs /tmp/cache2 10 4 4
就象你看到的一样,对头2个cache目录的heap GDSF设置被丢失了。
7.6 删除缓存对象
在某些情况下,你必须从squid的cache里手工删除一个或多个对象。这些情况可能包括: + 你的用户抱怨总接收到过时的数据; + 你的cache因为某个响应而“中毒”; + Squid的cache索引在经历磁盘I/O错误或频繁的crash和重启后,变得有问题; + 你想删除一些大目标来释放空间给新的数据; + Squid总从本地服务器中cache响应,现在你不想它这样做。
上述问题中的一些可以通过强迫web浏览器reload来解决。然而,这并非总是可靠。例如,一些浏览器通过载入另外的程序,从而显示某些类容类型;那个程序可能没有reload按钮,或甚至它了解cache的情况。
假如必要,你总可以使用squidclient程序来reload缓存目标。简单的在uri前面使用-r选项: % squidclient -r >;/tmp/foo
假如你碰巧在refresh_pattern指令里设置了ignore-reload选项,你和你的用户将不能强迫缓存响应更新。在这样的情形下,你最好清除这些有错误的缓存对象。
7.6.1 删除个别对象
Squid接受一种客户请求方式,用于删除cache对象。PURGE方式并非官方HTTP请求方式之一。它与DELETE不同,对后者,squid将其转发到原始服务器。PURGE请求要求squid删除在uri里提交的目标。squid返回200(OK)或404(Not Found)。
PURGE方式某种程度上有点危险,因为它删除了cache目标。除非你定义了相应的ACL,否则squid禁止PURGE方式。正常的,你仅仅允许来自本机和少数可信任主机的PURGE请求。配置看起来如下: acl AdminBoxes src 127.0.0.1 172.16.0.1 192.168.0.1 acl Purge method PURGE
http_access allow AdminBoxes Purge http_access deny Purge
squidclient程序提供了产生PURGE请求的容易方法,如下: % squidclient -m PURGE
代替的,你可以使用其他工具(例如perl脚本)来产生你自己的HTTP请求。它非常简单: PURGE HTTP/1.0 Accept: */*
注意某个单独的URI不唯一标明一个缓存响应。Squid也在cache关键字里使用原始请求方式。假如响应包含了不同的头部,它也可以使用其他请求头。当你发布PURGE请求时,Squid使用GET和HEAD的原始请求方式来查找缓存目标。而且,Squid会删除响应里的所有variants,除非你在PURGE请求的相应头部里指定了要删除的variants。Squid仅仅删除GET和HEAD请求的variants。
7.6.2 删除一组对象
不幸的是,Squid没有提供一个好的机制,用以立刻删除一组对象。这种要求通常出现在某人想删除所有属于同一台原始服务器的对象时。
因为很多理由,squid不提供这种功能。首先,squid必须遍历所有缓存对象,执行线性搜索,这很耗费CPU,并且耗时较长。当squid在搜索时,用户会面临性能下降问题。第二,squid在内存里对URI保持MD5算法,MD5是单向哈希,这意味着,例如,你不能确认是否某个给定的MD5哈希是由包含""字符串的URI产生而来。唯一的方法是从原始URI重新计算MD5值,并且看它们是否匹配。因为squid没有保持原始的URI,它不能执行这个重计算。
那么该怎么办呢?
你可以使用access.log里的数据来获取URI列表,它们可能位于cache里。然后,将它们用于squidclient或其他工具来产生PURGE请求,例如: % awk '{print $7}' /usr/local/squid/var/logs/access.log \ | grep \ | xargs -n 1 squidclient -m PURGE
7.6.3 删除所有对象
在极度情形下,你可能需要删除整个cache,或至少某个cache目录。首先,你必须确认squid没有在运行。
让squid忘记所有缓存对象的最容易的方法之一,是覆盖swap.state文件。注意你不能简单的删除swap.state文件,因为squid接着要扫描cache目录和打开所有的目标文件。你也不能简单的截断swap.state为0大小。代替的,你该放置一个单字节在里面,例如: # echo '' >; /usr/local/squid/var/cache/swap.state
当squid读取swap.state文件时,它获取到了错误,因为在这里的记录太短了。下一行读取就到了文件结尾,squid完成重建过程,没有装载任何目标元数据。
注意该技术不会从磁盘里删除cache文件。你仅仅使squid认为它的cache是空的。当squid运行时,它增加新文件到cache里,并且可能覆盖旧文件。在某些情形下,这可能导致你的磁盘使用超出了自由空间。假如这样的事发生,你必须在再次重启squid前删除旧文件。
删除cache文件的方法之一是使用rm。然而,它通常花费很长的时间来删除所有被squid创建的文件。为了让squid快速启动,你可以重命名旧cache目录,创建一个新目录,启动squid,然后同时删除旧目录。例如: # squid -k shutdown # cd /usr/local/squid/var # mv cache oldcache # mkdir cache # chown nobody:nobody cache # squid -z # squid -s # rm -rf oldcache &
另一种技术是简单的在cache文件系统上运行newfs(或mkfs)。这点仅在你的cache_dir使用整个磁盘分区时才可以运行。
7.7 refresh_pattern
refresh_pattern指令间接的控制磁盘缓存。它帮助squid决定,是否某个给定请求是cache命中,或作为cache丢失对待。宽松的设置增加了你的cache命中率,但也增加了用户接收过时响应的机会。另一方面,保守的设置,降低了cache命中率和过时响应。
refresh_pattern规则仅仅应用到没有明确过时期限的响应。原始服务器能使用Expires头部,或者Cache-Control:max-age指令来指定过时期限。
你可以在配置文件里放置任意数量的refresh_pattern行。squid按顺序查找它们以匹配正则表达式。当squid找到一个匹配时,它使用相应的值来决定,某个缓存响应是存活还是过期。refresh_pattern语法如下: refresh_pattern [-i] regexp min percent max [options]
例如: refresh_pattern -i \.jpg$ 30 50% 4320 reload-into-ims refresh_pattern -i \.png$ 30 50% 4320 reload-into-ims refresh_pattern -i \.htm$ 0 20% 1440 refresh_pattern -i \.html$ 0 20% 1440 refresh_pattern -i . 5 25% 2880
regexp参数是大小写敏感的正则表达式。你可以使用-i选项来使它们大小写不敏感。squid按顺序来检查refresh_pattern行;当正则表达式之一匹配URI时,它停止搜索。
min参数是分钟数量。它是过时响应的最低时间限制。如果某个响应驻留在cache里的时间没有超过这个最低限制,那么它不会过期。类似的,max参数是存活响应的最高时间限制。如果某个响应驻留在cache里的时间高于这个最高限制,那么它必须被刷新。
在最低和最高时间限制之间的响应,会面对squid的最后修改系数 (LM-factor)算法。对这样的响应,squid计算响应的年龄和最后修改系数,然后将它作为百分比值进行比较。响应年龄简单的就是从原始服务器产生,或最后一次验证响应后,经历的时间数量。源年龄在Last-Modified和Date头部之间是不同的。LM-factor是响应年龄与源年龄的比率。
图7-2论证了LM-factor算法。squid缓存了某个目标3个小时(基于Date和Last-Modified头部)。LM-factor的值是50%,响应在接下来的1.5个小时里是存活的,在这之后,目标会过期并被当作过时处理。假如用户在存活期间请求cache目标,squid返回没有确认的cache命中。若在过时期间发生请求,squid转发确认请求到原始服务器。
图7-2 基于LM-factor计算过期时间
(略图)
理解squid检查不同值的顺序非常重要。如下是squid的refresh_pattern算法的简单描述: + 假如响应年龄超过refresh_pattern的max值,该响应过期; + 假如LM-factor少于refresh_pattern百分比值,该响应存活; + 假如响应年龄少于refresh_pattern的min值,该响应存活; + 其他情况下,响应过期。
refresh_pattern指令也有少数选项导致squid违背HTTP协议规范。它们如下: override-expire 该选项导致squid在检查Expires头部之前,先检查min值。这样,一个非零的min时间让squid返回一个未确认的cache命中,即使该响应准备过期。
override-lastmod 改选项导致squid在检查LM-factor百分比之前先检查min值。
reload-into-ims 该选项让squid在确认请求里,以no-cache指令传送一个请求。换句话说,squid在转发请求之前,对该请求增加一个If-Modified-Since头部。注意这点仅仅在目标有Last-Modified时间戳时才能工作。外面进来的请求保留no-cache指令,以便它到达原始服务器。
ignore-reload 该选项导致squid忽略请求里的任何no-cache指令。
第8章 高级磁盘缓存主题
8.1 是否存在磁盘I/O瓶颈?
Web缓存器例如squid,通常在磁盘I/O变成瓶颈时,不会正确的体现和告知你。代替的是,随着负载的增加,响应时间和/或命中率会更低效。当然,响应时间和命中率可能因为其他原因而改变,例如网络延时和客户请求方式的改变。
也许探测cache性能瓶颈的最好方式是做压力测试,例如Web Polygraph。压力测试的前提是你能完全控制环境,消除未知因素。你可以用不同的cache配置来重复相同的测试。不幸的是,压力测试通常需要大量的时间,并要求有空闲的系统(也许它们正在使用中)。
假如你有资源执行squid压力测试,请以标准的cache工作负载开始。当你增加负载时,在某些点上你能看到明显的响应延时和/或命中率下降。一旦你观察到这样的性能降低,就禁止掉磁盘缓存,再测试一次。你可以配置squid从来不缓存任何响应(使用null存储机制,见8.7章)。代替的,你能配置工作负载到100%不可cache响应。假如不使用cache时,平均响应时间明显更好,那么可以确认磁盘I/O是该水平吞吐量的瓶颈。 假如你没有时间或没有资源来执行squid压力测试,那么可检查squid的运行时统计来查找磁盘I/O瓶颈。cache管理器的General Runtime Information 页面(见14章)会显示出cache命中和cache丢失的中值响应时间。 Median Service Times (seconds) 5 min 60 min: HTTP Requests (All): 0.39928 0.35832 Cache Misses: 0.42149 0.39928 Cache Hits: 0.12783 0.11465 Near Hits: 0.37825 0.39928 Not-Modified Replies: 0.07825 0.07409
对健壮的squid缓存来说,命中显然快于丢失。中值命中响应时间典型的少于0.5秒或更少。我强烈建议你使用SNMP或其他的网络监视工具来从squid缓存采集定期测量值。如果平均命中响应时间增加得太明显,意味着系统有磁盘I/0瓶颈。
假如你认为产品cache面临此类问题,可以用前面提到的同样的技术来验证你的推测。配置squid不cache任何响应,这样就避开了所有磁盘I/O。然后仔细观察cache丢失响应时间。假如它降下去,那么你的推测该是正确的。
一旦你确认了磁盘吞吐能力是squid的性能瓶颈,那么可做许多事来改进它。其中一些方法要求重编译squid,然而另一些相对较简单,只需调整Unix文件系统。
8.2 文件系统调整选项
首先,从来不在squid的缓存目录中使用RAID。以我的经验看,RAID总是降低squid使用的文件系统的性能。最好有许多独立的文件系统,每个文件系统使用单独的磁盘驱动器。
我发现4个简单的方法来改进squid的UFS性能。其中某些特指某种类型的操作系统例如BSD和Linux,也许对你的平台不太合适:
1.某些UFS支持一个noatime的mount选项。使用noatime选项来mount的文件系统,不会在读取时,更新相应的i节点访问时间。使用该选项的最容易的方法是在/etc/fstab里增加如下行: # Device Mountpoint FStype Options Dump Pass# /dev/ad1s1c /cache0 ufs rw,noatime 0 0
2.检查mount(的manpage里的async选项。设置了该选项,特定的I/O操作(例如更新目录)会异步执行。某些系统的文档会标明这是个危险的标签。某天你的系统崩溃,你也许会丢失整个文件系统。对许多squid安装来说,执行性能的提高值得冒此风险。假如你不介意丢失整个cache内容,那么可以使用该选项。假如cache数据非常有价值,async选项也许不适合你。
3.BSD有一个功能叫做软更新。软更新是BSD用于Journaling文件系统的代替品。在FreeBSD上,你可以在没有mount的文件系统中,使用tunefs命令来激活该选项: # umount /cache0 # tunefs -n enable /cache0 # mount /cache0
4.你对每个文件系统运行一次tunefs命令就可以了。在系统重启时,软更新自动在文件系统中激活了。 在OpenBSD和NetBSD中,可使用softdep mount选项: # Device Mountpoint FStype Options Dump Pass# /dev/sd0f /usr ffs rw,softdep 1 2
假如你象我一样,你可能想知道在async选项和软更新选项之间有何不同。一个重要的区别是,软更新代码被设计成在系统崩溃事件中,保持文件系统的一致性,而async选项不是这样的。这也许让你推断async执行性能好于软更新。然而,如我在附录D中指出的,事实相反。
以前我提到过,UFS性能特别是写性能,依赖于空闲磁盘的数量。对空文件系统的磁盘写操作,要比满文件系统快得多。这是UFS的最小自由空间参数,和空间/时间优化权衡参数背后的理由之一。假如cache磁盘满了,squid执行性能看起来很糟,那么试着减少cache_dir的容量值,以便更多的自由空间可用。当然,减少cache大小也会降低命中率,但响应时间的改进也许值得这么做。假如你给squid缓存配置新的设备,请考虑使用超过你需要的更大磁盘,并且仅仅使用空间的一半。
8.3 可选择的文件系统
某些操作系统支持不同于UFS(或ext2fs)的文件系统。Journaling文件系统是较普遍的选择。在UFS和Journaling文件系统之间的主要不同在于它们处理更新的方式。在UFS下,更新是实时的。例如,当你改变了某个文件并且将它存储到磁盘,新数据就替换了旧数据。当你删除文件时,UFS直接更新了目录。
Journaling文件系统与之相反,它将更新写往独立的记帐系统,或日志文件。典型的你能选择是否记录文件改变或元数据改变,或两者兼备。某个后台进程在空闲时刻读取记帐,并且执行实际的改变操作。Journaling文件系统典型的在系统崩溃后比UFS恢复更快。在系统崩溃后,Journaling文件系统简单的读取记帐,并且提交所有显著的改变。
Journaling文件系统的主要弊端在于它们需要额外的磁盘写操作。改变首先写往日志文件,然后才写往实际的文件或目录。这对web缓存影响尤其明显,因为首先web缓存倾向于更多的磁盘写操作。
Journaling文件系统对许多操作系统可用。在Linux上,你能选择ext3fs,reiserfs, XFS,和其他的。XFS也可用在SGI/IRIX,它原始是在这里开发的。Solaris用户能使用Veritas文件系统产品。TRU64(以前的Digital Unix)高级文件系统(advfs)支持Journaling。 你可以不改变squid的任何配置而使用Journaling文件系统。简单的创建和挂载在操作系统文档里描述的文件系统,而不必改变squid.cf配置文件里的cache_dir行。
用类似如下命令在Linux中制作reiserfs文件系统: # /sbin/mkreiserfs /dev/sda2
对XFS,使用: # mkfs -t xfs -f /dev/sda2
注意ext3fs其实简单的就是激活了记帐的ext2fs。当创建该文件系统时,对mke2fs使用-j选项: # /sbin/mke2fs -j /dev/sda2
请参考其他操作系统的相关文档。
8.4 aufs存储机制
aufs存储机制已经发展到超出了改进squid磁盘I/O响应时间的最初尝试。"a"代表着异步I/O。默认的ufs和aufs之间的唯一区别,在于I/O是否被squid主进程执行。数据格式都是一样的,所以你能在两者之间轻松选择,而不用丢失任何cache数据。
aufs使用大量线程进行磁盘I/O操作。每次squid需要读写,打开关闭,或删除cache文件时,I/O请求被分派到这些线程之一。当线程完成了I/O后,它给squid主进程发送信号,并且返回一个状态码。实际上在squid2.5中,某些文件操作默认不是异步执行的。最明显的,磁盘写总是同步执行。你可以修改src/fs/aufs/store_asyncufs.h文件,将ASYNC_WRITE设为1,并且重编译squid。
aufs代码需要pthreads库。这是POSIX定义的标准线程接口。尽管许多Unix系统支持pthreads库,但我经常遇到兼容性问题。aufs存储系统看起来仅仅在Linux和Solaris上运行良好。在其他操作系统上,尽管代码能编译,但也许会面临严重的问题。
为了使用aufs,可以在./configure时增加一个选项: % ./configure --enable-storeio=aufs,ufs
严格讲,你不必在storeio模块列表中指定ufs。然而,假如你以后不喜欢aufs,那么就需要指定ufs,以便能重新使用稳定的ufs存储机制。 假如愿意,你也能使用—with-aio-threads=N选项。假如你忽略它,squid基于aufs cache_dir的数量,自动计算可使用的线程数量。表8-1显示了1-6个cache目录的默认线程数量。 Table 8-1. Default number of threads for up to six cache directories cache_dirs Threads 1 16 2 26 3 32 4 36 5 40 6 44
将aufs支持编译进squid后,你能在squid.conf文件里的cache_dir行后指定它: cache_dir aufs /cache0 4096 16 256
在激活了aufs并启动squid后,请确认每件事仍能工作正常。可以运行tail -f store.log一会儿,以确认缓存目标被交换到磁盘。也可以运行tail -f cache.log并且观察任何新的错误或警告。
8.4.1 aufs如何工作
Squid通过调用pthread_create()来创建大量的线程。所有线程在任何磁盘活动之上创建。这样,即使squid空闲,你也能见到所有的线程。 无论何时,squid想执行某些磁盘I/O操作(例如打开文件读),它分配一对数据结构,并将I/O请求放进队列中。线程循环读取队列,取得I/O请求并执行它们。因为请求队列共享给所有线程,squid使用独享锁来保证仅仅一个线程能在给定时间内更新队列。
I/O操作阻塞线程直到它们被完成。然后,将操作状态放进一个完成队列里。作为完整的操作,squid主进程周期性的检查完成队列。请求磁盘I/O的模块被通知操作已完成,并获取结果。
你可能已猜想到,aufs在多CPU系统上优势更明显。唯一的锁操作发生在请求和结果队列。然而,所有其他的函数执行都是独立的。当主进程在一个CPU上执行时,其他的CPU处理实际的I/O系统调用。
8.4.2 aufs发行 线程的有趣特性是所有线程共享相同的资源,包括内存和文件描述符。例如,某个线程打开一个文件,文件描述符为27,所有其他线程能以相同的文件描述符来访问该文件。可能你已知道,在初次管理squid时,文件描述符短缺是较普遍问题。Unix内核典型的有两种文件描述符限制: 进程级的限制和系统级的限制。你也许认为每个进程拥有256个文件描述符足够了(因为使用线程),然而并非如此。在这样的情况下,所有线程共享少量的文件描述符。请确认增加系统的进程文件描述符限制到4096或更高,特别在使用aufs时。
调整线程数量有点棘手。在某些情况下,可在cache.log里见到如下警告: 2003/09/29 13:42:47| squidaio_queue_request: WARNING - Disk I/O overloading
这意味着squid有大量的I/O操作请求充满队列,等待着可用的线程。你首先会想到增加线程数量,然而我建议,你该减少线程数量。
增加线程数量也会增加队列的大小。超过一定数量,它不会改进aufs的负载能力。它仅仅意味着更多的操作变成队列。太长的队列导致响应时间变长,这绝不是你想要的。
减少线程数量和队列大小,意味着squid检测负载条件更快。当某个cache_dir超载,它会从选择算法里移除掉(见7.4章)。然后,squid选择其他的cache_dir或简单的不存储响应到磁盘。这可能是较好的解决方法。尽管命中率下降,响应时间却保持相对较低。
8.4.3 监视aufs操作
Cache管理器菜单里的Async IO Counters选项,可以显示涉及到aufs的统计信息。它显示打开,关闭,读写,stat,和删除接受到的请求的数量。例如: % squidclient mgr:squidaio_counts ... ASYNC IO Counters: Operation # Requests open 15318822 close 15318813 cancel 15318813 write 0 read 19237139 stat 0 unlink 2484325 check_callback 311678364 queue 0
取消(cancel)计数器正常情况下等同于关闭(close)计数器。这是因为close函数总是调用cancel函数,以确认任何未决的I/O操作被忽略。 写(write)计数器为0,因为该版本的squid执行同步写操作,即使是aufs。
check_callbak计数器显示squid主进程对完成队列检查了多少次。
queue值显示当前请求队列的长度。正常情况下,队列长度少于线程数量的5倍。假如你持续观察到队列长度大于这个值,说明squid配得有问题。增加更多的线程也许有帮助,但仅仅在特定范围内。
8.5 diskd存储机制
diskd(disk守护进程的短称)类似于aufs,磁盘I/O被外部进程来执行。不同于aufs的是,diskd不使用线程。代替的,它通过消息队列和共享内存来实现内部进程间通信。
消息队列是现代Unix操作系统的标准功能。许多年以前在AT&T的Unix System V的版本1上实现了它们。进程间的队列消息以较少的字节传递:32-40字节。每个diskd进程使用一个队列来接受来自squid的请求,并使用另一个队列来传回请求。
8.5.1 diskd如何工作
Squid对每个cache_dir创建一个diskd进程。这不同于aufs,aufs对所有的cache_dir使用一个大的线程池。对每个I/O操作,squid发送消息到相应的diskd进程。当该操作完成后,diskd进程返回一个状态消息给squid。squid和diskd进程维护队列里的消息的顺序。这样,不必担心I/O会无序执行。
对读和写操作,squid和diskd进程使用共享内存区域。两个进程能对同一内存区域进行读和写。例如,当squid产生读请求时,它告诉diskd进程在内存中何处放置数据。diskd将内存位置传递给read()系统调用,并且通过发送队列消息,通知squid该过程完成了。然后squid从共享内存区域访问最近的可读数据。
diskd与aufs本质上都支持squid的无阻塞磁盘I/O。当diskd进程在I/O操作上阻塞时,squid有空去处理其他任务。在diskd进程能跟上负载情况下,这点确实工作良好。因为squid主进程现在能够去做更多工作,当然它有可能会加大diskd的负载。diskd有两个功能来帮助解决这个问题。 首先,squid等待diskd进程捕获是否队列超出了某种极限。默认值是64个排队消息。假如diskd进程获取的数值远大于此,squid会休眠片刻,并等待diskd完成一些未决操作。这本质上让squid进入阻塞I/O模式。它也让更多的CPU时间对diskd进程可用。通过指定cache_dir行的Q2参数的值,你可以配置这个极限值: cache_dir diskd /cache0 7000 16 256 Q2=50
第二,假如排队操作的数量抵达了另一个极限,squid会停止要求diskd进程打开文件。这里的默认值是72个消息。假如squid想打开一个磁盘文件读或写,但选中的cache_dir有太多的未完成操作,那么打开请求会失败。当打开文件读时,会导致cache丢失。当打开文件写时,会阻碍squid存储cache响应。这两种情况下用户仍能接受到有效响应。唯一实际的影响是squid的命中率下降。这个极限用Q1参数来配置: cache_dir diskd /cache0 7000 16 256 Q1=60 Q2=50
注意在某些版本的squid中,Q1和Q2参数混杂在默认的配置文件里。最佳选择是,Q1应该大于Q2。
8.5.2 编译和配置diskd
为了使用diskd,你必须在运行./configure时,在--enable-storeio列表后增加一项: % ./configure --enable-storeio=ufs,diskd
diskd看起来是可移植的,既然共享内存和消息队列在现代Unix系统上被广泛支持。然而,你可能需要调整与这两者相关的内核限制。内核典型的有如下可用参数:
MSGMNB 每个消息队列的最大字节限制。对diskd的实际限制是每个队列大约100个排队消息。squid传送的消息是32-40字节,依赖于你的CPU体系。这样,MSGMNB应该是4000或更多。为安全起见,我推荐设置到8192。
MSGMNI 整个系统的最大数量的消息队列。squid对每个cache_dir使用两个队列。假如你有10个磁盘,那就有20个队列。你也许该增加更多,因为其他应用程序也要使用消息队列。我推荐的值是40。
MSGGSZ 消息片断的大小(字节)。大于该值的消息被分割成多个片断。我通常将这个值设为64,以使diskd消息不被分割成多个片断。
MSGSEG 在单个队列里能存在的最大数量的消息片断。squid正常情况下,限制队列的长度为100个排队消息。记住,在64位系统中,假如你没有增加MSGSSZ的值到64,那么每个消息就会被分割成不止1个片断。为了安全起见,我推荐设置该值到512。
MSGTQL 整个系统的最大数量的消息。至少是cache_dir数量的100倍。在10个cache目录情况下,我推荐设置到2048。
MSGMAX 单个消息的最大size。对Squid来说,64字节足够了。然而,你系统中的其他应用程序可能要用到更大的消息。在某些操作系统例如BSD中,你不必设置这个。BSD自动设置它为MSGSSZ * MSGSEG。其他操作系统中,你也许需要改变这个参数的默认值,你可以设置它与MSGMNB相同。
SHMSEG 每个进程的最大数量的共享内存片断。squid对每个cache_dir使用1个共享内存标签。我推荐设置到16或更高。
SHMMNI 共享内存片断数量的系统级的限制。大多数情况下,值为40足够了。
SHMMAX 单个共享内存片断的最大size。默认的,squid对每个片断使用大约409600字节。 为安全起见,我推荐设置到2MB,或2097152。
SHMALL 可分配的共享内存数量的系统级限制。在某些系统上,SHMALL可能表示成页数量,而不是字节数量。在10个cache_dir的系统上,设置该值到16MB(4096页)足够了,并有足够的保留给其他应用程序。
在BSD上配置消息队列,增加下列选项到内核配置文件里:
# System V message queues and tunable parameters options SYSVMSG # include support for message queues options MSGMNB=8192 # max characters per message queue options MSGMNI=40 # max number of message queue identifiers options MSGSEG=512 # max number of message segments per queue options MSGSSZ=64 # size of a message segment MUST be power of 2 options MSGTQL=2048 # max number of messages in the system options SYSVSHM options SHMSEG=16 # max shared mem segments per process options SHMMNI=32 # max shared mem segments in the system options SHMMAX=2097152 # max size of a shared mem segment options SHMALL=4096 # max size of all shared memory (pages)
在Linux上配置消息队列,增加下列行到/etc/sysctl.conf:
kernel.msgmnb=8192 kernel.msgmni=40 kernel.msgmax=8192 kernel.shmall=2097152 kernel.shmmni=32 kernel.shmmax=16777216
另外,假如你需要更多的控制,可以手工编辑内核资源文件中的include/linux/msg.h和include/linux/shm.h。
在Solaris上,增加下列行到/etc/system,然后重启:
set msgsys:msginfo_msgmax=8192 set msgsys:msginfo_msgmnb=8192 set msgsys:msginfo_msgmni=40 set msgsys:msginfo_msgssz=64 set msgsys:msginfo_msgtql=2048 set shmsys:shminfo_shmmax=2097152 set shmsys:shminfo_shmmni=32 set shmsys:shminfo_shmseg=16
在Digital Unix(TRU64)上,可以增加相应行到BSD风格的内核配置文件中,见前面所叙。另外,你可使用sysconfig命令。首先,创建如下的ipc.stanza文件:
ipc: msg-max = 2048 msg-mni = 40 msg-tql = 2048 msg-mnb = 8192 shm-seg = 16 shm-mni = 32 shm-max = 2097152 shm-max = 4096
然后,运行这个命令并重启: # sysconfigdb -a -f ipc.stanza
一旦你在操作系统中配置了消息队列和共享内存,就可以在squid.conf里增加如下的cache_dir行: cache_dir diskd /cache0 7000 16 256 Q1=72 Q2=64 cache_dir diskd /cache1 7000 16 256 Q1=72 Q2=64 ...
8.5.3 监视diskd
监视diskd运行的最好方法是使用cache管理器。请求diskd页面,例如:
% squidclient mgr:diskd ... sent_count: 755627 recv_count: 755627 max_away: 14 max_shmuse: 14 open_fail_queue_len: 0 block_queue_len: 0
OPS SUCCESS FAIL open 51534 51530 4 create 67232 67232 0 close 118762 118762 0 unlink 56527 56526 1 read 98157 98153 0 write 363415 363415 0
请见14.2.1.6章关于该输出的详细描述。
8.6 coss存储机制
循环目标存储机制(Cyclic Object Storage Scheme,coss)尝试为squid定制一个新的文件系统。在ufs基础的机制下,主要的性能瓶颈来自频繁的open()和unlink()系统调用。因为每个cache响应都存储在独立的磁盘文件里,squid总是在打开,关闭,和删除文件。
与之相反的是,coss使用1个大文件来存储所有响应。在这种情形下,它是特定供squid使用的,小的定制文件系统。coss实现许多底层文件系统的正常功能,例如给新数据分配空间,记忆何处有自由空间等。
不幸的是,coss仍没开发完善。coss的开发在过去数年里进展缓慢。虽然如此,基于有人喜欢冒险的事实,我还是在这里描述它。
8.6.1 coss如何工作
在磁盘上,每个coss cache_dir是一个大文件。文件大小一直增加,直到抵达它的大小上限。这样,squid从文件的开头处开始,覆盖掉任何存储在这里的数据。然后,新的目标总是存储在该文件的末尾处。
squid实际上并不立刻写新的目标数据到磁盘上。代替的,数据被拷贝进1MB的内存缓冲区,叫做stripe。在stripe变满后,它被写往磁盘。coss使用异步写操作,以便squid主进程不会在磁盘I/O上阻塞。
象其他文件系统一样,coss也使用块大小概念。在7.1.4章里,我谈到了文件号码。每个cache目标有一个文件号码,以便squid用于定位磁盘中的数据。对coss来说,文件号码与块号码一样。例如,某个cache目标,其交换文件号码等于112,那它在coss文件系统中就从第112块开始。因此coss不分配文件号码。某些文件号码不可用,因为cache目标通常在coss文件里占用了不止一个块。
coss块大小在cache_dir选项中配置。因为squid的文件号码仅仅24位,块大小决定了coss缓存目录的最大size:size = 块大小 x (2的24次方)。例如,对512字节的块大小,你能在coss cache_dir中存储8GB数据。
coss不执行任何squid正常的cache置换算法(见7.5章)。代替的,cache命中被"移动"到循环文件的末尾。这本质上是LRU算法。不幸的是,它确实意味着cache命中导致磁盘写操作,虽然是间接的。
在coss中,没必要去删除cache目标。squid简单的忘记无用目标所分配的空间。当循环文件的终点再次抵达该空间时,它就被重新利用。
8.6.2 编译和配置coss
为了使用coss,你必须在运行./configure时,在--enable-storeio列表里增加它: % ./configure --enable-storeio=ufs,coss ...
coss缓存目录要求max-size选项。它的值必须少于stripe大小(默认1MB,但可以用--enable-coss-membuf-size选项来配置)。也请注意你必须忽略L1和L2的值,它们被ufs基础的文件系统使用。如下是示例:
cache_dir coss /cache0/coss 7000 max-size=1000000 cache_dir coss /cache1/coss 7000 max-size=1000000 cache_dir coss /cache2/coss 7000 max-size=1000000 cache_dir coss /cache3/coss 7000 max-size=1000000 cache_dir coss /cache4/coss 7000 max-size=1000000
甚至,你可以使用block-size选项来改变默认的coss块大小。 cache_dir coss /cache0/coss 30000 max-size=1000000 block-size=2048
关于coss的棘手的事情是,cache_dir目录参数(例如/cache0/coss)实际上不是目录,它是squid打开或创建的常规文件。所以你可以用裸设备作为coss文件。假如你错误的创建coss文件作为目录,你可以在squid启动时见到如下错误:
2003/09/29 18:51:42| /usr/local/squid/var/cache: (21) Is a directory FATAL: storeCossDirInit: Failed to open a coss file.
因为cache_dir参数不是目录,你必须使用cache_swap_log指令(见13.6章)。否则squid试图在cache_dir目录中创建swap.state文件。在该情形下,你可以见到这样的错误:
2003/09/29 18:53:38| /usr/local/squid/var/cache/coss/swap.state: (2) No such file or directory FATAL: storeCossDirOpenSwapLog: Failed to open swap log.
coss使用异步I/O以实现更好的性能。实际上,它使用aio_read()和aio_write()系统调用。这点也许并非在所有操作系统中可用。当前它们可用在FreeBSD,Solaris,和Linux中。假如coss代码看起来编译正常,但你得到"Function not implemented"错误消息,那就必须在内核里激活这些系统调用。在FreeBSD上,必须在内核配置文件中有如下选项: options VFS_AIO
8.6.3 coss发行
coss还是实验性的功能。没有充分证实源代码在日常使用中的稳定性。假如你想试验一下,请做好存储在coss cache_dir中的资料丢失的准备。 从另一面说,coss的初步性能测试表现非常优秀。示例请见附录D。
coss没有很好的支持从磁盘重建cache数据。当你重启squid时,你也许会发现从swap.state文件读取数据失败,这样就丢失了所有的缓存数据。甚至,squid在重启后,不能记忆它在循环文件里的位置。它总是从头开始。
coss对目标置换采用非标准的方式。相对其他存储机制来说,这可能导致命中率更低。
某些操作系统在单个文件大于2GB时,会有问题。假如这样的事发生,你可以创建更多小的coss区域。例如: cache_dir coss /cache0/coss0 1900 max-size=1000000 block-size=128 cache_dir coss /cache0/coss1 1900 max-size=1000000 block-size=128 cache_dir coss /cache0/coss2 1900 max-size=1000000 block-size=128 cache_dir coss /cache0/coss3 1900 max-size=1000000 block-size=128
使用裸磁盘设备(例如/dev/da0s1c)也不会工作得很好。理由之一是磁盘设备通常要求I/O发生在512个字节的块边界(译者注:也就是块设备访问)。另外直接的磁盘访问绕过了系统高速缓存,可能会降低性能。然而,今天的许多磁盘驱动器,已经内建了高速缓存。
8.7 null存储机制 Squid有第5种存储机制叫做null。就像名字暗示的一样,这是最不健壮的机制。写往null cache_dir的文件实际上不被写往磁盘。 大多数人没有任何理由要使用null存储系统。当你想完全禁止squid的磁盘缓存时,null才有用。你不能简单的从squid.conf文件里删除所有cache_dir行,因为这样的话squid会增加默认的ufs cache_dir。null存储系统有些时候在测试squid,和压力测试时有用。既然文件系统是典型的性能瓶颈,使用null存储机制能获取基于当前硬件的squid的性能上限。
为了使用该机制,在运行./configure时,你必须首先在--enable-storeio列表里指定它: % ./configure --enable-storeio=ufs,null ...
然后在squid.conf里创建cache_dir类型为null: cache_dir /tmp null
也许看起来有点奇怪,你必须指定目录给null存储机制。squid使用目录名字作为cache_dir标识符。例如,你能在cache管理器的输出里见到它。 8.8 哪种最适合我?
Squid的存储机制选择看起来有点混乱和迷惑。aufs比diskd更好?我的系统支持aufs或coss吗?假如我使用新的机制,会丢失数据吗?可否混合使用存储机制?
首先,假如Squid轻度使用(就是说每秒的请求数少于5个),默认的ufs存储机制足够了。在这样的低请求率中,使用其他存储机制,你不会观察到明显的性能改进。
假如你想决定何种机制值得一试,那你的操作系统可能是个决定因素。例如,aufs在Linux和Solaris上运行良好,但看起来在其他系统中有问题。另外,coss代码所用到的函数,当前不可用在某些操作系统中(例如NetBSD)。
从我的观点看来,高性能的存储机制在系统崩溃事件中,更易受数据丢失的影响。这就是追求最好性能的权衡点。然而对大多数人来说,cache数据相对价值较低。假如squid的缓存因为系统崩溃而破坏掉,你会发现这很容易,只需简单的newfs磁盘分区,让cache重新填满即可。如果你觉得替换Squid的缓存内容较困难或代价很大,你就应该使用低速的,但可信的文件系统和存储机制。
近期的Squid允许你对每个cache_dir使用不同的文件系统和存储机制。然而实际上,这种做法是少见的。假如所有的cache_dir使用相同的size和相同的存储机制,可能冲突更少。
第9章 Cache拦截
Cache拦截是让传输流向Squid的流行技术,它不用配置任何客户端。你可以配置路由器或交换机将HTTP连接转发到squid运行的主机。squid运行的操作系统被配置成接受外部数据包,并将其递交给squid进程。为了让HTTP拦截生效,你必须配置3个独立的因素:网络设备,squid运行的操作系统,和squid自身。;
(译者注:Cache拦截实际上指的是Squid的透明代理)
9.1它如何工作?
Cache拦截包含了某些网络欺骗,它对理解在客户端和Squid之间的会话有用。我使用图9-1和如下的tcpdump示例输出,来解释当数据包通过网络时,如何被拦截。
1.用户代理(user-agent)想请求某个资源,它对原始服务器发起index.html请求,例如:。它需要原始服务器的IP地址,所以先发起一个DNS请求:
Packet 1 TIME: 19:54:41.317310 UDP: 206.168.0.3.2459 ->; 206.168.0.2.53 DATA: .d........... ---------------------------------------------------------------------------
Packet 2 TIME: 19:54:41.317707 (0.000397) UDP: 206.168.0.2.53 ->; 206.168.0.3.2459 DATA: .d...................PR.....%........PR. ....$........PR...ns1.sonic.net.........PR...ns2.Q........PR ...ns...M...............h.............!.z.......b......
2.现在有了IP地址,用户代理初始化到原始服务器80端口的TCP连接:
Packet 3 TIME: 19:54:41.320652 (0.002945) TCP: 206.168.0.3.3897 ->; 208.201.239.37.80 Syn DATA: ;
3.路由器或交换机注意到目的地址是80端口的TCP SYN包。下一步会发生什么依赖于特定的拦截技术。在4层交换和路由策略上,网络设备简单的将TCP包转发到Squid的数据链路地址。当squid直接挂在网络设备上时,就这样工作。对WCCP来说,路由器封装TCP包为GRE包。因为GRE包有它自己的IP地址,它可能被通过多个子网进行路由。换句话说,WCCP不要求squid直接挂在路由器上。
4.Squid主机的操作系统接受到拦截包。对4层交换来说,TCP/IP包并没有改变。
假如包使用了GRE封装,主机会剥离外部的IP和GRE头部,并将原始的TCP/IP包放在输入队列里。
注意squid主机接受到的包是针对外部地址的(原始服务器的)。正常情况下,这个包不匹配任何本地地址,它会被丢弃。为了让主机接受外部数据包,你必须在大多数操作系统上激活IP转发。
5.客户端的TCP/IP包被包过滤代码处理。数据包必须匹配某个规则,该规则指示内核转交这个包给squid。如果没有这样的规则,内核简单的将包按照它自己的方式转发给原始服务器,这不是你想要的。
注意SYN包的目的端口是80,但squid可能侦听在不同的端口,例如3128。包过滤规则允许你改变端口号。你不必让squid侦听在80端口。通过tcpdump,你能见到这步,因为转发的包不会再次通过网络接口代码。
即使squid侦听在80端口,包过滤器的重定向规则仍是必要的。可以让squid不在这些端口上接受拦截包。重定向规则有点神奇,它转交外部数据包给squid。
6.Squid接受到新连接的通知,它接受这个连接。内核发送SYN/ACK包返回给客户端:
Packet 4 TIME: 19:54:41.320735 (0.000083) TCP: 208.201.239.37.80 ->; 206.168.0.3.3897 SynAck DATA: ;
就象你见到的一样,源地址是原始服务器,尽管这个包不会抵达原始服务器。操作系统只是简单的将源地址和目的地址交换一下,并将它放进响应数据包里。
7.用户代理接受到SYN/ACK包,建立起完整的TCP连接。用户代理现在相信它是连接到原始服务器,所以它发送HTTP请求:
Packet 5 TIME: 19:54:41.323080 (0.002345) TCP: 206.168.0.3.3897 ->; 208.201.239.37.80 Ack DATA: ; --------------------------------------------------------------------------- Packet 6 TIME: 19:54:41.323482 (0.000402) TCP: 206.168.0.3.3897 ->; 208.201.239.37.80 AckPsh DATA: GET / HTTP/1.0 User-Agent: Wget/1.8.2 Host: Accept: */* Connection: Keep-Alive
8.Squid接受HTTP请求。它使用HTTP Host头部来转换局部URL为完整的URL。在这种情形下,可在access.log文件里见到。
9.从这点开始,squid正常的处理请求。一般cache命中会立刻返回。cache丢失会转发到原始服务器。
10.最后,是squid从原始服务器接受到的响应:
Packet 8 TIME: 19:54:41.448391 (0.030030) TCP: 208.201.239.37.80 ->; 206.168.0.3.3897 AckPsh DATA: HTTP/1.0 200 OK Date: Mon, 29 Sep 2003 01:54:41 GMT Server: Apache/1.3.26 (Unix) PHP/4.2.1 mod_gzip/1.3.19.1a mo d_perl/1.27 P3P: policyref=" AO DSP COR CURa ADMa DEVa TAIa PSAa PSDa IVAa IVDa CONo OUR DELa PUBi OTRa IND PHY ONL UNI PUR COM NAV INT DEM CNT STA P RE" Last-Modified: Sun, 28 Sep 2003 23:54:44 GMT ETag: "1b76bf-b910-3ede86c4" Accept-Ranges: bytes Content-Length: 47376 Content-Type: text/html X-Cache: MISS from X-Cache: MISS from 10.0.0.1 Connection: keep-alive
不应该让交换机或路由器来拦截squid到原始服务器的连接。假如这种情况发生,squid结束与自己的会话,并且不能满足任何cache丢失。防止这类转发死循环的最好方法是,确认用户和squid连接到交换机或路由器的独立接口。无论何时,应该在指定接口上应用拦截规则。最明显的,不该在squid使用的接口上激活拦截。
9.2为何要(或不要)拦截?
许多单位发现,cache拦截很有用,因为他们不能,或不愿意配置所有用户的web浏览器。相对于配置成百上千台工作站来说,在单个交换机或路由器上做一点网络欺骗更容易。从我们面临的许多选择来看,cache拦截确实有好也有坏。它可能让你的生活更容易,但也许会更难。
Cache拦截的最明显的贡献是,所有HTTP请求通过squid自动离开你的网络。你不必担心配置任何浏览器,用户可能在浏览器上禁止他们的代理设置。cache拦截让网络管理员完全控制HTTP会话。你可以改变,增加,或删除squid的缓存,而不会显著影响你的用户上网冲浪。
关于HTTP拦截的主要不利点就是该技术违背了TCP/IP的标准。这些协议要求路由器或交换机转发TCP/IP包到目的IP地址里指定的主机。然而转发包到cache代理破坏了这些规则。代理伪装身份接受转交过来的连接。用户代理被欺骗了,以为它们在与真正的web服务器会话。
这样的混乱导致在老版本的Microsoft IE浏览器中产生严重问题。浏览器的Reload按钮是刷新HTML页面的最容易的方法。当浏览器被配置成使用cache代理时,reload请求包含了一个Cache-Control:no-cache头部,它强迫产生cache丢失(或cache确认),并确保响应是最近更新的。假如没有明确配置使用代理,浏览器会忽略该头部。当使用cache拦截时,浏览器认为它在连接到原始服务器,因此没必要发送该头部。在这种情形下,squid不会告知用户的Reload按钮,也许不会验证cache响应。squid的ie_refresh提供了解决此bug的局部解决方法(见附录A)。Microsoft已经在其IE 5.5 SP1中解决了这个问题。
因为类似的理由,你不能结合cache拦截使用HTTP代理验证。因为客户端不知道这个代理,它不会发送必要的Proxy-Authorization头部。另外,407(代理验证请求)响应代码也不恰当,因为响应看起来象来自原始服务器,原始服务器从来不会发送如此响应。
也不能在cache拦截中使用RFC 1413 ident查询(见6.1.2.11章节)。Squid不能对必要的IP地址建立新的TCP Socket连接。操作系统在转发拦截连接到squid时,它执行欺骗。然后,当squid希望bind新的TCP Socket到外部IP地址时,它不能执行欺骗。它想bind的地址实际上并非真正本地的,所以bind系统调用失败。
cache拦截也与设计成阻止地址欺骗的IP过滤冲突(见RFC 2267:Network Ingress Filtering: Defeating Denial of Service Attacks Which Employ IP Source AddressSpoofing)。考虑如图9-2显示的网络。路由器有2个LAN接口:lan0和lan1。网络管理员在路由器上使用包过滤器,以确保没有内部主机传送假冒源地址的数据包。路由器只会转发源地址对应相连网络的数据包。包过滤规则也许看起来如下:
# lan0 allow ip from 172.16.1.0/24 to any via lan0 deny ip from any to any via lan0
# lan1 allow ip from 10.0.0.0/16 to any via lan1 deny ip from any to any via lan1
现在看看,当路由器和lan1中的squid主机配置成拦截来自lan0中的HTTP连接后,会发生什么。Squid装扮成原始服务器,这意味着从squid到用户的响应TCP包欺骗了源地址。lan0过滤规则导致路由器拒绝这些包。为了让cache拦截生效,网络管理员须移除lan0规则。这样就让网络有漏洞,从而易遭拒绝服务攻击。
我在先前的章节里描述过,客户端在打开连接之前必须先进行DNS查询。在某些防火墙环境中,这样做可能有问题。你想进行HTTP拦截的主机必须能够查询DNS。如果客户端了解自己正使用代理(因为手工配置或代理自动配置),它通常就不去解析主机名。代替的,它简单的将完整URL转发给squid,由squid来查询原始服务器的IP地址。
另一个小问题是,squid接受任意目的IP地址的连接。例如,某个web站点当机了,但它仍然有DNS记录存在。squid伪装这个站点接受TCP连接。客户端会认为该站点仍然在运行,因为连接有效。当squid连接到原始服务器失败时,它强迫返回错误消息。
万一形势不清,HTTP拦截在初次使用时有些棘手或困难。许多不同的组件必须组合工作,并且要配置正确。甚至,从内存中恢复整个配置也很困难。我强烈建议你在将其应用于生产环境之前,先建立测试环境。一旦你让它正常运行,请记录每一步细节。
9.3 网络设备
现在你了解了cache拦截的相关细节,让我们看看如何实际让它工作。我们先配置网络设备,它们用来拦截HTTP连接。
9.3.1 内置Squid
在该配置中,你无需交换或网络路由设备来拦截HTTP连接。代替的,squid运行的Unix系统,也就是路由器(或网桥),请见图9-2。;
该配置本质上跳过了9.1章的头三步。squid主机充当网络路由器,它接受HTTP连接包。假如你采用此方法,请直接跳到9.4章。
9.3.2 四层交换
许多单位使用四层交换机来支持HTTP拦截。这些产品提供更多的功能,例如健壮性检测和负载均衡。我在这里仅仅讲讲拦截。关于健壮性检测和负载均衡的信息,请见O'Reilly's Server Load Balancing and Load Balancing Servers, Firewalls, and Caches (John Wiley & Sons). 下面的章节包含了许多产品和技术的示例配置。
9.3.2.1 Alteon/Nortel
下面的配置来自ACEswitch 180和Alteon's WebOS 8.0.21。网络设置请见图9-4。;
客户端连接到端口1,通过端口2连接到因特网,squid运行在端口3。下面的行是交换机的/cfg/dump命令的输出。你无须敲入所有这些行。甚至,在Alteon的新版软件里,某些命令可能改变了。注意Alteon把这个功能叫做Web Cache重定向(WCR)。如下是处理步骤:
1.首先,你必须分配给Alteon交换机一个IP地址。这是必要的,以便交换机能检查squid的存活状态。
/cfg/ip/if 1 ena addr 172.16.102.1 mask 255.255.255.0 broad 172.16.102.255
2.Alteon的WCR属于服务负载均衡(SLB)配置。所以,必须使用如下命令在交换机上激活SLB功能:
/cfg/slb on
3. 现在,用squid的IP地址定义real server:
/cfg/slb/real 1 ena rip 172.16.102.66
4. 必须定义一个组,并分配给real server一个组号:
/cfg/slb/group 1 health tcp add 1
5. 下一步定义2个过滤规则。第1条规则匹配HTTP连接(目的端口是80的TCP包),并重定向它们到组1里的server。第2条规则匹配所有其他数据包,并正常转发它们。
/cfg/slb/filt 1 ena action redir sip any smask 0.0.0.0 dip any dmask 0.0.0.0 proto tcp sport any dport http group 1 rport 0
/cfg/slb/filt 224 ena action allow sip any smask 0.0.0.0 dip any dmask 0.0.0.0 proto any
6. 最后一步是给SLB配置指定的交换端口。在端口1上,处理客户端连接(这也是客户端连接的端口),并增加2条过滤规则。在端口2上,仅须配置它正常服务(例如,向上连接到Internet):
cfg/slb/port 1 client ena filt ena add 1 add 224
/cfg/slb/port 2 server ena
为了验证HTTP拦截配置正确并工作良好,你可以使用/stats/slb和/info/slb菜单里的命令。/info/slb/dump是快速有效的查看整个SLB配置的方法:
>;>; Main# /info/slb/dump
Real server state: 1: 172.16.102.66, 00:c0:4f:23:d7:05, vlan 1, port 3, health 3, up
Virtual server state:
Redirect filter state: 1: dport http, rport 0, group 1, health tcp, backup none real servers: 1: 172.16.102.66, backup none, up
Port state: 1: 0.0.0.0, client filt enabled, filters: 1 224 2: 0.0.0.0, server filt disabled, filters: empty 3: 0.0.0.0 filt disabled, filters: empty
在该输出里,注意到交换机显示Squid在端口3上可到达,并且运行正常。你也能见到过滤规则1应用到端口1。在端口状态节里,端口1定义为客户端连接端口,端口2简单的标记为服务端口。
/stats/slb/real命令显示real server(squid)的有用统计:
>;>; Main# /stats/slb/real 1 ------------------------------------------------------------------
Real server 1 stats: Health check failures: 0 Current sessions: 41 Total sessions: 760 Highest sessions: 55 Octets: 0
大部分统计与任务(例如TCP连接)数量相关。假如再次运行该命令,总共的任务计数会增加。
最后,/stats/slb/group命令显示几乎同样的信息:
>;>; Main# /stats/slb/group 1 ------------------------------------------------------------------
Real server group 1 stats:
Current Total Highest Real IP address Sessions Sessions Sessions Octets ---- --------------- -------- ---------- -------- --------------- 1 172.16.102.66 65 2004 90 0 ---- --------------- -------- ---------- -------- --------------- 65 2004 90 0
假如不止1个real server在组里,该输出会更有趣。
9.3.2.2 Foundry
下面的配置示例来自ServerIron XL,运行的软件版本是07.0.07T12。跟前面一样,客户端在端口1,Internet连接在端口2,squid运行在端口3。然而,这样的配置少了点东西,因为这里可以激活HTTP全局拦截。Foundry的cache拦截的名字叫做Transparent Cache Switching(TCS)。请参考图9-4。;
首先请给交换机分配1个IP地址,以便执行健壮性检测: ip address 172.16.102.1 255.255.255.0
Foundry允许你在特定端口上激活或禁用TCS。然而简单起见,这里全局激活它: ip policy 1 cache tcp http global
在该行里,cache是针对TCS功能的关键字。下1行定义web cache,我定义其名字为squid1,并且告诉交换机它的IP地址: server cache-name squid1 172.16.102.66
最后的步骤是将web cache加进cache组里: server cache-group 1 cache-name squid1
假如在转发连接时有问题,请参阅show cache-group命令的输出:
ServerIron#show cache-group
Cache-group 1 has 1 members Admin-status = Enabled Active = 0
Hash_info: Dest_mask = 255.255.255.0 Src_mask = 0.0.0.0
Cache Server Name Admin-status Hash-distribution squid1 6 3
HTTP Traffic From <->; to Web-Caches
Name: squid1 IP: 172.16.102.66 State: 6 Groups = 1
Host->;Web-cache Web-cache->;Host State CurConn TotConn Packets Octets Packets Octets Client active 441 12390 188871 15976623 156962 154750098 Web-Server active 193 11664 150722 151828731 175796 15853612 Total 634 24054 339593 167805354 332758 170603710
某些输出有些模糊,但通过重复该命令,并且观察计数器的增长,你能了解拦截是否在进行。
show server real提供几乎同样的信息:
ServerIron#show server real squid1
Real Servers Info
Name : squid1 Mac-addr: 00c0.4f23.d705
IP:172.16.102.66 Range:1 State:Active Wt:1 Max-conn:1000000
Src-nat (cfgp)offff) Dest-nat (cfgp)offff)
squid1 is a TRANSPARENT CACHE in groups 1
Remote server : No Dynamic : No Server-resets:0
Mem:server: 02009eae Mem:mac: 045a3714
Port State Ms CurConn TotConn Rx-pkts Tx-pkts Rx-octet Tx-octet Reas
---- ----- -- ------- ------- ------- ------- -------- -------- ---- http active 0 855 29557 379793 471713 373508204 39425322 0
default active 0 627 28335 425106 366016 38408994 368496301 0
Server Total 1482 57892 804899 837729 411917198 407921623 0
最后,使用show logging命令来观察交换机是否显示squid正常或异常:
ServerIron#show logging ...
00d00h11m51s:N4 server 172.16.102.66 squid1 port 80 is up
00d00h11m49s:N4 server 172.16.102.66 squid1 port 80 is down
00d00h10m21s:N4 server 172.16.102.66 squid1 port 80 is up
00d00h10m21s:N4 server 172.16.102.66 squid1 is up
注意ServerIron认为服务运行在80端口。以后你会见到squid运行在3128端口的示例。包过滤规则实际上将包的目的地址从80改变为3128。这导致一些与状态检测有关的有趣结果,我在9.3.2.5节里会讲到。
9.3.2.3 Extreme Networks
在该示例里,硬件是Summit1i,软件版本是6.1.3b11。再次将客户端分配在端口1,Internet在端口2,squid在端口3。网络配置见图9-5。;
Extreme交换机仅仅对在不同子网间进行路由的数据包进行HTTP连接的拦截。换句话说,如果你配置Extreme交换机使用二层模式(单一VLAN里),就不能将包转发给squid。为了让HTTP拦截正常工作,必须给用户,Squid,和Internet配置不同的VLAN。
configure Default delete port 1-8
create vlan Users configure Users ip 172.16.102.1 255.255.255.192 configure Users add port 1
create vlan Internet configure Internet ip 172.16.102.129 255.255.255.192 configure Internet add port 2
create vlan Squid configure Squid ip 172.16.102.65 255.255.255.192 configure Squid add port 3
下一步是激活和配置交换机的路由:
enable ipforwarding configure iproute add default 172.16.102.130
最后,配置交换机重定向HTTP连接到Squid:
create flow-redirect http tcp destination any ip-port 80 source any configure http add next-hop 172.16.102.66 9.3.2.4 Cisco Arrowpoint
下类配置基于我以前的测试笔记。然而,最近我没有使用这类型的交换机,不能确保如下命令仍然正确:
circuit VLAN1 ip address 172.16.102.1 255.255.255.0
service pxy1 type transparent-cache ip address 172.16.102.66 port 80 protocol tcp active
owner foo content bar add service pxy1 protocol tcp port 80 active
9.3.2.5 关于HTTP服务和健壮性检测的评论
在上面的示例里,路由器/交换机都直接转发包,不会改变目的TCP端口。在9.4章里用到的包过滤规则改变了目的端口。如果试图在同一主机上运行HTTP服务和squid,那么就产生了一个有趣的问题。
为了在3128端口运行Squid的同时,还要在80端口运行HTTP,包过滤配置必须有1条特殊的规则,它接受到本机HTTP服务的TCP连接。否则,连接会直接转交给Squid。该规则易于建立。假如目的端口是80,并且目的地址是服务器的,那么主机正常接受这个包。然而所有的拦截包有外部目的地址,所以它们不会匹配该规则。
然而,当路由器/交换机进行HTTP健壮性检测时,它连接到服务器的IP地址。这样,健壮性检测的数据包匹配了上述规则,它不会转交给Squid。路由器/交换机检测了错误的服务。假如HTTP服务down掉了,而squid还在运行,那健壮性检测就产生错误结果。
解决这个问题的一些选择是: 1.不要在Squid主机上运行HTTP服务; 2.增加1条特殊的包过滤规则,将来自路由器/交换机的状态检测的包转交给squid; 3.配置路由器/交换机,改变目的端口为3128; 4.禁止4层状态检测。
9.3.3 Cisco策略路由
策略路由与4层交换并非不同。它在Cisco和其他公司的路由产品上执行。主要的区别是策略路由不包括任何健壮性检测。这样,假如squid超载或完全不可响应,路由器还会继续转发包到squid,而不是将包路由到原始服务器。策略路由要求squid位于路由器直接相连的子网中。
在本示例里,使用了Cisco 7204路由器,运行IOS Version 12.0(5)T。网络配置与前面的一样,见图9-5。;
首先的配置步骤是定义访问列表,匹配来自客户端的到80端口的数据包。必须确保Squid发起的到80端口的数据包不会被再次拦截。做到这点的方法之一是,定义1个特殊规则,拒绝来自squid的数据包,紧跟1条规则允许所有其他的数据包:
access-list 110 deny tcp host 172.16.102.66 any eq www access-list 110 permit tcp any any eq www
另外,如果Squid和用户位于不同的子网,你可以仅仅允许来自用户所在网络的数据包: access-list 110 permit tcp 10.102.0.0 0.0.255.255 any eq www
下一步是定义路由映射。在这里你告诉路由器转发拦截包到何处去:
route-map proxy-redirect permit 10 match ip address 110 set ip next-hop 172.16.102.66
这些命令表明,“假如IP地址匹配访问列表110,就转发该包到172.16.102.66”。在route-map行的数字10是一个序列号,假如你有多个路由映射的话。最后一步是应用路由映射到客户端连接的接口:
interface Ethernet0/0 ip policy route-map proxy-redirect
IOS不对策略路由提供很多调试方法。然而,show route-map命令足够可用:
router#show route-map proxy-redirect route-map proxy-redirect, permit, sequence 10
Match clauses: ip address (access-lists): 110 Set clauses: ip next-hop 172.16.102.66 Policy routing matches: 730 packets, 64649 bytes
9.3.4 Web Cache Coordination协议
Cisco对4层交换技术的响应叫做Web Cache Coordination Protocol(WCCP).WCCP在许多方面与典型的4层拦截不同。
首先,拦截包被封装在GRE(路由封装类)里。这点允许数据包跨子网传输,也就意味着squid不必直接连在路由器上。因为数据包是封装的,squid主机必须对其进行解包。并非所有的Unix系统有解开GRE包的代码。
第二个不同是,路由器如何决定将负载分摊到多个cache上。事实上,路由器不做这个决定,由cache来做。当路由器有一组支持WCCP的cache时,其中一个cache主动成为组的领导。由领导cache来决定如何分摊负载和通知路由器。在路由器重定向然任何连接之前,这是一个额外的必要步骤。
因为WCCP使用GRE,路由器可能强迫要求将来自HTTP请求的大TCP包分割成片断。幸运的是,这点不会经常发生,因为大部分HTTP请求比以太网MTU size(1500字节)要小。默认的TCP和IP包头部是20字节,这意味着以太网帧能实际携带1460字节的数据。GRE封装在GRE头部增加了20字节,另外在第二个IP头部增加了20字节。这样来自客户端的正常的1500字节的TCP/IP包,在封装后变成了1540字节。这样数据包就太大而不能在单个以太网帧里传输,所以路由器将原始包分割成两个片断。
9.3.4.1 WCCPv1
该节的配置示例在运行IOS Version 12.0(5)T的Cisco 7204路由器上测试。网络配置跟图9-5同。
首先,在IOS配置中输入如下两行,激活路由器的WCCP:
ip wccp version 1 ip wccp web-cache
接着,必须在单独的路由器接口上激活WCCP。在HTTP包离开路由器的接口上激活WCCP,也就是路由器连接到外部原始服务器或Internet网关的接口:
interface Ethernet0/1 ip address 172.16.102.129 255.255.255.192 ip wccp web-cache redirect out
请确认保存了配置改变。
你也许想使用访问列表来阻止某些web站点的拦截。可以使用访问列表来防止循环转发。例如:
! don't re-intercept connections coming from Squid: access-list 112 deny tcp host 172.16.102.66 any eq www
! don't intercept this broken web site access-list 112 deny tcp any 192.16.8.7 255.255.255.255 eq www
! allow other HTTP traffic access-list 110 permit tcp any any eq www
ip wccp web-cache redirect-list 112
路由器不发送任何数据到squid,直到squid宣称它自己是路由器。我在9.5.1节里解释如何配置squid的WCCP。
9.3.4.2 WCCPv2
注意从路由器发送到Squid的GRE包,包含了额外的4个字节。WCCPv2在GRE头部和封装的IP包之间,插入了一个重定向头部。也许需要修改内核代码来计算这个额外的头部。
9.3.4.3 调试
IOS提供许多命令来监视和调试WCCP。show ip wccp web-cache命令提供一些基本的信息:
router#show ip wccp web-cache
Global WCCP information: Router information: Router Identifier: 172.16.102.129 Protocol Version: 1.0 Service Identifier: web-cache Number of Cache Engines: 1 Number of routers: 1 Total Packets Redirected: 1424 Redirect access-list: -none- Total Packets Denied Redirect: 0 Total Packets Unassigned: 0 Group access-list: -none- Total Messages Denied to Group: 0 Total Authentication failures: 0
欲了解更多细节,在前叙命令后加一个detail单词:
router#show ip wccp web-cache detail
WCCP Cache-Engine information:
IP Address: 172.16.102.66 Protocol Version: 0.4 State: Usable Initial Hash Info: 00000000000000000000000000000000 00000000000000000000000000000000 Assigned Hash Info: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF Hash Allotment: 256 (100.00%) Packets Redirected: 1424 Connect Time: 00:17:40
这里可以看到squid的IP地址和状态。假如不止一个cache与路由器会话,那么hash分配信息看起来不同。大多数情况下,每个cache接受到相等的hash值。
注意第二条命令输出的协议版本值,与第一条命令的不一样。不幸的是,赋予了版本号太多的意义。show ip wccp web-cache命令看起来报告WCCP协议的主版本号(例如1或2),然而show ip wccp web-cache detail的版本号看起来匹配Squid的wccp_version指令的值。
9.4 操作系统配置
为了让cache拦截正常工作,必须在操作系统中激活某些网络功能。首先,必须激活IP包转发。这就允许操作系统接受目的地址是外部IP的数据包。接着,必须激活和配置内核中的相关代码,以重定向外部包到Squid。
9.4.1 Linux
本节的指导适合2.4系列Linux内核。我使用RedHat Linux7.2(内核是2.4.7-10)。假如你使用的版本不同,那可能不能运行。建议搜索下Squid的FAQ或其他地方,找到关于内核的更新的或历史的信息。
在我测试iptables过程中,不必激活IP转发。然而,你也可以试试在一开始就激活它,并在一切运行良好后,看看能否禁掉它。激活包转发的最好的方法是在/etc/sysctl.conf文件里增加如下行:
net.ipv4.ip_forward = 1
一般来说,在HTTP拦截生效前,不必编译新内核。假如你不知道如何配置和创建新Linux内核,请参阅O'Reilly's Running Linux by Matt Welsh, Matthias Kalle Dalheimer, and Lar Kaufman。当你配置内核时,请确认如下选项被激活:
o General setup Networking support (CONFIG_NET=y) Sysctl support (CONFIG_SYSCTL=y)
o Networking options Network packet filtering (CONFIG_NETFILTER=y) TCP/IP networking (CONFIG_INET=y) Netfilter Configuration Connection tracking (CONFIG_IP_NF_CONNTRACK=y) IP tables support (CONFIG_IP_NF_IPTABLES=y) Full NAT (CONFIG_IP_NF_NAT=y) REDIRECT target support (CONFIG_IP_NF_TARGET_REDIRECT=y)
o File systems /proc filesystem support (CONFIG_PROC_FS=y)
另外,请确认该选项没被激活:
o Networking options
Fast switching (CONFIG_NET_FASTROUTE=n)
重定向外部数据包到squid的代码是Netfilter软件的一部分。如下是发送HTTP拦截连接到squid的规则:
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 3128
Linux内核维护许多不同的tables。-t nat选项表明我们正在修改NAT(网络地址转换)表。本质上,我们使用iptables将原始服务器的TCP/IP地址转换为squid的本地TCP/IP地址。
每个iptables表有许多链。-A PREROUTING表明我们增加了一条规则到内建的链叫做PREROUTING。PREROUTING链仅对从外部网络进入系统的数据包有效。
接下来的三个选项决定哪个包匹配该规则。-i eth0选项限制规则仅对eth0接口上接受的数据包有效。-p tcp选项指定TCP包,--dport 80指定包的目的端口是80。假如这三个条件都是true,那么包匹配该规则。
-j REDIRECT选项表明对匹配规则的包,采取何种动作。REDIRECT是内建的动作名,它导致iptables改变包的目的地址为127.0.0.1。--to-port 3128选项也指示iptables改变目的TCP端口为3128。
假如你在squid主机上运行HTTP服务(例如Apache),就必须增加另外的iptables规则。该必要规则允许连接到HTTP服务。否则,REDIRECT规则导致iptables转发连接到squid的3128端口。可以使用-I选项在列表顶部插入一条新规则:
iptables -t nat -I PREROUTING -i eth0 -p tcp -d 172.16.102.66 --dport 80 -j ACCEPT
一旦确认所有iptables规则工作正确,记得运行如下命令来保存配置:
/sbin/service iptables save
将当前规则保存到/etc/sysconfig/iptables,当系统重启时,这些规则自动载入。
9.4.1.1 Linux和WCCP
2.4版本的Linux内核自带1个GRE伪装接口。然而,它不能解码WCCP任务里封装的GRE包。问题看起来在于路由器设置了WCCP/GRE包的协议类型域为0x883E。Linux的GRE驱动不知道如何处理这类型包,因为它不了解0x883E类型的协议。
可以试试给Linux打GRE模块的补丁,以便它能在WCCP下工作。Squid FAQ包含了对这个补丁的链接。然而,假如使用WCCP指定的Linux模块,事情会容易些。可以在这里找到它:
必须编译ip_wccp.c文件为可装载内核模块。有点棘手的是,依赖于内核版本的不同,编译选项可能不同。你可以进入内核源代码目录,敲入make modules并观察编译器命令的滚动。然后拷贝这些命令中的一个,然后改变最后一个参数为ip_wccp.c。如下是我在2.4.7-10 Linux内核中使用的命令:
% gcc -Wall -D_ _KERNEL_ _ -I/usr/src/linux-2.4.7-10/include \ -DMODULE -DMODVERSIONS -DEXPORT_SYMBAB \ -include /usr/src/linux-2.4.7-10/include/linux/modversions.h \ -O2 -c ip_wccp.c
gcc命令在当前目录会生成ip_wccp.o文件。下一步使用insmod命令,装载模块到内核中:
# insmod ip_wccp.o
注意ip_wccp模块接受来自任何源地址的GRE/WCCP包。换句话说,恶意用户可能发送数据到squid cache。假如使用该模块,应该安装一条iptables规则,拒绝外部的GRE包。例如:
# iptables -A INPUT -p gre -s 172.16.102.65 -j ACCEPT # iptables -A INPUT -p gre -j DROP
不要忘记敲入/sbin/service iptables save命令来保存配置。
9.4.2 FreeBSD
本节的例子基于FreeBSD-4.8,并可以在任何FreeBSD-4和5系列的后续版本上运行。
要激活IP包转发,在/etc/sysctl.conf中增加如下行:
net.inet.ip.forwarding=1
需要在内核中激活2个特殊选项。假如你不知道如何编译内核,参见FreeBSD Handbook第9章(). 编辑内核配置文件,确保有如下行:
options IPFIREWALL options IPFIREWALL_FORWARD
假如squid主机位于无人照看的机房中,我也推荐使用IPFIREWALL_DEFAULT_TO_ACCEPT选项。假如你被防火墙的规则困扰,仍然可以登陆系统中。
ipfw命令告诉内核重定向拦截连接到squid:
/sbin/ipfw add allow tcp from 172.16.102.66 to any out /sbin/ipfw add allow tcp from any 80 to any out /sbin/ipfw add fwd 127.0.0.1,3128 tcp from any to any 80 in /sbin/ipfw add allow tcp from any 80 to 172.16.102.66 in
第一条规则匹配squid主机发起的数据包。它确保外出的TCP连接不会被重新定向回squid。第二条规则匹配squid响应客户端的数据包。我在这里列出它,因为随后会有另外的ipfw规则,这些规则会拒绝这些包。第三条规则实际重定向进来的连接到squid。第四条规则匹配从原始服务器返回squid的数据包。这里又一次假设随后会有相应的拒绝规则。
假如在squid主机上运行HTTP服务,就必须增加另外的规则,它放过,而不是重定向,目的地址是原始服务器的TCP包。下列规则在fwd规则之前:
/sbin/ipfw add allow tcp from any to 172.16.102.66 80 in
FreeBSD典型的将ipfw规则存储在/etc/rc.firewall里。一旦你确认规则设置正确,记得保存它们。将如下行加入/etc/rc.local文件,让FreeBSD在启动时自动运行/etc/rc.firewall脚本。
firewall_enable="YES"
9.4.2.1 FreeBSD和WCCP
FreeBSD版本4.8和后续版本内建了对GRE和WCCP的支持。早期的版本需要补丁,你可以在这里找到: . 内建代码的性能非常好,它是真正的内核组织编写的。可能也需要编译支持GRE的新内核。将如下行加入内核配置文件里:
pseudo-device gre
对Freebsd-5,使用device代替了pseudo-device。当然,你也需要前面章节里提到的FIREWALL选项。
在安装和重启了新内核后,必须配置GRE通道来接受来自路由器的GRE包。例如:
# ifconfig gre0 create # ifconfig gre0 172.16.102.66 172.16.102.65 netmask 255.255.255.255 up # ifconfig gre0 tunnel 172.16.102.66 172.16.102.65 # route delete 172.16.102.65
ifconfig命令在gre0接口上,增加了一个到路由器(172.16.102.65)的路由表入口。我发现必须删除该路由,以便squid能与其他路由器会话。
你也许想或必须对来自路由器的GRE包,增加一条ipfw规则:
/sbin/ipfw add allow gre from 172.16.102.65 to 172.16.102.66
9.4.3 OpenBSD
本节的示例基于OpenBSD 3.3
为了激活包转发,在/etc/sysctl.conf文件里增加该行:
net.inet.ip.forwarding=1
现在,在/etc/pf.conf文件里增加如下类似行,配置包过滤规则:
rdr inet proto tcp from any to any port = www ->; 127.0.0.1 port 3128 pass out proto tcp from 172.16.102.66 to any pass out proto tcp from any port = 80 to any pass in proto tcp from any port = 80 to 172.16.102.66
假如你没有使用OpenBSD的包过滤器,需要在/etc/rc.conf.local文件里增加一行来激活它:
pf=YES 9.4.3.1 OpenBSD和WCCP 首先,增加如下行到/etc/sysctl.conf文件,告诉系统接受和处理GRE和WCCP包:
net.inet.gre.allow=1 net.inet.gre.wccp=1
然后,用如下命令配置GRE接口:
# ifconfig gre0 172.16.102.66 172.16.102.65 netmask 255.255.255.255 up # ifconfig gre0 tunnel 172.16.102.66 172.16.102.65 # route delete 172.16.102.65
跟Freebsd一样,我发现必须删除ifconfig自动产生的路由。最后,依赖于包过滤器的配置,必须增加一条规则以允许GRE包:
pass in proto gre from 172.16.102.65 to 172.16.102.66
9.4.4 在NetBSD和其他系统上的IPFilter
本节的示例基于NetBSD 1.6.1。它们也能运行在Solaris,HP-UX,IRIX,和Tru64上,既然这些系统本身就配备了IPFilter.
激活NetBSD的包转发,将如下行加进/etc/sysctl.conf: net.inet.ip.forwarding=1
然后,将如下行插入NAT配置文件/etc/ipnat.conf中: rdr fxp0 0/0 port 80 ->; 172.16.102.66 port 3128 tcp
你的接口名可能与本例的fxp0不同。
9.4.4.1 NetBSD和WCCP
我不能在NetBSD上运行WCCP,即使打了GRE补丁来接受WCCP包。该问题看起来根源在IPFilter rdr规则阻塞了特定的端口。来自路由器的包通过NetBSD的gre0接口(在这里它们被解包)。然而,包回到路由器时,走另一条通道,未被封装并且不走同一网络接口。这样,IPFilter代码没有将squid的本地IP地址转换回原始服务器的地址。 9.5 配置Squid 假如你使用Linux 2.4和iptables,在运行./configure时,可使用--enable-linux-netfilter选项。它激活某些Linux的特殊代码,以发现发起请求的原始服务器的IP地址。Squid正常情况下从Host头部,得到原始服务器的名字(和/或地址)。--enable-linux-netfilter功能仅对没有Host头部的请求来说是必要的。统计显示几乎所有的请求有Host头部,所以实际中可以不使用--enable-linux-netfilter选项。
假如正在使用IPFilter包(NetBSD,Solaris,或其他),因为同样的理由,你应该使用--enable-ipf-transparent选项。在OpenBSD上,请使用--enable-pf-transparent选项。每次运行./configure时,必须重编译squid,见3.8章的描述。
在运行了./configure和重编译了squid后,可以编辑squid.conf文件。作为起点,请确认下列指令定义了给定的值:
httpd_accel_host virtual httpd_accel_port 80 httpd_accel_uses_host_header on httpd_accel_with_proxy on httpd_accel_single_host off
http_accel_host指令是关键。它指示squid接受包含局部URI的HTTP请求。httpd_accel_uses_host_header被激活,允许squid使用Host头部来重新构建完整URI。virtual关键字指示squid在缺乏Host头部时,将原始服务器的IP地址放进URL里。
httpd_accel_with_proxy指令控制squid是否既接受HTTP服务(部分URI)请求,又接受代理(完整URI)请求。在cache拦截里,它应该被激活。如果没有用户明确的配置使用squid做代理,那即使httpd_accel_with_proxy没被激活,squid也能工作。
httpd_acces_single_host指令正常情况下被禁止,在早期版本的squid里,它可能被默认激活。在cache拦截里,它应明确被禁止。
假如拦截不止针对80端口,你也许该将httpd_accel_port设为0。见附录A的更多信息。
假如你没有使用WCCP,就该准备开始发起拦截会话到squid了。通过使用浏览器来上网冲浪,或者使用squidclient发起测试请求,就可以放手一试。假如你使用WCCP,那么还有许多步骤要完成。
9.5.1 配置WCCPv1
路由器不发送任何会话到squid,直到squid宣称它自己是路由器。为了让squid那样做,在squid.conf中增加如下行:
wccp_router 172.16.102.65 wccp_version 4
路由器有多个接口。请确认使用与squid相连的接口的IP地址。这点是必要的,因为来自路由器的WCCP消息,将源IP地址设置为外出接口的地址。假如源地址不匹配wccp_router值,squid会拒绝WCCP消息。
WCCPv1文档规定4作为协议版本号。然而,某些用户报告,Cisco IOS 11.2仅支持协议版本3。假如你使用该版本的IOS,请在squid.conf里改变版本号:
wccp_version 3
9.6 调试问题
HTTP拦截比较复杂,因为许多不同设备必须组合正确工作。为了帮助你跟踪问题,如下是一个问题解答检查列表:
客户端数据包正在通过路由器/交换机吗? 在简单网络里,这点显而易见。你可以trace线缆并观察指示灯的活动闪烁。然而在大而复杂的网络,数据包可能走不同的路线。假如你的组织够大,并有网络sniffer设备,就可以观察线路中web客户端的请求数据包。低技术的方法是,断开有问题的线路,并观察是否影响客户端的web浏览。
路由器/交换机配置是否正确? 你也许要再次检查路由器/交换机配置。假如你已配置了某个接口,那能否确保它正确呢? 是否新的配置真正在设备上运行?也许在你保存配置之前,路由器/交换机已重启了。在改变生效前,你或许需要reboot设备。
交换机/路由器能与squid主机会话吗? 能从路由器/交换机上ping通squid吗?大部分4层拦截配置要求网络设备和squid在同一子网里。登陆路由器/交换机,确认能ping通squid的IP地址。
交换机/路由器相信squid在运行吗? 许多传输拦截设备不会发送会话到squid,除非它们知道squid是健壮的。使用调试命令来预览squid的健壮性状态。也许会发现三层健壮性检测(例如ICMP ping)比四层检测(例如HTTP)更容易,它使网络设备更容易将squid标记为存活状态。
Squid实际在运行吗? 请再次确认squid真正在运行,特别是在系统近期重启过的情况下。
数据包正在抵达squid主机吗? 使用tcpdump能见到拦截的TCP连接。如下是示例: # tcpdump -n -i eth0 port 80
假如使用WCCP,请检查来自路由器的GRE包: # tcpdump -n -i eth0 ip proto gre
假如没有看到tcpdump的任何输出,则路由器/交换机可能没有发送任何数据。在这种情况下,返回到以前的建议。
注意,假如设备正使用四层健壮性检测,你可以在tcpdump的输出里见到这些。健壮性检测来自路由器/交换机的IP地址,所以它们容易被认出。假如你见到健壮性检测,但没有其他数据,那可能意味着路由器/交换机正把squid的响应理解为不健壮。例如,设备可能想见到200(OK)响应,但squid返回一个错误,例如401(未授权)或404(未发现)。请对access.log运行tail -f命令。
激活了IP转发吗? 请再次确认squid运行的操作系统配置了IP包转发。假如没有,主机可能会丢弃拦截数据包,因为目的IP地址并非本地。
配置包过滤了吗? 请确认包过滤器(例如ipfw,iptables,pf等)配置正确。当每件事都运行良好时,你能定期运行命令来显示过滤规则,并观察计数器增长。例如:
# ipfw show 300 ; sleep 3; ipfw show 300 00300 86216 8480458 fwd 127.0.0.1,3128 tcp from any to any 80 in 00300 86241 8482240 fwd 127.0.0.1,3128 tcp from any to any 80 in
注意该示例在FreeBSD上,包和字节计数器(第二和第三列)正在增长。
环路接口起来和配置了吗? 假如有一条规则转发/重定向包到127.0.0.1,请确认环路接口(例如lo0,lo等)起来了,并配置过它。假如没有,内核简单的跳过这条转发/重定向规则。
WCCP/GRE包被正确解开了吗? 假如使用WCCP,请确认GRE包被正确解开。假如因为某些理由,系统不知道该如何处理GRE包,那就在netstat -s的输出里会见到"unknown/unsupported protocol"计数器在增长。
# netstat -s | grep unknown
46 packets for unknown/unsupported protocol
假如OS有GRE接口,请频繁运行netstat -i命令,观察不断增长的包数量:
# netstat -in | grep ^gre0
Name Mtu Network Address Ipkts Ierrs Opkts Oerrs Coll gre0 1476 ; 304452 0 0 4 0
另外,在GRE接口上运行tcpdump: # tcpdump -n -i gre0
Squid能响应客户端吗? 有可能路由器/交换机能发送包到squid,但squid不能将包发送回客户端。这种情况可能发生在:防火墙过滤规则拒绝外出数据包,或squid没有到客户端IP地址的路由。为了检查这种情况,请运行netstat -n并观察SYN_RCVD状态的sockets:
% netstat -n
Active Internet connections
Proto Recv-Q Send-Q Local Address Foreign Address (state)
tcp4 0 0 10.102.129.246.80 10.102.0.1.36260 SYN_RCVD tcp4 0 0 10.102.129.226.80 10.102.0.1.36259 SYN_RCVD tcp4 0 0 10.102.128.147.80 10.102.0.1.36258 SYN_RCVD tcp4 0 0 10.102.129.26.80 10.102.0.2.36257 SYN_RCVD tcp4 0 0 10.102.129.29.80 10.102.0.2.36255 SYN_RCVD tcp4 0 0 10.102.129.226.80 10.102.0.1.36254 SYN_RCVD tcp4 0 0 10.102.128.117.80 10.102.0.1.36253 SYN_RCVD tcp4 0 0 10.102.128.149.80 10.102.0.1.36252 SYN_RCVD
假如你看到这些,请使用ping和traceroute来确认squid能与客户端双向通信。
Squid能与原始服务器会话吗? 假如squid不能连接到原始服务器,拦截HTTP连接会无法进行。如果这点发生,netstat会显示许多连接在SYN_SENT状态:
% netstat -n
Active Internet connections
Proto Recv-Q Send-Q Local Address Foreign Address (state)
tcp4 0 0 172.16.102.66.5217 10.102.129.145.80 SYN_SENT tcp4 0 0 172.16.102.66.5216 10.102.129.224.80 SYN_SENT tcp4 0 0 172.16.102.66.5215 10.102.128.71.80 SYN_SENT tcp4 0 0 172.16.102.66.5214 10.102.129.209.80 SYN_SENT tcp4 0 0 172.16.102.66.5213 10.102.129.62.80 SYN_SENT tcp4 0 0 172.16.102.66.5212 10.102.129.160.80 SYN_SENT tcp4 0 0 172.16.102.66.5211 10.102.128.129.80 SYN_SENT tcp4 0 0 172.16.102.66.5210 10.102.129.44.80 SYN_SENT tcp4 0 0 172.16.102.66.5209 10.102.128.73.80 SYN_SENT tcp4 0 0 172.16.102.66.5208 10.102.128.43.80 SYN_SENT
再次用ping和traceroute来确认squid能与原始服务器会话。
外出连接正被拦截吗? 假如squid能ping通原始服务器,并且仍然见到大量的连接在SYN_SENT状态,那么路由器/交换机可能正在拦截squid的外出TCP连接。在某些情况下,squid能检测到这种转发循环,并写警告日志到cache.log。如此的转发死循环能迅速耗光squid的所有文件描述符,这样也会在cache.log里产生警告。
假如你怀疑这个问题,请使用squidclient程序来发起一些简单的HTTP请求。例如,该命令发起一条直接到原始服务器的HTTP请求: % /usr/local/squid/bin/squidclient -p 80 -h slashdot.org /
假如该命令成功,你可以在屏幕上见到来自Slashdot站点的许多难看的HTML。然后通过squid来试试同样的请求: % /usr/local/squid/bin/squidclient -r -p 3128 -h 127.0.0.1
假如没有在cache.log里看到错误消息,就可以再次在屏幕上看到一些HTML。假如看到转发循环错误,就必须重新配置交换机/路由器,以便它允许squid的外出连接正常通过,而不被拦截。