Skip to content
Snippets Groups Projects

Draft: Initial suggestion - REST zone changelog endpoint(s)

Open pettai requested to merge pettai/knot-dns-rest:changelog into main
5 unresolved threads

REST way of how users may list the changes done in the zone(s)

Introduces two new REST endpoints:

/zones/changelog /zones/changelog/zone (filtered)

To fetch all zone changes from the audit log.

Merge request reports

Loading
Loading

Activity

Filter activity
  • Approvals
  • Assignees & reviewers
  • Comments (from bots)
  • Comments (from users)
  • Commits & branches
  • Edits
  • Labels
  • Lock status
  • Mentions
  • Merge request status
  • Tracking
  • This is probably not up to your code standards, but we needed this feature for our users to be able to check "who did what?" amongst all zones and zone data.

    But I put it out here as-is, and then we can see if it's applicable for this app, and if bigger changes should be done (change log format of the audit log etc.)

    (We also have an "inverse" of the audit log pattern matching, for all audit actions, eg. to see user creation/deletion and login/failed logins, but that's more OT than displaying the change log of the DNS data).

    • Maintainer

      I have no big problem with that.

      So far as I can see, there are some rights missing in my opinion (probably not good idea to send a changelogs to all users).

      Could you add some short documentation (in the README file), and maybe some really simple test?

      I have yet to try it.

      Edited by Jan Hák
    • Doh, the (no)rights required was a miss in this MR branch (I fixed that) Good with working tests that checks for that now :thumbsup:

    • Please register or sign in to reply
  • pettai added 1 commit

    added 1 commit

    • c444c25c - update README with changelog endpoint docs

    Compare with previous version

  • pettai added 1 commit

    added 1 commit

    Compare with previous version

  • Ok :thumbsup: But it seems audit-log isn't configured/tested in pytest ATM. I'll have to fix that first, if you want meaningful tests of this endpoint.

  • pettai added 1 commit

    added 1 commit

    • 3ae73bef - fix minimum auth for changelog + simple changelog tests

    Compare with previous version

  • All your concerns should have been taken care of now. Let me know if you find anything else...

  • Jan Hák
    Jan Hák @jhak started a thread on the diff
42 assert response.status_code == 200
43
44 def test_get_zones_changelog(auth):
45 ## Test API no-token
46
47 # try to get all zones
48 response = auth.get('/zones/changelog')
49 assert response.status_code == 401
50
51 # try to get all zones
52 response = auth.get('/zones/changelog/test')
53 assert response.status_code == 401
54
55 ## Test API with auth
56 auth.login('test_admin')
57
  • Maintainer

    Add here some zone record changes. Add and remove few records so changelogs are not empty.

    E.g. response = auth.put(f'/zones/test/records/test-dns1/A/1.1.1.1')

  • Please register or sign in to reply
  • Jan Hák
    Jan Hák @jhak started a thread on the diff
  • 95 # Get arguments
    96 if not zone:
    97 zone = get_argument('zone')
    98
    99 # Get data
    100 try:
    101 loglines=[]
    102 full_path_audit_log = current_app.config['audit-log']
    103 dir_path, file_name = os.path.split(current_app.config['audit-log'])
    104 # iterate each file in a directory
    105 for file in sorted(Path(dir_path).iterdir(), key=os.path.getmtime):
    106 cur_path = os.path.join(dir_path, file)
    107 if os.path.isfile(cur_path) and file_name in cur_path:
    108 with open(cur_path, 'r') as file:
    109 for line in file.readlines():
    110 if re.match("^(?:(?!(logged in|failed to log in|Registered user|Removed user)).)*$", line) is not None:
    • Maintainer

      This match is too complicated for no reason. if re.match("^\s*[+-]", line): seems enough for me

    • We want the starting line(s) containing: [2024-10-12 04:13:34,581]: User 'pettai' deleted record in zone 'example.net.'

      to be included in the returned change log as well, so we know who to cherish or blame

      Edited by pettai
    • Maintainer

      Oh, I see..

      I'll have to think about it.. Nice would be join username right into JSON record (comment I added to the return line further)..

      But at first glance seems a bit difficult, maybe after some format changes in audit log..

      Edited by Jan Hák
    • Please register or sign in to reply
  • Jan Hák
    Jan Hák @jhak started a thread on the diff
  • 107 if os.path.isfile(cur_path) and file_name in cur_path:
    108 with open(cur_path, 'r') as file:
    109 for line in file.readlines():
    110 if re.match("^(?:(?!(logged in|failed to log in|Registered user|Removed user)).)*$", line) is not None:
    111 if zone is None:
    112 loglines += line
    113 elif zone in line:
    114 loglines += line
    115
    116 except FileNotFoundError as e:
    117 abort(404, e.strerror)
    118
    119 changelog = ""
    120 changelog = changelog.join(loglines)
    121
    122 return changelog
    • Maintainer

      Returned data should be JSON. Are you able to find some way to serialize it into JSON or should I suggest something??

    • Please suggest if you have ideas, but it's quite handy to get the "diff" format for readability. I've never seen that in JSONified output anywhere...

      Edited by pettai
    • Maintainer

      I wouldn't have a problem if you returned it as a list of lines.

      Maybe we could add some username (comment above) so you could blame the author, but a list of lines is where I would start..

    • regarding username, could you give an example of how you would like it to look like? (Note that the timestamp is useful too for a timeline of the changes that where made)

    • Maintainer

      I have still not I'm still not sure about the final form... The problem is that the two types of lines have quite different formats... My first thought was just something like this:

      [
      {"user": "test_admin", "message":"[2024-10-15 14:03:28,250]: User 'test_admin' add record(s) to zone 'test'"},
      {"user": "test_admin", "message":"-\ttest.\t3600\tSOA\tdns1.test. hostmaster.test. 1 21600 3600 604800 86400'"},
      ...
      ]

      Of course any feature like username, date, etc. in a separate entry would be a great feature to help filtering. But it depends what would be possible with actual format and what wouldn't.

      Btw. I'm inclined to change the audit log format to better achieve this.

    • You can skip: {"user": "test_admin", "message":"[2024-10-15 14:03:28,250]: User 'test_admin' add record(s) to zone 'test'"},

      if the log format would include timestamp(s) {"timestamp": 1728953470, "user": "test_admin", "message":"-\ttest.\t3600\tSOA\tdns1.test. hostmaster.test. 1 21600 3600 604800 86400'"},

      etc.

    • Please register or sign in to reply
  • Jan Hák
    Jan Hák @jhak started a thread on the diff
  • 98
    99 # Get data
    100 try:
    101 loglines=[]
    102 full_path_audit_log = current_app.config['audit-log']
    103 dir_path, file_name = os.path.split(current_app.config['audit-log'])
    104 # iterate each file in a directory
    105 for file in sorted(Path(dir_path).iterdir(), key=os.path.getmtime):
    106 cur_path = os.path.join(dir_path, file)
    107 if os.path.isfile(cur_path) and file_name in cur_path:
    108 with open(cur_path, 'r') as file:
    109 for line in file.readlines():
    110 if re.match("^(?:(?!(logged in|failed to log in|Registered user|Removed user)).)*$", line) is not None:
    111 if zone is None:
    112 loglines += line
    113 elif zone in line:
    • Maintainer

      It seems to work only for "text" messages (messages like user xyz added/removed a record), but not for zone record changes (line starting with +-).

      In my opinion, there is no way to decide if the change happened in the specified zone or in another... Because the record doesn't need to contain the zone name at all.

    • the re.match pattern is an inverse match, so it filter out all known log messages that doesn't concern the actual changes. (basically match "!auth log messages").

      Another approach is to change the audit log format so it becomes easier to match on auth event and change log events

    • Maintainer

      yeah, I know, I was talking about elif zone in line: line.. This will not pass "record change lines" (lines starting with +-) when zone is specified, but zone name is not in the record name string..

      Change of format seems inevitable right now..

      Edited by Jan Hák
    • ah, I see. The reason I wrote it like that was (to my knowledge), knot zone records are always written as FQDN. AFAICT, all our zones have all RRs in FQDN naming standard (I think it's a nice feature using absolute names rather than relative names). I realize this might not be true for all installations of knot or something you can change as a setting in knot?

    • Maintainer

      I could be wrong, I wasn't aware we always return FQDN, I'll have to look..

      But still, if I take it from the opposite side (the record name will contain the foreign zone name), it will pass, even if it is not a change to the specified zone.

      Edited by Jan Hák
    • Please register or sign in to reply
    Please register or sign in to reply
    Loading