Skip to content

Commit

Permalink
[4.2.x] Fix #742 - Add setting to limit NRTM response size (backport #…
Browse files Browse the repository at this point in the history
…745) (#751)

(cherry picked from commit 0e33e13)

Co-authored-by: Sasha Romijn <[email protected]>
  • Loading branch information
mergify[bot] and mxsasha authored Feb 15, 2023
1 parent ced8c61 commit 6093f6d
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 1 deletion.
10 changes: 10 additions & 0 deletions docs/admins/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,16 @@ Sources
|br| **Default**: not defined, all access denied. Clients in
``nrtm_access_list``, if defined, have filtered access.
|br| **Change takes effect**: after SIGHUP, upon next request.
* ``sources.{name}.nrtm_query_serial_range_limit``: the maximum number of
serials a client may request in one NRTM query, if otherwise permitted.
This is intended to limit the maximum load of NRTM queries - it is checked
before IRRd runs any heavy database queries. The limit is applied to the
requested range regardless of any gaps, i.e. querying a range of ``10-20``
is allowed if this setting to be at least 10, even if there are no entries
for some of those serials. IRRd is aware of the serial ``LAST`` refers to
and will take that into account.
|br| **Default**: not defined, no limits on NRTM query size.
|br| **Change takes effect**: after SIGHUP, upon next request.
* ``sources.{name}.strict_import_keycert_objects``: a setting used when
migrating authoritative data that may contain `key-cert` objects.
See the :doc:`data migration guide </admins/availability-and-migration>`
Expand Down
3 changes: 3 additions & 0 deletions irrd/conf/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
'export_timer',
'nrtm_access_list',
'nrtm_access_list_unfiltered',
'nrtm_query_serial_range_limit',
'strict_import_keycert_objects',
'rpki_excluded',
'scopefilter_excluded',
Expand Down Expand Up @@ -398,6 +399,8 @@ def _check_staging_config(self) -> List[str]:
errors.append(f'Setting import_timer for source {name} must be a number.')
if not str(details.get('export_timer', '0')).isnumeric():
errors.append(f'Setting export_timer for source {name} must be a number.')
if not str(details.get('nrtm_query_serial_range_limit', '0')).isnumeric():
errors.append(f'Setting nrtm_query_serial_range_limit for source {name} must be a number.')

if details.get('nrtm_access_list'):
expected_access_lists.add(details.get('nrtm_access_list'))
Expand Down
3 changes: 3 additions & 0 deletions irrd/conf/test_conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ def test_load_valid_reload_valid_config(self, monkeypatch, save_yaml_config, tmp
'TESTDB': {
'authoritative': True,
'keep_journal': True,
'nrtm_query_serial_range_limit': 10,
},
'TESTDB2': {
'nrtm_host': '192.0.2.1',
Expand Down Expand Up @@ -259,6 +260,7 @@ def test_load_invalid_config(self, save_yaml_config, tmpdir):
'export_timer': 'bar',
'nrtm_host': '192.0.2.1',
'unknown': True,
'nrtm_query_serial_range_limit': 'not-a-number',
},
'TESTDB2': {
'authoritative': True,
Expand Down Expand Up @@ -316,6 +318,7 @@ def test_load_invalid_config(self, save_yaml_config, tmpdir):
assert 'Setting rpki.notify_invalid_header must be a string, if defined.' in str(ce.value)
assert 'Setting import_timer for source TESTDB must be a number.' in str(ce.value)
assert 'Setting export_timer for source TESTDB must be a number.' in str(ce.value)
assert 'Setting nrtm_query_serial_range_limit for source TESTDB must be a number.' in str(ce.value)
assert 'Invalid source name: lowercase' in str(ce.value)
assert 'Invalid source name: invalid char' in str(ce.value)
assert 'but rpki.notify_invalid_enabled is not set' in str(ce.value)
Expand Down
5 changes: 5 additions & 0 deletions irrd/mirroring/nrtm_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,12 @@ def generate(self, source: str, version: str,

serial_end_display = serial_end_available if serial_end_requested is None else serial_end_requested

range_limit = get_setting(f'sources.{source}.nrtm_query_serial_range_limit')
if range_limit and int(range_limit) < (serial_end_display - serial_start_requested):
raise NRTMGeneratorException(f'Serial range requested exceeds maximum range of {range_limit}')

q = RPSLDatabaseJournalQuery().sources([source]).serial_range(serial_start_requested, serial_end_requested)

operations = list(database_handler.execute_query(q))

output = f'%START Version: {version} {source} {serial_start_requested}-{serial_end_display}\n'
Expand Down
44 changes: 43 additions & 1 deletion irrd/mirroring/tests/test_nrtm_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def prepare_generator(monkeypatch, config_override):
'sources': {
'TEST': {
'keep_journal': True,
'nrtm_query_serial_range_limit': 200,
}
}
})
Expand Down Expand Up @@ -84,7 +85,7 @@ def test_generate_serial_range_v1(self, prepare_generator):
%END TEST""").strip()

def test_generate_until_last(self, prepare_generator):
def test_generate_until_last(self, prepare_generator, config_override):
generator, mock_dh = prepare_generator
result = generator.generate('TEST', '3', 110, None, mock_dh)

Expand Down Expand Up @@ -169,6 +170,47 @@ def test_no_source_status_entry(self, prepare_generator, config_override):
generator.generate('TEST', '3', 110, 300, mock_dh)
assert 'There are no journal entries for this source.' in str(nge.value)

def test_v3_range_limit_not_set(self, prepare_generator, config_override):
generator, mock_dh = prepare_generator
config_override({
'sources': {
'TEST': {
'keep_journal': True,
}
}
})

result = generator.generate('TEST', '3', 110, 190, mock_dh)

assert result == textwrap.dedent("""
%START Version: 3 TEST 110-190
ADD 120
object 1 🦄
auth: CRYPT-PW DummyValue # Filtered for security
DEL 180
object 2 🌈
%END TEST""").strip()

def test_range_limit_exceeded(self, prepare_generator, config_override):
generator, mock_dh = prepare_generator
config_override({
'sources': {
'TEST': {
'keep_journal': True,
'nrtm_query_serial_range_limit': 50,
}
}
})

with pytest.raises(NRTMGeneratorException) as nge:
generator.generate('TEST', '3', 110, 190, mock_dh)
assert 'Serial range requested exceeds maximum range of 50' in str(nge.value)

def test_include_auth_hash(self, prepare_generator):
generator, mock_dh = prepare_generator
result = generator.generate('TEST', '3', 110, 190, mock_dh, False)
Expand Down

0 comments on commit 6093f6d

Please sign in to comment.