GitLab ist jetzt auf dem neuesten Stand! Die jüngsten Probleme wurden behoben. Wir entschuldigen uns für die dadurch verursachten Unannehmlichkeiten.

Commit 6a5cd3ca authored by Robert Speicher's avatar Robert Speicher

Merge branch 'feature/check-against-rbl-only' into 'master'

Split from !2455

References #9092

See merge request !2515
parent 96f12f57
......@@ -69,6 +69,7 @@ v 8.4.0 (unreleased)
- Expose button to CI Lint tool on project builds page
- Fix: Creator should be added as a master of the project on creation
- Added X-GitLab-... headers to emails from CI and Email On Push services (Anton Baklanov)
- Add IP check against DNSBLs at account sign-up
v 8.3.4
- Use gitlab-workhorse 0.5.4 (fixes API routing bug)
......
......@@ -74,6 +74,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:metrics_timeout,
:metrics_method_call_threshold,
:metrics_sample_interval,
:ip_blocking_enabled,
:dnsbl_servers_list,
:recaptcha_enabled,
:recaptcha_site_key,
:recaptcha_private_key,
......
......@@ -8,6 +8,11 @@ class RegistrationsController < Devise::RegistrationsController
def create
if !Gitlab::Recaptcha.load_configurations! || verify_recaptcha
if Gitlab::IpCheck.new(request.remote_ip).spam?
flash[:alert] = 'Could not create an account. This IP is listed for spam.'
return render action: 'new'
end
super
else
flash[:alert] = "There was an error with the reCAPTCHA code below. Please re-enter the code."
......
......@@ -43,6 +43,8 @@
# metrics_port :integer default(8089)
# sentry_enabled :boolean default(FALSE)
# sentry_dsn :string
# ip_blocking_enabled :boolean default(FALSE)
# dns_blacklist_threshold :float default(0.33)
#
class ApplicationSetting < ActiveRecord::Base
......
......@@ -212,6 +212,22 @@
%fieldset
%legend Spam and Anti-bot Protection
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :ip_blocking_enabled do
= f.check_box :ip_blocking_enabled
Enable IP check against blacklist at sign-up
.help-block Helps preventing accounts creation from 'known spam sources'
.form-group
= f.label :dnsbl_servers_list, class: 'control-label col-sm-2' do
DNSBL servers list
.col-sm-10
= f.text_field :dnsbl_servers_list, class: 'form-control'
.help-block
Please enter DNSBL servers separated with comma
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
......
class AddIpBlockingSettingsToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :ip_blocking_enabled, :boolean, default: false
add_column :application_settings, :dnsbl_servers_list, :text
end
end
......@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160119145451) do
ActiveRecord::Schema.define(version: 20160120130905) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......@@ -64,6 +64,8 @@ ActiveRecord::Schema.define(version: 20160119145451) do
t.integer "metrics_sample_interval", default: 15
t.boolean "sentry_enabled", default: false
t.string "sentry_dsn"
t.boolean "ip_blocking_enabled", default: false
t.text "dnsbl_servers_list"
end
create_table "audit_events", force: :cascade do |t|
......
require 'resolv'
class DNSXLCheck
class Resolver
def self.search(query)
begin
Resolv.getaddress(query)
true
rescue Resolv::ResolvError
false
end
end
end
IP_REGEXP = /\A(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\z/
DEFAULT_THRESHOLD = 0.33
def self.create_from_list(list)
dnsxl_check = DNSXLCheck.new
list.each do |entry|
dnsxl_check.add_list(entry.domain, entry.weight)
end
dnsxl_check
end
def test(ip)
if use_threshold?
test_with_threshold(ip)
else
test_strict(ip)
end
end
def test_with_threshold(ip)
return false if lists.empty?
search(ip)
final_score >= threshold
end
def test_strict(ip)
return false if lists.empty?
search(ip)
@score > 0
end
def use_threshold=(value)
@use_threshold = value == true
end
def use_threshold?
@use_threshold &&= true
end
def threshold=(threshold)
raise ArgumentError, "'threshold' value must be grather than 0 and less than or equal to 1" unless threshold > 0 && threshold <= 1
@threshold = threshold
end
def threshold
@threshold ||= DEFAULT_THRESHOLD
end
def add_list(domain, weight)
@lists ||= []
@lists << { domain: domain, weight: weight }
end
def lists
@lists ||= []
end
private
def search(ip)
raise ArgumentError, "'ip' value must be in #{IP_REGEXP} format" unless ip.match(IP_REGEXP)
@score = 0
reversed = reverse_ip(ip)
search_in_rbls(reversed)
end
def reverse_ip(ip)
ip.split('.').reverse.join('.')
end
def search_in_rbls(reversed_ip)
lists.each do |rbl|
query = "#{reversed_ip}.#{rbl[:domain]}"
@score += rbl[:weight] if Resolver.search(query)
end
end
def final_score
weights = lists.map{ |rbl| rbl[:weight] }.reduce(:+).to_i
return 0 if weights == 0
(@score.to_f / weights.to_f).round(2)
end
end
module Gitlab
class IpCheck
def initialize(ip)
@ip = ip
application_settings = ApplicationSetting.current
@ip_blocking_enabled = application_settings.ip_blocking_enabled
@dnsbl_servers_list = application_settings.dnsbl_servers_list
end
def spam?
@ip_blocking_enabled && blacklisted?
end
private
def blacklisted?
on_dns_blacklist?
end
def on_dns_blacklist?
dnsbl_check = DNSXLCheck.new
prepare_dnsbl_list(dnsbl_check)
dnsbl_check.test(@ip)
end
def prepare_dnsbl_list(dnsbl_check)
@dnsbl_servers_list.split(',').map(&:strip).reject(&:empty?).each do |domain|
dnsbl_check.add_list(domain, 1)
end
end
end
end
require 'spec_helper'
require 'ostruct'
describe 'DNSXLCheck', lib: true, no_db: true do
let(:spam_ip) { '127.0.0.2' }
let(:no_spam_ip) { '127.0.0.3' }
let(:invalid_ip) { 'a.b.c.d' }
let!(:dnsxl_check) { DNSXLCheck.create_from_list([OpenStruct.new({ domain: 'test', weight: 1 })]) }
before(:context) do
class DNSXLCheck::Resolver
class << self
alias_method :old_search, :search
def search(query)
return false if query.match(/always\.failing\.domain\z/)
return true if query.match(/\A2\.0\.0\.127\./)
return false if query.match(/\A3\.0\.0\.127\./)
end
end
end
end
describe '#test' do
before do
dnsxl_check.threshold = 0.75
dnsxl_check.add_list('always.failing.domain', 1)
end
context 'when threshold is used' do
before { dnsxl_check.use_threshold= true }
it { expect(dnsxl_check.test(spam_ip)).to be_falsey }
end
context 'when threshold is not used' do
before { dnsxl_check.use_threshold= false }
it { expect(dnsxl_check.test(spam_ip)).to be_truthy }
end
end
describe '#test_with_threshold' do
it { expect{ dnsxl_check.test_with_threshold(invalid_ip) }.to raise_error(ArgumentError) }
it { expect(dnsxl_check.test_with_threshold(spam_ip)).to be_truthy }
it { expect(dnsxl_check.test_with_threshold(no_spam_ip)).to be_falsey }
end
describe '#test_strict' do
before do
dnsxl_check.threshold = 1
dnsxl_check.add_list('always.failing.domain', 1)
end
it { expect{ dnsxl_check.test_strict(invalid_ip) }.to raise_error(ArgumentError) }
it { expect(dnsxl_check.test_with_threshold(spam_ip)).to be_falsey }
it { expect(dnsxl_check.test_with_threshold(no_spam_ip)).to be_falsey }
it { expect(dnsxl_check.test_strict(spam_ip)).to be_truthy }
it { expect(dnsxl_check.test_strict(no_spam_ip)).to be_falsey }
end
describe '#threshold=' do
it { expect{ dnsxl_check.threshold = 0 }.to raise_error(ArgumentError) }
it { expect{ dnsxl_check.threshold = 1.1 }.to raise_error(ArgumentError) }
it { expect{ dnsxl_check.threshold = 0.5 }.not_to raise_error }
end
end
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment