GUI editor to tame mod_security rules

⌈⌋ branch:  modseccfg


Check-in [0f1d190f43]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add basic plugin_load(), generilize `add_menu()` into `init()`
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 0f1d190f43bf7a0ddff2fc85e874507b9bf3b5b77e072b0489f47f5402ecbe12
User & Date: mario 2020-12-26 22:38:14
Context
2020-12-28
12:59
Add docs for logfmt1 check-in: 33aecba645 user: mario tags: trunk
2020-12-26
22:38
Add basic plugin_load(), generilize `add_menu()` into `init()` check-in: 0f1d190f43 user: mario tags: trunk
22:37
Use radiobuttons for exclusive actions (deny/block), move pause into params section, fix SecRuleCombined to use deepcopy (instead of duplicating flags in global rule) check-in: 129ce9633e user: mario tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to README.md.

1
2
3
4
5
6
7
8
..
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
## modseccfg

 * GUI to define SecRuleRemoveById settings on a vhost-basis
 * Tries to suggest false positives from error and audit logs
 * And configure mod_security and CoreRuleSet variables.
 * Runs locally, via `ssh -X` forwarding, or per `modseccfg ssh:/`
   remoting.

................................................................................

### from `project` import `meta`

| meta           | info                                                            |
|:---------------|:----------------------------------------------------------------|
| depends        | python:[pysimplegui](https://pypi.org/project/PySimpleGUI/), python:[pluginconf](https://pypi.org/project/pluginconf/), python:[tkinter](https://docs.python.org/3/library/tkinter.html), sys:[mod-security](https://packages.debian.org/sid/libapache2-mod-security2), bin:[sshfs](https://packages.debian.org/sid/sshfs)  |
| compat         | Python ≥3.6, Apache 2.x, mod_security 2.9.x, CRS 3.x, BSD/Linux |
| compliancy     | xdg, pluginspec, !pep8, !logfmt, !desktop, !xdnd, !mallard, sshrc, !netrc, !http_proxy, !nobackup, !PKG_INFO, !releases.json, !doap, !packfile |
| system usage   | opportune shell invokes (sshfs, find, cat, dpkg, xdg-open)      |
| paths          | ~/mnt/,  ~/backup-config/, ~/.config/modseccfg/                 |
| testing        | few data-driven assertions, only manual UI and usage tests      |
| docs           | minimal wiki, news, no man page                                 |
| activity       | burst, temporary                                                |
| state          | beta                                                            |
| support        | `None`                                                          |
| contrib        | mail, fossil DVCS (create an account or send bundles)           |
| announce       | [freshcode.club](https://freshcode.club/projects/modseccfg), pypi.org    |


|







 







|











1
2
3
4
5
6
7
8
..
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
## mod_security config GUI

 * GUI to define SecRuleRemoveById settings on a vhost-basis
 * Tries to suggest false positives from error and audit logs
 * And configure mod_security and CoreRuleSet variables.
 * Runs locally, via `ssh -X` forwarding, or per `modseccfg ssh:/`
   remoting.

................................................................................

### from `project` import `meta`

| meta           | info                                                            |
|:---------------|:----------------------------------------------------------------|
| depends        | python:[pysimplegui](https://pypi.org/project/PySimpleGUI/), python:[pluginconf](https://pypi.org/project/pluginconf/), python:[tkinter](https://docs.python.org/3/library/tkinter.html), sys:[mod-security](https://packages.debian.org/sid/libapache2-mod-security2), bin:[sshfs](https://packages.debian.org/sid/sshfs)  |
| compat         | Python ≥3.6, Apache 2.x, mod_security 2.9.x, CRS 3.x, BSD/Linux |
| compliancy     | xdg, pluginspec, !pep8, logfmt, !desktop, !xdnd, !mallard, sshrc, !netrc, !http_proxy, !nobackup, !PKG_INFO, !releases.json, !doap, !packfile |
| system usage   | opportune shell invokes (sshfs, find, cat, dpkg, xdg-open)      |
| paths          | ~/mnt/,  ~/backup-config/, ~/.config/modseccfg/                 |
| testing        | few data-driven assertions, only manual UI and usage tests      |
| docs           | minimal wiki, news, no man page                                 |
| activity       | burst, temporary                                                |
| state          | beta                                                            |
| support        | `None`                                                          |
| contrib        | mail, fossil DVCS (create an account or send bundles)           |
| announce       | [freshcode.club](https://freshcode.club/projects/modseccfg), pypi.org    |


Changes to modseccfg/install/__init__.py.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from modseccfg.utils import srvroot, conf
import PySimpleGUI as sg

dir = re.sub("[^/]+$", "", __file__)


# hook: mainwindow.add_menu
def add_menu(menu):
    print("\n")
    m = menu[0][1] # File
    i = 5 # m.index("Test")
    m.insert(i, ls())
    m.insert(i, "Install")
    
# find files
def ls():







|
<







15
16
17
18
19
20
21
22

23
24
25
26
27
28
29
from modseccfg.utils import srvroot, conf
import PySimpleGUI as sg

dir = re.sub("[^/]+$", "", __file__)


# hook: mainwindow.add_menu
def init(menu, **kwargs):

    m = menu[0][1] # File
    i = 5 # m.index("Test")
    m.insert(i, ls())
    m.insert(i, "Install")
    
# find files
def ls():

Changes to modseccfg/mainwindow.py.

168
169
170
171
172
173
174
175

176
177
178

179
180
181
182
183
184
185
#-- GUI event loop and handlers
class gui_event_handler:

    # prepare window
    def __init__(self):
        #-- hooks
        log.init.info("scan plugins")
        self.plugins = [recipe, install, scripts]

        for mod in self.plugins:
            if hasattr(mod, "add_menu"):
                mod.add_menu(menu)

        
        #-- build
        gui_event_handler.mainwindow = self
        log.init.info("build window")
        self.w = sg.Window(
            title=f"mod_security config {utils.srvroot.srv}", layout=layout, font="Sans 12",
            size=(1200,825), return_keyboard_events=conf.keyboard_binds, resizable=1, icon=icons.icon







|
>

|
<
>







168
169
170
171
172
173
174
175
176
177
178

179
180
181
182
183
184
185
186
#-- GUI event loop and handlers
class gui_event_handler:

    # prepare window
    def __init__(self):
        #-- hooks
        log.init.info("scan plugins")
        self.plugins = [recipe, install, scripts] + utils.load_plugins()
        #print(self.plugins)
        for mod in self.plugins:
            if hasattr(mod, "init"):

                mod.init(menu=menu, layout=layout, mainwindow=self)
        
        #-- build
        gui_event_handler.mainwindow = self
        log.init.info("build window")
        self.w = sg.Window(
            title=f"mod_security config {utils.srvroot.srv}", layout=layout, font="Sans 12",
            size=(1200,825), return_keyboard_events=conf.keyboard_binds, resizable=1, icon=icons.icon

Changes to modseccfg/scripts/__init__.py.

204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
            scripts[meta["title"]] = meta
            c = meta.get("category").title()
                          # create and/or append
            menu[c] = menu.get(c, []) + [meta["title"]]
    return menu            

# inject into mainwindow.menu/layout
def add_menu(menu):
    if not conf.get("script_sep_menus"):
        menu = menu[3][1] # submenus under ["Log", [→]]
    for group, files in scan_files().items():
        add = [group, sorted(files)]
        if conf.get("script_sep_menus"): add = [add]
        menu += add

# satisfy event lookup from mainwindow
def has(title):
    return title in scripts








|











204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
            scripts[meta["title"]] = meta
            c = meta.get("category").title()
                          # create and/or append
            menu[c] = menu.get(c, []) + [meta["title"]]
    return menu            

# inject into mainwindow.menu/layout
def init(menu, **kwargs):
    if not conf.get("script_sep_menus"):
        menu = menu[3][1] # submenus under ["Log", [→]]
    for group, files in scan_files().items():
        add = [group, sorted(files)]
        if conf.get("script_sep_menus"): add = [add]
        menu += add

# satisfy event lookup from mainwindow
def has(title):
    return title in scripts

Changes to modseccfg/utils.py.

25
26
27
28
29
30
31

32
33
34
35
36
37
38
39
40
41
42
43
44
45
..
85
86
87
88
89
90
91











92
93
94
95
96
97
98

import sys
import os
import pathlib
import re
import functools
import subprocess

import atexit
import json
import pluginconf, pluginconf.gui
import appdirs
try: import frosch; frosch.hook()
except: pass
import logging, inspect


#-- dict mirrored into object properties
class DictObj(dict):
    def __init__(self, dict={}):
        self.__dict__ = self
        self.update(dict)
................................................................................

#-- plugin lookup
pluginconf.module_base = __name__
pluginconf.plugin_base = [__package__]
for module,meta in pluginconf.all_plugin_meta().items():
    pluginconf.add_plugin_defaults(conf, conf.plugins, meta, module)













#-- path
def expandpath(dir):
    return str(pathlib.Path(dir).expanduser())

#-- @decorator to override module function
@functools.singledispatch







>






|







 







>
>
>
>
>
>
>
>
>
>
>







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
..
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110

import sys
import os
import pathlib
import re
import functools
import subprocess
import importlib
import atexit
import json
import pluginconf, pluginconf.gui
import appdirs
try: import frosch; frosch.hook()
except: pass
import logging, inspect, traceback


#-- dict mirrored into object properties
class DictObj(dict):
    def __init__(self, dict={}):
        self.__dict__ = self
        self.update(dict)
................................................................................

#-- plugin lookup
pluginconf.module_base = __name__
pluginconf.plugin_base = [__package__]
for module,meta in pluginconf.all_plugin_meta().items():
    pluginconf.add_plugin_defaults(conf, conf.plugins, meta, module)

# invoked from main
def load_plugins():
    add = []
    for name, state in conf.plugins.items():
        module_name = f"modseccfg.{name}"
        if state and not name.startswith("_") and not module_name in sys.modules:
            try:
                add.append(importlib.import_module(module_name))
            except:
                log.error(traceback.format_exc())
    return add

#-- path
def expandpath(dir):
    return str(pathlib.Path(dir).expanduser())

#-- @decorator to override module function
@functools.singledispatch

Changes to requirements.txt.

1
2
3

4
5
pysimplegui
python3-tk
pluginconf

appdirs
frosch



>


1
2
3
4
5
6
pysimplegui
python3-tk
pluginconf
logfmt1
appdirs
frosch