defaults:
bmo_slim_image: &bmo_slim_image
- image: mozillabteam/bmo-slim:20170818.1
+ image: mozillabteam/bmo-slim:20170824.1
user: app
mysql_image: &mysql_image
use Bugzilla::User;
use Bugzilla::Util;
use Bugzilla::CPAN;
+use Bugzilla::Bloomfilter;
use Bugzilla::Metrics::Collector;
use Bugzilla::Metrics::Template;
}
sub check_rate_limit {
- my ($class, $name, $id) = @_;
+ my ($class, $name, $ip) = @_;
my $params = Bugzilla->params;
if ($params->{rate_limit_active}) {
my $rules = decode_json($params->{rate_limit_rules});
warn "no rules for $name!";
return 0;
}
- if (Bugzilla->memcached->should_rate_limit("$name:$id", @$limit)) {
- Bugzilla->audit("[rate_limit] $id exceeds rate limit $name: " . join("/", @$limit));
- ThrowUserError("rate_limit");
+ if (Bugzilla->memcached->should_rate_limit("$name:$ip", @$limit)) {
+ my $action = 'block';
+ my $filter = Bugzilla::Bloomfilter->lookup("rate_limit_whitelist");
+ if ($filter && $filter->test($ip)) {
+ $action = 'ignore';
+ }
+ my $limit = join("/", @$limit);
+ Bugzilla->audit("[rate_limit] action=$action, ip=$ip, limit=$limit");
+ ThrowUserError("rate_limit") if $action eq 'block';
}
}
}
--- /dev/null
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Bugzilla::Bloomfilter;
+
+use 5.10.1;
+use strict;
+use warnings;
+
+use Bugzilla::Constants;
+use Algorithm::BloomFilter;
+use File::Temp qw(tempfile);
+
+sub _new_bloom_filter {
+ my ($n) = @_;
+ my $p = 0.01;
+ my $m = $n * abs(log $p) / log(2) ** 2;
+ my $k = $m / $n * log(2);
+ return Algorithm::BloomFilter->new($m, $k);
+}
+
+sub _filename {
+ my ($name) = @_;
+
+ my $datadir = bz_locations->{datadir};
+ return sprintf("%s/%s.bloom", $datadir, $name);
+}
+
+sub populate {
+ my ($class, $name, $items) = @_;
+ my $memcached = Bugzilla->memcached;
+
+ my $filter = _new_bloom_filter(@$items + 0);
+ foreach my $item (@$items) {
+ $filter->add($item);
+ }
+
+ my ($fh, $filename) = tempfile( "${name}XXXXXX", DIR => bz_locations->{datadir}, UNLINK => 0);
+ binmode $fh, ':bytes';
+ print $fh $filter->serialize;
+ close $fh;
+ rename($filename, _filename($name)) or die "failed to rename $filename: $!";
+ $memcached->clear_bloomfilter({name => $name});
+}
+
+sub lookup {
+ my ($class, $name) = @_;
+ my $memcached = Bugzilla->memcached;
+ my $filename = _filename($name);
+ my $filter_data = $memcached->get_bloomfilter( { name => $name } );
+
+ if (!$filter_data && -f $filename) {
+ open my $fh, '<:bytes', $filename;
+ local $/ = undef;
+ $filter_data = <$fh>;
+ close $fh;
+ $memcached->set_bloomfilter({ name => $name, filter => $filter_data });
+ }
+
+ return Algorithm::BloomFilter->deserialize($filter_data);
+}
+
+1;
}
}
+sub set_bloomfilter {
+ my ($self, $args) = @_;
+ return unless $self->{memcached};
+ if (exists $args->{name}) {
+ return $self->_set($self->_bloomfilter_prefix . '.' . $args->{name}, $args->{filter});
+ }
+ else {
+ ThrowCodeError('params_required', { function => "Bugzilla::Memcached::set_bloomfilter",
+ params => [ 'name' ] });
+ }
+}
+
+sub get_bloomfilter {
+ my ($self, $args) = @_;
+ return unless $self->{memcached};
+ if (exists $args->{name}) {
+ return $self->_get($self->_bloomfilter_prefix . '.' . $args->{name});
+ }
+ else {
+ ThrowCodeError('params_required', { function => "Bugzilla::Memcached::set_bloomfilter",
+ params => [ 'name' ] });
+ }
+}
+
+sub clear_bloomfilter {
+ my ($self, $args) = @_;
+ return unless $self->{memcached};
+ if ($args && exists $args->{name}) {
+ $self->_delete($self->_config_prefix . '.' . $args->{name});
+ }
+ else {
+ $self->_inc_prefix("bloomfilter");
+ }
+}
+
sub clear {
my ($self, $args) = @_;
return unless $self->{memcached};
return $_[0]->_prefix("config");
}
+sub _bloomfilter_prefix {
+ return $_[0]->_prefix("bloomfilter");
+}
+
sub _encode_key {
my ($self, $key) = @_;
$key = $self->_global_prefix . '.' . uri_escape_utf8($key);
-FROM mozillabteam/bmo-slim:latest
+FROM mozillabteam/bmo-slim:20170824.1
MAINTAINER Dylan William Hardison <dylan@mozilla.com>
ENV BUNDLE=https://s3.amazonaws.com/moz-devservices-bmocartons/bmo/vendor.tar.gz
# PREREQ_PM
my %requires = (
+ 'Algorithm::BloomFilter' => 0,
'CGI' => '<= 3.63',
'CPAN::Meta::Prereqs' => '2.132830',
'CPAN::Meta::Requirements' => '2.121',
--- /dev/null
+#!/usr/bin/perl -w
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+use strict;
+use warnings;
+use lib qw(. lib local/lib/perl5);
+
+use Bugzilla;
+use Bugzilla::Constants;
+use Bugzilla::Bloomfilter;
+
+# set Bugzilla usage mode to USAGE_MODE_CMDLINE
+Bugzilla->usage_mode(USAGE_MODE_CMDLINE);
+
+my $name = shift @ARGV or die "usage: $0 \$name < list\n";
+my @lines = <STDIN>;
+chomp @lines;
+Bugzilla::Bloomfilter->populate($name, \@lines);
+