Squid - 每个域的设置命中

Squid - Setup hits for each domain

我是 squid 的新手,我想知道当用户使用代理时,squid 中是否有一个选项可以设置每个域每天的点击次数。例如,我想将 100 次点击设置为站点 http://example.com 的限制,当超过限制时,我们需要阻止该域。

任何建议都是可取的。

简短的回答是否定的,Squid 没有选项来限制对域的点击次数。不过,Squid 确实能够启动自定义脚本并在 stdin/stdout 上连接到它们,将数据发送到脚本并使用其响应来控制它是允许还是拒绝请求。您可以使用该功能来解决此问题。我能想到的两种方法:

方案1:写一个helper来接受被访问的域,并维护它看到的所有域的计数和每个域被访问的次数,然后 return 对 Squid 的响应指示域是否是 over/under 阈值,Squid 使用它来 allow/deny 请求。这种方法的优点是所有逻辑都包含在一个脚本中。缺点是 squid 会动态启动多个 helpers 以响应负载,这意味着您需要想出一种方法让脚本的每个实例共享状态,并且 shutdown/startup Squid 进程。

选项 2: 将上述逻辑拆分为两个脚本,一个用于查看日志文件并记录每个域被访问的次数,第二个脚本用于Squid 可以作为外部助手启动,查询由第一个脚本维护的计数和 return 对 Squid 的响应,指示域是超过还是低于阈值。这种方法的优点是它可以横向扩展以支持多个助手,并且还可以在 reload/restart 的 Squid 进程中存活。

首先,您的 Squid 配置:

logformat domainonly %>rd
access_log daemon:/tmp/domains.log logformat=domainonly
external_acl_type domaincheck concurrency=0 %>rd /tmp/domaincheck.pl
acl overlimit-domains external domaincheck
http_access deny overlimit-domains

二、看日志的脚本。我建议你把它放在你的 crontab 中,并以一个平衡你的系统负载的时间间隔触发它,与你愿意在计数文件中显示命中并被 Squid 阻止之前容忍多少超限访问。你还应该确保 squid 每天轮换你的日志一次,并设置一个单独的脚本到 运行 在午夜清除 $basedir 中的所有文件,将计数文件清零以准备第二天。

#!/usr/bin/perl
# File:/tmp/domainwatch.pl
use strict;
use Data::Dumper;
my $basedir = '/tmp/domaincntrs';
my $ptrfile = $basedir."/logpos.txt";
my $logfile = '/tmp/domains.log';
my $logpos = 0;

# Get last log position from pointerfile, detect if it has wrapped
if (open(INFILE, "<$ptrfile")) {
    $logpos = <INFILE>;
    close(INFILE);
    $logpos = 0 if ($logpos > (-s $logfile));
}

# Open logfile, seek and begin reading
my %domainhash;
if (open(LOGFILE, "<$logfile")) {
    seek (LOGFILE, $logpos, 0);
    while (my $domain = <LOGFILE>) {
        chomp($domain);
        $domainhash{$domain} = $domainhash{$domain} + 1;
    }
    $logpos = tell(LOGFILE);
    close(LOGFILE);
} else {
    print "could not open logfile $logfile: $!\n";
}

# Iterate over entries learned from log and increment counter files for each domain
foreach my $domain (keys(%domainhash)) {
    my $cntr = 0;
    # Get current counter
    if (open(CNTRFILE, "<".$basedir."/".$domain)) {
        $cntr = <CNTRFILE>;
        close(CNTRFILE);
    }
    # Write new counter
    if (open(CNTRFILE, ">".$basedir."/".$domain)) {
        print CNTRFILE ($cntr + $domainhash{$domain});
        close(CNTRFILE);
    }   
}

# Write current log position back to pointer file
if (open (PTRFILE, ">$ptrfile")) {
    print PTRFILE $logpos;
    close PTRFILE;
} else {
    print "could not write to pointerfile $ptrfile: $!\n";
}

最后,Squid 可以用来做出策略决定的帮助脚本:

#!/usr/bin/perl
# File:/tmp/domaincheck.pl
use strict;

# Enable autoflush
$|=1;
my $basedir = '/tmp/domaincntrs';

# Set up infinite loop
while (my $line = <STDIN>) {
    my ($domain, $limit, $rest) = split(/\s+/, $line, 3);
    chomp($line);
    $limit = 100 if (!int($limit));
    my $cntr = 0;
    my $resp = '';
    if (open(INFILE, $basedir."/".$domain)) {
        $cntr = <INFILE>;
        close(INFILE);
    }
    chomp($cntr);
    $resp = ($cntr > $limit) ? 'OK' : 'ERR';
    if (open(LOGFILE, ">>/tmp/domaincheck.log")) {
        print LOGFILE "domain=$domain limit=$limit cntr=$cntr resp=$resp\n";
        close(LOGFILE);
    }
    print "$resp\n";
}

一些调整技巧:您将在 domains.log 中获得很多您可能意想不到的访问结果,例如。即使对于被拒绝的条目。您应该考虑设置一个 ACL 来定义成功的请求(例如,HTTP 结果代码 200-299),然后将该 ACL 应用于 access_log 语句,以控制写入该文件的内容。

domaincheck.pl 脚本的默认限制为 100,但会接受从 Squid 配置文件传递给它的变量限制。您可以使用它在您的 squid.conf 中指定该 ACL 的多次调用,例如:

# Define our busy and quiet domains
acl busy-domains dstdomain .google.com .microsoft.com
acl quiet-domains dstdomain .centos.org .adobe.com
# Define some busy and quiet limits
acl busy-domains-limit external domaincheck 750
acl quiet-domains-limit external domaincheck 100
# Combine the domains and limits ACLs into policy rules to deny access when both conditions are true
http_access deny busy-domains busy-domains-limit
http_access deny quiet-domains quiet-domains-limit

根据需要对 domain/limit 个阈值重复此操作。