Audit revision conflicts between IPv4 and IPv6 due to shared session variable
Describe the bug
For creating audit revisions, separate paths exists for DHCP4 and DHCP6 auditing. The paths are not fully separated though. The two implementations make use of some shared session variables, amongst which audit_revision_id
. These session variables tightly couple the two paths, which can lead to conflicts.
To Reproduce
Here's an example scenario for a conflict:
kea=# SELECT createAuditRevisionDHCP4(CURRENT_TIMESTAMP,'all','test', true); -- audit_revision_id = 1
kea=# SELECT createAuditRevisionDHCP6(CURRENT_TIMESTAMP,'all','test', true); -- audit_revision_id = 1
kea=# SELECT createAuditRevisionDHCP4(CURRENT_TIMESTAMP,'all','test', true); -- audit_revision_id = 2
kea=# INSERT INTO dhcp4_client_class (name) VALUES ('something'); -- uses audit_revision_id = 2, ok
kea=# INSERT INTO dhcp6_client_class (name) VALUES ('something'); -- uses audit_revision_id = 2, fail
Because the global revision id now points at 2, but only dhcp6_audit
with id 1 exists, we get:
ERROR: insert or update on table "dhcp6_audit" violates foreign key constraint "fk_dhcp6_audit_revision"
DETAIL: Key (revision_id)=(2) is not present in table "dhcp6_audit_revision".
CONTEXT: SQL statement "INSERT INTO dhcp6_audit (object_type, object_id, modification_type, revision_id)
VALUES (object_type_val, object_id_val,
(SELECT id FROM modification WHERE modification_type = modification_type_val),
audit_revision_id)"
PL/pgSQL function createauditentrydhcp6(character varying,bigint,character varying) line 11 at SQL statement
SQL statement "SELECT createAuditEntryDHCP6('dhcp6_client_class', NEW.id, 'create')"
PL/pgSQL function func_dhcp6_client_class_ains() line 4 at PERFORM
In this case, the INSERT breaks, because the DHCP6 audit record points to a non-existent dhcp6_audit_revision
id.
Another scenario is possible, where coincidentally the incorrect revision id does exist. In that case, the audit will be assigned to an old revision in the audit trail, changing history. An example scenario for this one:
kea=# SELECT createAuditRevisionDHCP4(CURRENT_TIMESTAMP,'all','test', true); -- audit_revision_id = 1
kea=# INSERT INTO dhcp4_client_class (name) VALUES ('one'); -- uses audit_revision_id = 1, ok
kea=# SELECT createAuditRevisionDHCP4(CURRENT_TIMESTAMP,'all','test', true); -- audit_revision_id = 2
kea=# INSERT INTO dhcp4_client_class (name) VALUES ('two'); -- uses audit_revision_id = 2, ok
kea=# SELECT createAuditRevisionDHCP6(CURRENT_TIMESTAMP,'all','test', true); -- audit_revision_id = 1
kea=# INSERT INTO dhcp6_client_class (name) VALUES ('something'); -- uses audit_revision_id = 1, ok
kea=# INSERT INTO dhcp4_client_class (name) VALUES ('three'); -- uses audit_revision_id = 1, fail
Expected behavior The audit revisions should be independent and use their own specific session variables, instead of shared ones. When running the first scenario from the reproduction scenario:
- It must not fail
- The DHCP6 change must be logged in
dhcp6_audit
withrevision_id
= 1 - The DHCP4 change must be logged in
dhcp4_audit
withrevision_id
= 2
When running the second scenario:
- DHCP4 client class 'one' must be related to
dhcp4_audit' with
revision_id` = 1 - DHCP4 client class 'two' must be related to
dhcp4_audit' with
revision_id` = 2 - DHCP4 client class 'three' must be related to
dhcp4_audit' with
revision_id` = 2 - DHCP6 client class 'something' must be related to
dhcp6_audit' with
revision_id` = 1
Environment:
- Kea version: 2.5.3 and before
- Affects both MySQL and PostgreSQL
Work-around
To prevent issues with the current database schema, make sure that DHCP4 and DHCP6 updates are separated in the query sequence:
- create DHCP4 audit revision
- perform all DHCP4 updates
- create DHCP6 audit revision
- perform all DHCP6 updates
Contacting you You can reach me at account-gitlab-isc@makaay.nl