Skip to content

Commit

Permalink
Merge pull request #9 from Martlark/1.0.14
Browse files Browse the repository at this point in the history
1.1.0
  • Loading branch information
Martlark authored Jul 29, 2020
2 parents d2d89d6 + aaf9d92 commit cb69ecb
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 25 deletions.
6 changes: 0 additions & 6 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,6 @@ matrix:
include:
- python: 3.7
env: TOXENV=lint
- python: 2.7
env: TOXENV=py27
- python: 3.4
env: TOXENV=py34
- python: 3.5
env: TOXENV=py35
- python: 3.6
env: TOXENV=py36

Expand Down
23 changes: 22 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,22 @@ Url patterns
Url matching match_type can be 'string' or 'regex'. String is direct match. Regex is a regex pattern.
Block networks / cidr
---------------------
Use the `block_cidr(network)` method to block a range of addresses or whole regions.
Example:
.. code:: python
ip_ban = IpBan()
app = Flask(__name__)
ip_ban.init_app(app)
# block a network in Aruba
ip_ban.block_cidr('190.220.142.104/29')
Nuisance file
-------------
Expand All @@ -159,7 +175,7 @@ that use nuisance url patterns they won't result in a block.
Load them by calling ip_ban.load_nuisances()
You can add your own nuisance yaml file by calling with the parameter file_name=.
You can add your own nuisance yaml file by calling with the parameter `file_name`.
See the nuisance.yaml file in the source for formatting and details.
Expand All @@ -179,6 +195,10 @@ This folder and secret key is also used by the persistence feature.
Only ip records using the `block`, `add` and `remove` methods or by 404; are persisted or shared. Any whitelisting or
pattern bans are not persisted/shared and must be done for each instance of your application.
The bit that shares ipc records between processes only updates during the `before_request` handler
of the Flask app. It only updates every 5 seconds at the most. If the app does no
request handling between bans then that ban record won't be shared between processes.
IP Header
---------
When running a flask app in a docker hosted environment (or similar) the ip address will be the virtual
Expand Down Expand Up @@ -216,6 +236,7 @@ Release History
---------------
1.0.13 - Remove reason= which did nothing. Add url to report table. Add more nuisances. Add release history.
1.1.0 - Add more nuisances. Add ability to block regions by using `block_cidr()`. Remove support for obsolete Python releases (2.7,3.4,3.5).
Licensing
---------
Expand Down
32 changes: 22 additions & 10 deletions flask_ipban/ip_ban.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,16 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import

import ipaddress
import os
import re
from datetime import datetime

import yaml
from flask import request, abort

from flask_ipban.ip_record import IpRecord
from flask_ipban.abuse_ipdb import AbuseIPDB
from flask_ipban.ip_record import IpRecord


class IpBan:
Expand All @@ -37,7 +36,7 @@ class IpBan:
"""

VERSION = '1.0.13'
VERSION = '1.1.0'

def __init__(self, app=None, ban_count=20, ban_seconds=3600 * 24, persist=False, record_dir=None, ipc=False,
secret_key=None, ip_header=None, abuse_IPDB_config=None):
Expand All @@ -60,6 +59,7 @@ def __init__(self, app=None, ban_count=20, ban_seconds=3600 * 24, persist=False,
self._ip_whitelist = {'127.0.0.1': True}
# self._ip_whitelist = {}
self._ip_ban_list = {}
self._cidr_entries = {}
# initialise with well known search bot links
self._url_whitelist_patterns = {
'^/.well-known/': dict(pattern=re.compile('^/.well-known'), match_type='regex'),
Expand Down Expand Up @@ -122,7 +122,7 @@ def _after_request(self, response):

def block(self, ip_list, permanent=False, no_write=False, timestamp=None, url='block'):
"""
add a list of ip address to the block list
add a list of ip addresses to the block list
:param ip_list: list of ip addresses to block
:param permanent: (optional) True=do not allow entries to expire
Expand Down Expand Up @@ -170,11 +170,11 @@ def get_ip(self):
ip = request.headers.get(self.ip_header)
return ip or request.environ.get('REMOTE_ADDR')

def test_pattern_blocklist(self, url, ip=None):
def test_pattern_blocklist(self, url='', ip=None):
"""
return true if the url or ip pattern matches an existing block
:param url: the url to check
:param url: (optional) the url to check
:param ip: (optional) an ip to check
:return:
"""
Expand All @@ -191,6 +191,13 @@ def test_pattern_blocklist(self, url, ip=None):
self._logger.warning('ip block match {}'.format(ip))
return True

if ip:
ip_address = ipaddress.ip_address(ip)
for c, c_ip in self._cidr_entries.items():
if ip_address in c_ip:
self._logger.warning(f'ip block match {ip}. CIDR: {c}')
return True

return False

def _before_request_check(self):
Expand Down Expand Up @@ -227,6 +234,9 @@ def _before_request_check(self):
self._logger.debug('IP expired from ban list {}. Url: {}'.format(ip, url))
entry['count'] = 0

def block_cidr(self, cidr):
self._cidr_entries[cidr] = ipaddress.ip_network(cidr)

def ip_whitelist_add(self, ip):
"""
add the ip to the list of ips to whitelist
Expand Down Expand Up @@ -325,9 +335,9 @@ def display(self, option='html'):
s += '</thead><tbody>\n'
for k, r in self._ip_ban_list.items():
s += '<tr><td>{}</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td></tr>\n'.format(k, r['count'],
r.get('permanent', ''),
r.get('url', ''),
r['timestamp'])
r.get('permanent', ''),
r.get('url', ''),
r['timestamp'])
s += '</tbody></table>'
elif option == 'csv':
for k, r in self._ip_ban_list.items():
Expand Down Expand Up @@ -448,6 +458,7 @@ def load_nuisances(self, file_name=None):
my_ip = '127.0.0.1'
test_ip_ban = IpBan(ban_count=4, ban_seconds=20, persist=True, record_dir='.flask-ip-ban-test-app',
# ip_header='X_IP_HEADER',
ipc=True,
abuse_IPDB_config=dict(
key=os.environ.get('ABUSE_IPDB_KEY'),
report=True, load=False, debug=True))
Expand Down Expand Up @@ -482,6 +493,7 @@ def route_block_perm():
def route_display():
return test_ip_ban.display()


@app.route('/clean')
def route_clean():
return str(test_ip_ban.ip_record.clean())
Expand Down
10 changes: 9 additions & 1 deletion flask_ipban/nuisance.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# limitations under the License.
#
# some common nuisances that should be blocked in a flask app
# updated: 28-02-2019
# updated: 17-Mar-2020
#
# format:
#
Expand All @@ -31,6 +31,7 @@ regex:
- .*\.asp$
- .*\.aspx$
- .*\.asx$
- .*\.bak$
- .*\.cfm$
- .*\.cf$
- .*\.cgi$
Expand All @@ -55,6 +56,7 @@ regex:
- .*\.war$
- .*\.wss$
# hacking or scanning attempts
- .*\/login
- .*/a2billing/.*
- \/.*\/(calendar|date|fortune|redirect|passwd)$
- .*?PHPSESSID=
Expand All @@ -67,10 +69,16 @@ regex:
- \/bin\/
- \/cgi-bin\/
- \/htbin\/
- mysql
- phpmyadmin
- \/scripts\/
- \/solr\/
- \/HNAP1\/
- \/WEB-INF\/
- \/wp-
- \/web.config.txt$
- \+CSCOE\+/
- ^/joomla/

string:
# all of these are hacking attempts by vulnerability scanners
Expand Down
6 changes: 6 additions & 0 deletions flask_ipban/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ def setUp(self):

self.app.route('/')(hello_world)

def test_cidr(self):
self.assertFalse(self.ip_ban.test_pattern_blocklist(ip='192.0.2.1'))
self.ip_ban.block_cidr('192.0.2.0/28')
self.assertTrue(self.ip_ban.test_pattern_blocklist(ip='192.0.2.1'))
self.assertFalse(self.ip_ban.test_pattern_blocklist(ip='203.0.2.1'))

def testAddRemoveIpWhitelist(self):
self.assertEqual(self.ip_ban.ip_whitelist_add(localhost), 1)
for x in range(self.ip_ban.ban_count * 2):
Expand Down
4 changes: 2 additions & 2 deletions pypar.commands.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ pip install twine

python setup.py sdist bdist_wheel
#
twine check dist/flask_ipban-1.0.13*
twine check dist/flask_ipban-1.1.0*
# test
# pip install -e .
# twine upload --repository-url https://test.pypi.org/legacy/ dist/* -u martlark
# prod pypi
# add release in git hub to match the version
twine upload dist/flask_ipban-1.0.13* -u martlark
twine upload dist/flask_ipban-1.1.0* -u martlark
6 changes: 1 addition & 5 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from codecs import open
from setuptools import setup

VERSION = '1.0.13'
VERSION = '1.1.0'
LONG_DESCRIPTION = open('README.rst', 'r', encoding='utf-8').read()

setup(
Expand Down Expand Up @@ -44,11 +44,7 @@
'License :: OSI Approved :: Apache Software License',

'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
Expand Down

0 comments on commit cb69ecb

Please sign in to comment.