FEATURE: rolls up 1.2.*.* IP ranges when number of entries > 10

This commit is contained in:
Régis Hanol 2014-11-27 19:29:30 +01:00
parent 062e954122
commit 5b90ceb71d
5 changed files with 99 additions and 17 deletions

View file

@ -21,8 +21,16 @@ export default Ember.ArrayController.extend(Discourse.Presence, {
return bootbox.confirm(I18n.t("admin.logs.screened_ips.roll_up_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function (confirmed) { return bootbox.confirm(I18n.t("admin.logs.screened_ips.roll_up_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function (confirmed) {
if (confirmed) { if (confirmed) {
self.set("loading", true) self.set("loading", true)
return Discourse.ScreenedIpAddress.rollUp().then(function() { return Discourse.ScreenedIpAddress.rollUp().then(function(results) {
if (results && results.subnets) {
if (results.subnets.length > 0) {
self.send("show"); self.send("show");
bootbox.alert(I18n.t("admin.logs.screened_ips.rolled_up_some_subnets", { subnets: results.subnets.join(", ") }));
} else {
self.set("loading", false);
bootbox.alert(I18n.t("admin.logs.screened_ips.rolled_up_no_subnet"));
}
}
}); });
} }
}); });

View file

@ -29,21 +29,51 @@ class Admin::ScreenedIpAddressesController < Admin::AdminController
render json: success_json render json: success_json
end end
def roll_up def star_subnets_query
# 1 - retrieve all subnets that needs roll up @star_subnets_query ||= <<-SQL
sql = <<-SQL SELECT network(inet(host(ip_address) || '/24')) AS ip_range
SELECT network(inet(host(ip_address) || './24')) AS ip_range
FROM screened_ip_addresses FROM screened_ip_addresses
WHERE action_type = :action_type WHERE action_type = #{ScreenedIpAddress.actions[:block]}
AND family(ip_address) = 4 AND family(ip_address) = 4
AND masklen(ip_address) = 32 AND masklen(ip_address) = 32
GROUP BY ip_range GROUP BY ip_range
HAVING COUNT(*) >= :min_count HAVING COUNT(*) >= :min_count
SQL SQL
end
subnets = ScreenedIpAddress.exec_sql(sql, def star_star_subnets_query
action_type: ScreenedIpAddress.actions[:block], @star_star_subnets_query ||= <<-SQL
min_count: SiteSetting.min_ban_entries_for_roll_up).values.flatten WITH weighted_subnets AS (
SELECT network(inet(host(ip_address) || '/16')) AS ip_range,
CASE masklen(ip_address)
WHEN 32 THEN 1
WHEN 24 THEN :roll_up_weight
ELSE 0
END AS weight
FROM screened_ip_addresses
WHERE action_type = #{ScreenedIpAddress.actions[:block]}
AND family(ip_address) = 4
)
SELECT ip_range
FROM weighted_subnets
GROUP BY ip_range
HAVING SUM(weight) >= :min_count
SQL
end
def star_subnets
min_count = SiteSetting.min_ban_entries_for_roll_up
ScreenedIpAddress.exec_sql(star_subnets_query, min_count: min_count).values.flatten
end
def star_star_subnets
weight = SiteSetting.min_ban_entries_for_roll_up
ScreenedIpAddress.exec_sql(star_star_subnets_query, min_count: 10, roll_up_weight: weight).values.flatten
end
def roll_up
# 1 - retrieve all subnets that needs roll up
subnets = [star_subnets, star_star_subnets].flatten
# 2 - log the call # 2 - log the call
StaffActionLogger.new(current_user).log_roll_up(subnets) unless subnets.blank? StaffActionLogger.new(current_user).log_roll_up(subnets) unless subnets.blank?
@ -63,25 +93,23 @@ class Admin::ScreenedIpAddressesController < Admin::AdminController
MIN(created_at) AS min_created_at, MIN(created_at) AS min_created_at,
MAX(last_match_at) AS max_last_match_at MAX(last_match_at) AS max_last_match_at
FROM screened_ip_addresses FROM screened_ip_addresses
WHERE action_type = :action_type WHERE action_type = #{ScreenedIpAddress.actions[:block]}
AND family(ip_address) = 4 AND family(ip_address) = 4
AND masklen(ip_address) = 32
AND ip_address << :ip_address AND ip_address << :ip_address
) s ) s
WHERE ip_address = :ip_address WHERE ip_address = :ip_address
SQL SQL
ScreenedIpAddress.exec_sql(sql, action_type: ScreenedIpAddress.actions[:block], ip_address: subnet) ScreenedIpAddress.exec_sql(sql, ip_address: subnet)
# 5 - remove old matches # 5 - remove old matches
ScreenedIpAddress.where(action_type: ScreenedIpAddress.actions[:block]) ScreenedIpAddress.where(action_type: ScreenedIpAddress.actions[:block])
.where("family(ip_address) = 4") .where("family(ip_address) = 4")
.where("masklen(ip_address) = 32")
.where("ip_address << ?", subnet) .where("ip_address << ?", subnet)
.delete_all .delete_all
end end
render json: success_json render json: success_json.merge!({ subnets: subnets })
end end
private private

View file

@ -1862,6 +1862,8 @@ en:
description: 'IP addresses that are being watched. Use "Allow" to whitelist IP addresses.' description: 'IP addresses that are being watched. Use "Allow" to whitelist IP addresses.'
delete_confirm: "Are you sure you want to remove the rule for %{ip_address}?" delete_confirm: "Are you sure you want to remove the rule for %{ip_address}?"
roll_up_confirm: "Are you sure you want to roll up ban entries?" roll_up_confirm: "Are you sure you want to roll up ban entries?"
rolled_up_some_subnets: "Successfully rolled up IP ban entries to these subnets: %{subnets}."
rolled_up_no_subnet: "There was nothing to roll up."
actions: actions:
block: "Block" block: "Block"
do_nothing: "Allow" do_nothing: "Allow"

View file

@ -0,0 +1,24 @@
# custom importer for www.t-nation.com, feel free to borrow ideas
require File.expand_path(File.dirname(__FILE__) + "/base.rb")
require "mysql2"
class ImportScripts::Tnation < ImportScripts::Base
DATABASE = "tnation"
def initialize
super
@client = Mysql2::Client.new(
host: "localhost",
database: DATABASE
)
end
def execute
end
end
ImportScripts::Tnation.new.perform

View file

@ -20,7 +20,7 @@ describe Admin::ScreenedIpAddressesController do
describe 'roll_up' do describe 'roll_up' do
it "works" do it "rolls up 1.2.3.* entries" do
Fabricate(:screened_ip_address, ip_address: "1.2.3.4", match_count: 1) Fabricate(:screened_ip_address, ip_address: "1.2.3.4", match_count: 1)
Fabricate(:screened_ip_address, ip_address: "1.2.3.5", match_count: 1) Fabricate(:screened_ip_address, ip_address: "1.2.3.5", match_count: 1)
Fabricate(:screened_ip_address, ip_address: "1.2.3.6", match_count: 1) Fabricate(:screened_ip_address, ip_address: "1.2.3.6", match_count: 1)
@ -29,7 +29,7 @@ describe Admin::ScreenedIpAddressesController do
Fabricate(:screened_ip_address, ip_address: "42.42.42.5", match_count: 1) Fabricate(:screened_ip_address, ip_address: "42.42.42.5", match_count: 1)
StaffActionLogger.any_instance.expects(:log_roll_up) StaffActionLogger.any_instance.expects(:log_roll_up)
SiteSetting.expects(:min_ban_entries_for_roll_up).returns(3) SiteSetting.stubs(:min_ban_entries_for_roll_up).returns(3)
xhr :post, :roll_up xhr :post, :roll_up
response.should be_success response.should be_success
@ -39,6 +39,26 @@ describe Admin::ScreenedIpAddressesController do
subnet.match_count.should == 3 subnet.match_count.should == 3
end end
it "rolls up 1.2.*.* entries" do
Fabricate(:screened_ip_address, ip_address: "1.2.3.4", match_count: 1)
Fabricate(:screened_ip_address, ip_address: "1.2.3.5", match_count: 1)
Fabricate(:screened_ip_address, ip_address: "1.2.4.6", match_count: 1)
Fabricate(:screened_ip_address, ip_address: "1.2.7.8", match_count: 1)
Fabricate(:screened_ip_address, ip_address: "1.2.9.1", match_count: 1)
Fabricate(:screened_ip_address, ip_address: "1.2.42.0/24", match_count: 1)
StaffActionLogger.any_instance.expects(:log_roll_up)
SiteSetting.stubs(:min_ban_entries_for_roll_up).returns(5)
xhr :post, :roll_up
response.should be_success
subnet = ScreenedIpAddress.where(ip_address: "1.2.0.0/16").first
subnet.should be_present
subnet.match_count.should == 6
end
end end
end end