mirror of
https://github.com/netbirdio/plugins.git
synced 2026-05-22 18:44:07 -07:00
dns/bind: revamp logging (#3223)
This commit is contained in:
@@ -11,6 +11,7 @@ Plugin Changelog
|
||||
|
||||
1.25
|
||||
|
||||
* Revamp the logging page with proper columns (contributed by Robbert Rijkse)
|
||||
* Update base to BIND 9.18
|
||||
|
||||
1.24
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright (C) 2022 Robbert Rijkse
|
||||
* Copyright (C) 2021 Frank Wall
|
||||
* Copyright (C) 2015 Deciso B.V.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OPNsense\Bind;
|
||||
|
||||
/**
|
||||
* Class LogsController
|
||||
* @package OPNsense\Bind
|
||||
*/
|
||||
class LogsController extends \OPNsense\Base\IndexController
|
||||
{
|
||||
public function indexAction()
|
||||
{
|
||||
// choose template
|
||||
$this->view->pick('OPNsense/Bind/logs');
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,11 @@
|
||||
<Services>
|
||||
<BIND cssClass="fa fa-tags">
|
||||
<Configuration url="/ui/bind/general/index" order="10"/>
|
||||
<GeneralLog VisibleName="Log / General" order="20" url="/ui/diagnostics/log/named/named"/>
|
||||
<QueryLog VisibleName="Log / Queries" order="30" url="/ui/diagnostics/log/named/query"/>
|
||||
<BlockedLog VisibleName="Log / Blocked" order="40" url="/ui/diagnostics/log/named/rpz"/>
|
||||
<Logs VisibleName="Log Files" order="60" url="/ui/bind/logs">
|
||||
<GeneralLog VisibleName="General" order="10" url="/ui/bind/logs"/>
|
||||
<QueryLog VisibleName="Query" order="20" url="/ui/bind/log/core/query/"/>
|
||||
<BlockedLog VisibleName="Blocked" order="30" url="/ui/bind/log/core/blocked/"/>
|
||||
</Logs>
|
||||
</BIND>
|
||||
</Services>
|
||||
</menu>
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
{#
|
||||
# Copyright (c) 2022 Robbert Rijkse
|
||||
# Copyright (c) 2019 Deciso B.V.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#}
|
||||
|
||||
<script>
|
||||
$( document ).ready(function() {
|
||||
// get entries from named/named.log
|
||||
let grid_generallog = $("#grid-generallog").UIBootgrid({
|
||||
options:{
|
||||
sorting:false,
|
||||
rowSelect: false,
|
||||
selection: false,
|
||||
rowCount:[20,50,100,200,500,1000,-1],
|
||||
},
|
||||
search:'/api/diagnostics/log/named/named'
|
||||
});
|
||||
|
||||
// get entries from named/query.log
|
||||
let grid_querylog = $("#grid-querylog").UIBootgrid({
|
||||
options:{
|
||||
sorting:false,
|
||||
rowSelect: false,
|
||||
selection: false,
|
||||
rowCount:[20,50,100,200,500,1000,-1],
|
||||
},
|
||||
search:'/api/diagnostics/log/named/query'
|
||||
});
|
||||
|
||||
// get entries from named/rpz.log
|
||||
let grid_blockedlog = $("#grid-blockedlog").UIBootgrid({
|
||||
options:{
|
||||
sorting:false,
|
||||
rowSelect: false,
|
||||
selection: false,
|
||||
rowCount:[20,50,100,200,500,1000,-1],
|
||||
},
|
||||
search:'/api/diagnostics/log/named/rpz'
|
||||
});
|
||||
|
||||
grid_generallog.on("loaded.rs.jquery.bootgrid", function(){
|
||||
$(".action-page").click(function(event){
|
||||
event.preventDefault();
|
||||
$("#grid-generallog").bootgrid("search", "");
|
||||
let new_page = parseInt((parseInt($(this).data('row-id')) / $("#grid-log").bootgrid("getRowCount")))+1;
|
||||
$("input.search-field").val("");
|
||||
// XXX: a bit ugly, but clearing the filter triggers a load event.
|
||||
setTimeout(function(){
|
||||
$("ul.pagination > li:last > a").data('page', new_page).click();
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
|
||||
grid_querylog.on("loaded.rs.jquery.bootgrid", function(){
|
||||
$(".action-page").click(function(event){
|
||||
event.preventDefault();
|
||||
$("#grid-querylog").bootgrid("search", "");
|
||||
let new_page = parseInt((parseInt($(this).data('row-id')) / $("#grid-log").bootgrid("getRowCount")))+1;
|
||||
$("input.search-field").val("");
|
||||
// XXX: a bit ugly, but clearing the filter triggers a load event.
|
||||
setTimeout(function(){
|
||||
$("ul.pagination > li:last > a").data('page', new_page).click();
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
|
||||
grid_blockedlog.on("loaded.rs.jquery.bootgrid", function(){
|
||||
$(".action-page").click(function(event){
|
||||
event.preventDefault();
|
||||
$("#grid-blockedlog").bootgrid("search", "");
|
||||
let new_page = parseInt((parseInt($(this).data('row-id')) / $("#grid-log").bootgrid("getRowCount")))+1;
|
||||
$("input.search-field").val("");
|
||||
// XXX: a bit ugly, but clearing the filter triggers a load event.
|
||||
setTimeout(function(){
|
||||
$("ul.pagination > li:last > a").data('page', new_page).click();
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<ul class="nav nav-tabs" role="tablist" id="maintabs">
|
||||
<li class="active"><a data-toggle="tab" href="#generallog"><b>{{ lang._('General Log') }}</b></a></li>
|
||||
<li><a data-toggle="tab" href="#querylog">{{ lang._('Query Log') }}</a></li>
|
||||
<li><a data-toggle="tab" href="#blockedlog">{{ lang._('Blocked Log') }}</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="content-box tab-content">
|
||||
|
||||
<div id="generallog" class="tab-pane fade in active">
|
||||
<div class="content-box" style="padding-bottom: 1.5em;">
|
||||
<div class="col-sm-12">
|
||||
<table id="grid-generallog" class="table table-condensed table-hover table-striped table-responsive" data-store-selection="true">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-column-id="timestamp" data-width="15em" data-type="string">{{ lang._('Date') }}</th>
|
||||
<th data-column-id="process_name" data-width="9em" data-type="string">{{ lang._('Type') }}</th>
|
||||
<th data-column-id="severity" data-width="11em" data-type="string">{{ lang._('Level')}}</th>
|
||||
<th data-column-id="line" data-type="string">{{ lang._('Line') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="querylog" class="tab-pane fade">
|
||||
<div class="content-box" style="padding-bottom: 1.5em;">
|
||||
<div class="col-sm-12">
|
||||
<table id="grid-querylog" class="table table-condensed table-hover table-striped table-responsive" data-store-selection="true">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-column-id="timestamp" data-width="15em" data-type="string">{{ lang._('Date') }}</th>
|
||||
<th data-column-id="process_name" data-width="11em" data-type="string">{{ lang._('Client') }}</th>
|
||||
<th data-column-id="pid" data-width="11em" data-type="string">{{ lang._('IP Address') }}</th>
|
||||
<th data-column-id="facility" data-width="20em" data-type="string">{{ lang._('Record') }}</th>
|
||||
<th data-column-id="line" data-type="string">{{ lang._('Query') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="blockedlog" class="tab-pane fade">
|
||||
<div class="content-box" style="padding-bottom: 1.5em;">
|
||||
<div class="col-sm-12">
|
||||
<table id="grid-blockedlog" class="table table-condensed table-hover table-striped table-responsive" data-store-selection="true">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-column-id="timestamp" data-width="15em" data-type="string">{{ lang._('Date') }}</th>
|
||||
<th data-column-id="process_name" data-width="11em" data-type="string">{{ lang._('Client') }}</th>
|
||||
<th data-column-id="pid" data-width="11em" data-type="string">{{ lang._('IP Address') }}</th>
|
||||
<th data-column-id="facility" data-width="20em" data-type="string">{{ lang._('Record') }}</th>
|
||||
<th data-column-id="line" data-type="string">{{ lang._('Query') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,112 @@
|
||||
"""
|
||||
Copyright (c) 2022 Robbert Rijkse
|
||||
All rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
"""
|
||||
import datetime
|
||||
import re
|
||||
from . import NewBaseLogFormat
|
||||
|
||||
class BindGeneralLogFormat(NewBaseLogFormat):
|
||||
def __init__(self, filename):
|
||||
super().__init__(filename)
|
||||
self._priority = 1
|
||||
self._parts = list()
|
||||
|
||||
def match(self, line):
|
||||
return self._filename.find('named/named.log') > -1
|
||||
|
||||
def set_line(self, line):
|
||||
super().set_line(line)
|
||||
self._parts = self._line.split(maxsplit=4)
|
||||
|
||||
@property
|
||||
def timestamp(self):
|
||||
# bind format return actual log data
|
||||
ts = datetime.datetime.strptime(f"{self._parts[0]} {self._parts[1]}", "%d-%b-%Y %H:%M:%S.%f")
|
||||
return ts.isoformat()
|
||||
|
||||
@property
|
||||
def severity(self):
|
||||
# Grab the log level
|
||||
severity = self._parts[3].strip(":")
|
||||
options = {
|
||||
"critical": 2,
|
||||
"error": 3,
|
||||
"warning": 4,
|
||||
"notice": 5,
|
||||
"info": 6,
|
||||
"debug": 7,
|
||||
"dynamic": 7
|
||||
}
|
||||
if severity in options:
|
||||
return options[severity]
|
||||
return None
|
||||
|
||||
@property
|
||||
def process_name(self):
|
||||
# Grab the type of log message
|
||||
return self._parts[2].strip(":")
|
||||
|
||||
@property
|
||||
def line(self):
|
||||
# Only grab the left over message
|
||||
return self._parts[4].strip()
|
||||
|
||||
class BindQueryLogFormat(NewBaseLogFormat):
|
||||
def __init__(self, filename):
|
||||
super().__init__(filename)
|
||||
self._priority = 1
|
||||
self._parts = list()
|
||||
|
||||
def match(self, line):
|
||||
return self._filename.find('named/query.log') > -1 or self._filename.find('named/rpz.log') > -1
|
||||
|
||||
def set_line(self, line):
|
||||
super().set_line(line)
|
||||
self._parts = self._line.split(maxsplit=7)
|
||||
|
||||
@property
|
||||
def timestamp(self):
|
||||
# bind format return actual log data
|
||||
ts = datetime.datetime.strptime(f"{self._parts[0]} {self._parts[1]}", "%d-%b-%Y %H:%M:%S.%f")
|
||||
return ts.isoformat()
|
||||
|
||||
@property
|
||||
def pid(self):
|
||||
# Grab the IP and Port number of the client
|
||||
# pid is used for this because you can't define custom names
|
||||
return self._parts[4].strip()
|
||||
|
||||
@property
|
||||
def facility(self):
|
||||
# Grab the record the query was for
|
||||
# facility is used for this because you can't define custom names
|
||||
return self._parts[5].strip()[1:-2]
|
||||
|
||||
@property
|
||||
def process_name(self):
|
||||
# Grab the client memory ID
|
||||
# process_name is used for this because you can't define custom names
|
||||
return self._parts[3].strip()
|
||||
|
||||
@property
|
||||
def line(self):
|
||||
# Only grab the left over message
|
||||
return self._parts[7].strip()
|
||||
Reference in New Issue
Block a user