Commit 7ee677ca authored by Ryan Cobb's avatar Ryan Cobb

Allow whitelisting of ports

Adds the ability to whitelist ports
parent af97fc57
......@@ -361,18 +361,33 @@ module ApplicationSettingImplementation
def separate_whitelists(string_array)
string_array.reduce([[], []]) do |(ip_whitelist, domain_whitelist), string|
ip_obj = Gitlab::Utils.string_to_ip_object(string)
address, port = parse_addr_and_port(string)
ip_obj = Gitlab::Utils.string_to_ip_object(address)
if ip_obj
ip_whitelist << ip_obj
ip_whitelist << Gitlab::UrlBlockers::IpWhitelistEntry.new(ip_obj, port: port)
else
domain_whitelist << string
domain_whitelist << Gitlab::UrlBlockers::DomainWhitelistEntry.new(address, port: port)
end
[ip_whitelist, domain_whitelist]
end
end
def parse_addr_and_port(str)
case str
when /\A\[(?<address> .* )\]:(?<port> \d+ )\z/x # string like "[::1]:80"
address, port = $~[:address], $~[:port]
when /\A(?<address> [^:]+ ):(?<port> \d+ )\z/x # string like "127.0.0.1:80"
address, port = $~[:address], $~[:port]
else # string with no port number
address, port = str, nil
end
[address, port&.to_i]
end
def array_to_string(arr)
arr&.join("\n")
end
......
---
title: Add ability to whitelist ports
merge_request:
author:
type: added
......@@ -49,7 +49,7 @@ module Gitlab
return [uri, nil] unless address_info
ip_address = ip_address(address_info)
return [uri, nil] if domain_whitelisted?(uri) || ip_whitelisted?(ip_address)
return [uri, nil] if domain_whitelisted?(uri) || ip_whitelisted?(ip_address, port: get_port(uri))
protected_uri_with_hostname = enforce_uri_hostname(ip_address, uri, dns_rebind_protection)
......@@ -150,7 +150,7 @@ module Gitlab
end
def get_port(uri)
uri.port || uri.default_port
uri&.port || uri&.default_port
end
def validate_html_tags(uri)
......@@ -254,11 +254,11 @@ module Gitlab
end
def domain_whitelisted?(uri)
Gitlab::UrlBlockers::UrlWhitelist.domain_whitelisted?(uri.normalized_host)
Gitlab::UrlBlockers::UrlWhitelist.domain_whitelisted?(uri.normalized_host, port: get_port(uri))
end
def ip_whitelisted?(ip_address)
Gitlab::UrlBlockers::UrlWhitelist.ip_whitelisted?(ip_address)
def ip_whitelisted?(ip_address, port: nil)
Gitlab::UrlBlockers::UrlWhitelist.ip_whitelisted?(ip_address, port: port)
end
def config
......
# frozen_string_literal: true
module Gitlab
module UrlBlockers
class DomainWhitelistEntry
attr_reader :domain, :port
def initialize(domain, port: nil)
@domain = domain
@port = port
end
def match?(requested_domain, requested_port = nil)
return false unless @domain == requested_domain
return true if @port.nil?
@port == requested_port.to_i
end
end
end
end
# frozen_string_literal: true
module Gitlab
module UrlBlockers
class IpWhitelistEntry
attr_reader :ip, :port
def initialize(ip, port: nil)
@ip = ip
@port = port
end
def match?(requested_ip, requested_port = nil)
return false unless @ip.include?(requested_ip)
return true if @port.nil?
@port == requested_port.to_i
end
end
end
end
......@@ -4,21 +4,25 @@ module Gitlab
module UrlBlockers
class UrlWhitelist
class << self
def ip_whitelisted?(ip_string)
def ip_whitelisted?(ip_string, port: nil)
return false if ip_string.blank?
ip_whitelist, _ = outbound_local_requests_whitelist_arrays
ip_obj = Gitlab::Utils.string_to_ip_object(ip_string)
ip_whitelist.any? { |ip| ip.include?(ip_obj) }
ip_whitelist.any? do |ip_whitelist_entry|
ip_whitelist_entry.match?(ip_obj, port)
end
end
def domain_whitelisted?(domain_string)
def domain_whitelisted?(domain_string, port: nil)
return false if domain_string.blank?
_, domain_whitelist = outbound_local_requests_whitelist_arrays
domain_whitelist.include?(domain_string)
domain_whitelist.any? do |domain_whitelist_entry|
domain_whitelist_entry.match?(domain_string, port)
end
end
private
......
......@@ -501,6 +501,18 @@ describe Gitlab::UrlBlocker, :stub_invalid_dns_only do
it_behaves_like 'dns rebinding checks'
end
end
context 'with ports' do
let(:whitelist) do
["127.0.0.1:2000"]
end
it 'allows domain with port when resolved ip has port whitelisted' do
stub_domain_resolv("www.resolve-domain.com", '127.0.0.1') do
expect(described_class).not_to be_blocked_url("http://www.resolve-domain.com:2000", url_blocker_attributes)
end
end
end
end
end
......
......@@ -13,12 +13,8 @@ describe Gitlab::UrlBlockers::UrlWhitelist do
end
describe '#domain_whitelisted?' do
let(:whitelist) do
[
'www.example.com',
'example.com'
]
end
let(:whitelist) { ['www.example.com', 'example.com'] }
let(:not_whitelisted) { ['subdomain.example.com', 'example.org'] }
it 'returns true if domains present in whitelist' do
aggregate_failures do
......@@ -26,7 +22,7 @@ describe Gitlab::UrlBlockers::UrlWhitelist do
expect(described_class).to be_domain_whitelisted(domain)
end
['subdomain.example.com', 'example.org'].each do |domain|
not_whitelisted.each do |domain|
expect(described_class).not_to be_domain_whitelisted(domain)
end
end
......@@ -35,6 +31,29 @@ describe Gitlab::UrlBlockers::UrlWhitelist do
it 'returns false when domain is blank' do
expect(described_class).not_to be_domain_whitelisted(nil)
end
context 'with ports' do
let(:whitelist) { ['example.io:3000'] }
let(:parsed_whitelist) { [['example.io', { port: 3000 }]] }
let(:not_whitelisted) do
[
'example.io',
['example.io', { port: 3001 }]
]
end
it 'returns true if domain and ports present in whitelist' do
aggregate_failures do
parsed_whitelist.each do |domain_and_port|
expect(described_class).to be_domain_whitelisted(*domain_and_port)
end
not_whitelisted.each do |domain_and_port|
expect(described_class).not_to be_domain_whitelisted(*domain_and_port)
end
end
end
end
end
describe '#ip_whitelisted?' do
......@@ -114,5 +133,35 @@ describe Gitlab::UrlBlockers::UrlWhitelist do
expect(described_class).not_to be_ip_whitelisted("127.0.1.15")
end
end
context 'with ports' do
let(:whitelist) { ['127.0.0.9:3000', '[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443'] }
let(:parsed_whitelist) do
[
['127.0.0.9', { port: 3000 }],
['[2001:db8:85a3:8d3:1319:8a2e:370:7348]', { port: 443 }]
]
end
let(:not_whitelisted) do
[
'127.0.0.9',
['127.0.0.9', { port: 3001 }],
'[2001:db8:85a3:8d3:1319:8a2e:370:7348]',
['[2001:db8:85a3:8d3:1319:8a2e:370:7348]', { port: 3001 }]
]
end
it 'returns true if ip and ports present in whitelist' do
aggregate_failures do
parsed_whitelist.each do |ip_and_port|
expect(described_class).to be_ip_whitelisted(*ip_and_port)
end
not_whitelisted.each do |ip_and_port|
expect(described_class).not_to be_ip_whitelisted(*ip_and_port)
end
end
end
end
end
end
......@@ -68,12 +68,12 @@ RSpec.shared_examples 'application settings examples' do
setting.outbound_local_requests_whitelist_raw = 'example.com'
expect(setting.outbound_local_requests_whitelist_arrays).to contain_exactly(
[], ['example.com']
[], [an_object_having_attributes(domain: 'example.com')]
)
setting.outbound_local_requests_whitelist_raw = 'gitlab.com'
expect(setting.outbound_local_requests_whitelist_arrays).to contain_exactly(
[], ['gitlab.com']
[], [an_object_having_attributes(domain: 'gitlab.com')]
)
end
end
......@@ -81,15 +81,42 @@ RSpec.shared_examples 'application settings examples' do
context 'outbound_local_requests_whitelist_arrays' do
it 'separates the IPs and domains' do
setting.outbound_local_requests_whitelist = [
'192.168.1.1', '127.0.0.0/28', 'www.example.com', 'example.com',
'::ffff:a00:2', '1:0:0:0:0:0:0:0/124', 'subdomain.example.com'
'192.168.1.1',
'127.0.0.0/28',
'::ffff:a00:2',
'1:0:0:0:0:0:0:0/124',
'example.com',
'subdomain.example.com',
'www.example.com',
'::',
'1::',
'::1',
'1:2:3:4:5::7:8',
'[1:2:3:4:5::7:8]',
'[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443',
'www.example2.com:8080',
'example.com:8080'
]
ip_whitelist = [
IPAddr.new('192.168.1.1'), IPAddr.new('127.0.0.0/8'),
IPAddr.new('::ffff:a00:2'), IPAddr.new('1:0:0:0:0:0:0:0/124')
an_object_having_attributes(ip: IPAddr.new('192.168.1.1')),
an_object_having_attributes(ip: IPAddr.new('127.0.0.0/8')),
an_object_having_attributes(ip: IPAddr.new('::ffff:a00:2')),
an_object_having_attributes(ip: IPAddr.new('1:0:0:0:0:0:0:0/124')),
an_object_having_attributes(ip: IPAddr.new('::')),
an_object_having_attributes(ip: IPAddr.new('1::')),
an_object_having_attributes(ip: IPAddr.new('::1')),
an_object_having_attributes(ip: IPAddr.new('1:2:3:4:5::7:8')),
an_object_having_attributes(ip: IPAddr.new('[1:2:3:4:5::7:8]')),
an_object_having_attributes(ip: IPAddr.new('[2001:db8:85a3:8d3:1319:8a2e:370:7348]'), port: 443)
]
domain_whitelist = [
an_object_having_attributes(domain: 'example.com'),
an_object_having_attributes(domain: 'subdomain.example.com'),
an_object_having_attributes(domain: 'www.example.com'),
an_object_having_attributes(domain: 'www.example2.com', port: 8080),
an_object_having_attributes(domain: 'example.com', port: 8080)
]
domain_whitelist = ['www.example.com', 'example.com', 'subdomain.example.com']
expect(setting.outbound_local_requests_whitelist_arrays).to contain_exactly(
ip_whitelist, domain_whitelist
......@@ -117,7 +144,7 @@ RSpec.shared_examples 'application settings examples' do
expect(setting.outbound_local_requests_whitelist_arrays).to contain_exactly(
[],
['example.com']
[an_object_having_attributes(domain: 'example.com')]
)
setting.add_to_outbound_local_requests_whitelist(
......@@ -126,7 +153,7 @@ RSpec.shared_examples 'application settings examples' do
expect(setting.outbound_local_requests_whitelist_arrays).to contain_exactly(
[],
['example.com', 'gitlab.com']
[an_object_having_attributes(domain: 'example.com'), an_object_having_attributes(domain: 'gitlab.com')]
)
end
......@@ -137,7 +164,7 @@ RSpec.shared_examples 'application settings examples' do
expect(setting.outbound_local_requests_whitelist).to contain_exactly('gitlab.com')
expect(setting.outbound_local_requests_whitelist_arrays).to contain_exactly(
[], ['gitlab.com']
[], [an_object_having_attributes(domain: 'gitlab.com')]
)
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