Check-in [7c310d47ec]
Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Build data bag() from log line. Complete some comments for recipies, and fix Macro usage with NEWID generator. New recipes for Cloudflare and Log formats (incomplete). |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
7c310d47ec55caf8bf46baf5c4d127d2 |
User & Date: | mario 2020-11-25 18:45:18 |
Original Comment: | Build data bad() from log line. Complete some comments for recipies, and fix Macro usage with NEWID generator. New recipes for Cloudflare and Log formats (incomplete). |
Context
2020-11-26
| ||
00:26 | Release as 0.4.0 check-in: 393a1fb162 user: mario tags: trunk, 0.4.0 | |
2020-11-25
| ||
18:45 | Build data bag() from log line. Complete some comments for recipies, and fix Macro usage with NEWID generator. New recipes for Cloudflare and Log formats (incomplete). check-in: 7c310d47ec user: mario tags: trunk | |
18:43 | Introduce vhost.warn message (for writeability or multiple vhost warnings in mainwindow.status bar) check-in: 2ff5b44713 user: mario tags: trunk | |
Changes
Changes to modseccfg/recipe.py.
1 2 | # api: modseccfg # encoding: utf-8 | | | | | | | > | | | | | | | > > | > > | > > | > > | | > > | > > > > > > > > > > > > > | > > > > > > | > | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | | < | < | > > | | | < < | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 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 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | # api: modseccfg # encoding: utf-8 # version: 0.2 # type: data # title: recipe # description: Apache/mod_security config examples or conversions # category: config # config: # { name: replace_rules, type: bool, value: 0, description: "try to find replacement spot, else just append" } # # Basically just blobs of text and an editor window. # [Save] will append directives to selected vhost/*.conf file. # # Some samples from: # Β· https://wiki.atomicorp.com/wiki/index.php/Mod_security # from modseccfg import utils, vhosts import PySimpleGUI as sg import re, random from textwrap import dedent class templates: locationmatch = """ <LocationMatch "/app/"> SecRuleRemoveById $id # $msg # SecRuleEngine DetectionOnly </LocationMatch> """ directory = """ <Directory "/srv/www/app/"> SecRuleRemoveById $id # $msg </Directory> """ filesmatch = """ <FilesMatch "\.php$"> SecRuleRemoveById $id # $msg </FilesMatch> """ exclude_parameter = """ # Exclude GET/POST parameter from rule # SecRuleUpdateTargetByID $id "!ARGS:param" """ rule_to_detectiononly = """ # One rule to DetectionOnly # SecRuleUpdateActionById $id "pass,status:200,log,auditlog" """ url_to_detectiononly = """ # Set one URL to DetectionOnly # SecRule REQUEST_URI "$request_uri" "phase:1,id:$rand,t:none,t:lowercase,pass,msg:'DetectionOnly for $request_uri',ctl:ruleEngine=DetectionOnly" """ exempt_remote_addr = """ # Exempt client addr from all SecRules # SecRule REMOTE_ADDR "^\\Q$remote_addr\\E$" "phase:1,id:$rand,t:none,nolog,allow,ctl:ruleEngine=Off,ctl:auditEngine=Off" """ whitelist_ip_file = """ # List of IPs from filename trigger DetectionOnly mode # SecRule REMOTE_ADDR "@pmFromFile $confn.whitelist" "phase:1,id:$rand,t:none,nolog,allow,ctl:ruleEngine=DetectionOnly" """ ip2location = """ # Use mod_ip2location or Cloudflare header # SetEnvIfExpr "req('CF-IPCountry') =~ '\w\w'" IP2LOCATION_COUNTRY_SHORT=%{HTTP_CF_IPCOUNTRY} SecRule ENV:IP2LOCATION_COUNTRY_SHORT "!^(UK|DE|FR)$" "id:$rand,deny,status:500,msg:'Req not from whitelisted country'" """ macros = """ # This directive block defines some macros, which you can use to simplify a few # SecRules exceptions. Best applied to a central *.conf file, rather# than vhost. # An `Use` directive/prefix is necessary to expand these macros. # β # Use SecRuleRemoveByPath 900410 /app/exempt/ # <IfModule mod_macro.c> <Macro NEWID $STR> # define %{ENV:NEWID} in the 50000 range; might yield duplicates SetEnvIfExpr "md5('$STR') =~ /(\d).*(\d).*(\d).*(\d)/" "NEWID=5$1$2$3$4" </Macro> <Macro SecRuleRemoveByPath $ID $PATH> Use NEWID "$ID$PATH" SecRule REQUEST_URI "@eq $PATH" "id:%{ENV:NEWID},t:none,msg:'Whitelist Β«$PATHΒ»',ctl:removeById=$ID" </Macro> </IfModule> """ apache_cloudflare_remoteip = """ # Sets REMOTE_ADDR for Apache at large. # @url https://support.cloudflare.com/hc/en-us/articles/360029696071-Orig-IPs # @bug Seemingly mod_security needs another fix. # <IfModule mod_remoteip.c> RemoteIPHeader CF-Connecting-IP RemoteIPTrustedProxy 173.245.48.0/20 RemoteIPTrustedProxy 103.21.244.0/22 RemoteIPTrustedProxy 103.22.200.0/22 RemoteIPTrustedProxy 103.31.4.0/22 RemoteIPTrustedProxy 141.101.64.0/18 RemoteIPTrustedProxy 108.162.192.0/18 RemoteIPTrustedProxy 190.93.240.0/20 RemoteIPTrustedProxy 188.114.96.0/20 RemoteIPTrustedProxy 197.234.240.0/22 RemoteIPTrustedProxy 198.41.128.0/17 RemoteIPTrustedProxy 162.158.0.0/15 RemoteIPTrustedProxy 104.16.0.0/12 RemoteIPTrustedProxy 172.64.0.0/13 RemoteIPTrustedProxy 131.0.72.0/22 RemoteIPTrustedProxy 2400:cb00::/32 RemoteIPTrustedProxy 2606:4700::/32 RemoteIPTrustedProxy 2803:f800::/32 RemoteIPTrustedProxy 2405:b500::/32 RemoteIPTrustedProxy 2405:8100::/32 RemoteIPTrustedProxy 2a06:98c0::/29 RemoteIPTrustedProxy 2c0f:f248::/32 </IfModule> """ apache_errorlog_format = """ # Extend error log w/ REQUEST_URI and somewhat standard datetime format (not quite 8601) # # β Feedback appreciated. What ought to be the post-90s Apache default? # SetEnvIf Request_URI "(^.*$)" REQ=$1 ErrorLogFormat "[%{cu}t] [%m:%l] [pid %P:tid %T] [client %a] %E: %M [request_uri %{REQ}e]" """ def ls(): return [title.replace("_", " ").title() for title in templates.__dict__.keys() if not title.startswith("__")] def has(name): return hasattr(templates, name.lower().replace(" ", "_")) def show(name, data, id=0, vhost={}): # resolve vars = bag(data) name = name.lower().replace(" ", "_") text = getattr(templates, name) if type(text) is str: text = dedent(text).lstrip() text = repl(text, vars) #@ToDo: mainwindow should supply a data bag (either full secrule entry, or params from log - depending on which is active) else: text = text(data, vars) print(data) print(text) # window w = sg.Window(title=f"Recipe '{name}'", resizable=1, layout=[ [sg.Multiline(default_text=text, key="src", size=(90,24), font="Mono 12")], [sg.Button("Save", key="save"), sg.Button("Cancel", key="cancel")] ]) event, values = w.read() print(event, values) w.close() # write β¦ if event == "save": writer.append(vhost.fn, text) # prepare vars dict from mainwindow event data + selected log line def bag(data): vars = { "id": "0", "rand": random.randrange(2000,5000), "request_uri": "/PATH", "confn": data.get("confn") } if data.get("log"): for k,v in re.findall('\[(\w+) "([^"]+)"\]', str(data["log"])): if k in ("uri", "request_line"): k = "request_uri" vars[k] = v if data.get("rule"): vars["id"] = data["rule"][0] return vars # substitute $varnames in text string def repl(text, vars): text = re.sub(r"\$(\w+)", lambda m,*k: str(vars.get(m.group(1), m.group(0))), text) return text |