Introduce source IP based rate limiting
Since we are running haproxy in L4, we are tracking the incoming byte rate from client IPs and rejecting TCP connections in a sliding window. This approach limits the incoming HTTP requests however image uploading through the horizon web app is unaffected. Change-Id: Ie40d28acb2dc2983fc9edbbeacfd671b380a8f6d Closes-Bug: #1836514 Signed-off-by: Mert Kırpıcı <mert.kirpici@canonical.com>
This commit is contained in:
parent
e47ecb9359
commit
c0f8708761
18
config.yaml
18
config.yaml
|
@ -291,6 +291,24 @@ options:
|
|||
default: False
|
||||
description: |
|
||||
If True, exposes stats interface externally.
|
||||
haproxy-rate-limiting-enabled:
|
||||
type: boolean
|
||||
default: False
|
||||
description: |
|
||||
If True, imposes source IP based rate limits on accessing the dashboard.
|
||||
The actual limits are controlled through the configuration options:
|
||||
haproxy-limit-period and haproxy-max-bytes-in-rate.
|
||||
haproxy-max-bytes-in-rate:
|
||||
type: int
|
||||
default: 500000
|
||||
description: |
|
||||
The number of bytes the client is allowed to send through the connection
|
||||
during one limit period.
|
||||
haproxy-limit-period:
|
||||
type: int
|
||||
default: 10
|
||||
description: |
|
||||
The number of seconds over the number of bytes are counted.
|
||||
enforce-ssl:
|
||||
type: boolean
|
||||
default: False
|
||||
|
|
|
@ -87,6 +87,14 @@ class HorizonHAProxyContext(OSContextGenerator):
|
|||
'prefer_ipv6': config('prefer-ipv6'),
|
||||
'haproxy_expose_stats': config('haproxy-expose-stats')
|
||||
}
|
||||
|
||||
if config('haproxy-rate-limiting-enabled'):
|
||||
ctxt['haproxy_rate_limiting_enabled'] = \
|
||||
config('haproxy-rate-limiting-enabled')
|
||||
ctxt['haproxy_max_bytes_in_rate'] = \
|
||||
config('haproxy-max-bytes-in-rate')
|
||||
ctxt['haproxy_limit_period'] = config('haproxy-limit-period')
|
||||
|
||||
return ctxt
|
||||
|
||||
|
||||
|
|
|
@ -51,6 +51,11 @@ listen {{ service }}
|
|||
{%- endif %}
|
||||
balance source
|
||||
option tcplog
|
||||
{% if haproxy_rate_limiting_enabled -%}
|
||||
stick-table type ip size 100k store bytes_in_rate({{ haproxy_limit_period }}s)
|
||||
tcp-request connection track-sc0 src
|
||||
tcp-request connection reject if { sc_bytes_in_rate(0) gt {{ haproxy_max_bytes_in_rate }} }
|
||||
{% endif -%}
|
||||
{% for unit, address in units.items() -%}
|
||||
server {{ unit }} {{ address }}:{{ ports[1] }} check
|
||||
{% endfor %}
|
||||
|
|
|
@ -1239,6 +1239,29 @@ class TestHorizonContexts(CharmTestCase):
|
|||
_open.assert_called_with('/etc/default/haproxy', 'w')
|
||||
self.assertTrue(_file.write.called)
|
||||
|
||||
def test_HorizonHAProxyContext_rate_limiting(self):
|
||||
limiting = True
|
||||
max_bytes_in = 100000
|
||||
limit_period = 42
|
||||
self.test_config.set('haproxy-rate-limiting-enabled', limiting)
|
||||
self.test_config.set('haproxy-max-bytes-in-rate', max_bytes_in)
|
||||
self.test_config.set('haproxy-limit-period', limit_period)
|
||||
self.relation_ids.return_value = []
|
||||
self.local_unit.return_value = 'openstack-dashboard/0'
|
||||
self.get_relation_ip.return_value = "10.5.0.1"
|
||||
with patch_open() as (_open, _file):
|
||||
self.assertEquals(horizon_contexts.HorizonHAProxyContext()(),
|
||||
{'units': {'openstack-dashboard-0': '10.5.0.1'},
|
||||
'service_ports': {'dash_insecure': [80, 70],
|
||||
'dash_secure': [443, 433]},
|
||||
'prefer_ipv6': False,
|
||||
'haproxy_expose_stats': False,
|
||||
'haproxy_rate_limiting_enabled': limiting,
|
||||
'haproxy_max_bytes_in_rate': max_bytes_in,
|
||||
'haproxy_limit_period': limit_period})
|
||||
_open.assert_called_with('/etc/default/haproxy', 'w')
|
||||
self.assertTrue(_file.write.called)
|
||||
|
||||
def test_RouterSettingContext(self):
|
||||
self.test_config.set('profile', 'cisco')
|
||||
self.assertEqual(horizon_contexts.RouterSettingContext()(),
|
||||
|
|
Loading…
Reference in New Issue