mirror of
https://github.com/pavel-odintsov/fastnetmon
synced 2024-09-22 14:01:20 +02:00
commit
cb459556ac
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
*.pyc
|
||||
__pycache__
|
||||
*.DS_Store
|
5
src/a10_plugin/.gitignore
vendored
Normal file
5
src/a10_plugin/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
*.py
|
||||
*.python
|
||||
*.egg
|
||||
*.egg-info/
|
||||
|
68
src/a10_plugin/README.md
Normal file
68
src/a10_plugin/README.md
Normal file
@ -0,0 +1,68 @@
|
||||
Fastnetmon Plugin: A10 Networks TPS AXAPIv3 integration for FastNetMon
|
||||
|
||||
This script connect to A10 TPS device to create Protected Object and announce BGP route toward upstream router upon FastNetMon ban detection.
|
||||
|
||||
1. Place both Python files at a directory that is reachable by FastNetMon
|
||||
2. Make sure both scripts are executable, i.e. "chmod +x a10.py fastnetmon_a10_v0.2.py"
|
||||
3. Modify fastnetmon.conf for notification, i.e. notify_script_path = <path>/fastnetmon_a10_v0.2.py
|
||||
|
||||
Please modify the following:
|
||||
|
||||
1. A10 mitigator IP
|
||||
2. BGP Autonomous System Number
|
||||
3. Username and Password for your A10 Device. Note that you can use your own password vault or protection schema
|
||||
|
||||
For more information about A10 Networks AXAPIv3:
|
||||
https://www.a10networks.com/resources/glossary/axapi-custom-management
|
||||
|
||||
|
||||
v0.2 - Jul 7th, 2016 - initial commit
|
||||
|
||||
Author: Eric Chou ericc@a10networks.com
|
||||
|
||||
Feedback and Feature Requests are Welcomed.
|
||||
|
||||
Example Usage:
|
||||
|
||||
- Ban action:
|
||||
|
||||
```
|
||||
a10-ubuntu3:~/fastnetmon/src/a10_plugin$ sudo python fastnetmon_a10_v0.2.py "10.10.10.10" "outgoing" "111111" "ban"
|
||||
|
||||
TH4435-1#show ddos dst zone all-entries
|
||||
Legend (Rate/Limit): 'U'nlimited, 'E'xceeded, '-' Not applicable
|
||||
Legend (State) : 'W'hitelisted, 'B'lacklisted, 'P'ermitted, black'H'oled, 'I'dle, 'L'earning, 'M'onitoring, '-' Regular mode
|
||||
Zone Name / Zone Service Info | [State]| Curr Conn| Conn Rate| Pkt Rate | kBit Rate|Frag Pkt R|Sources # |Age |LockU
|
||||
| | Limit| Limit| Limit| Limit| Limit| Limit|#min| Time
|
||||
-----------------------------------------------------------------------------------------------------------------------------------
|
||||
10.10.10.10_zone [M] U U U U U 1S 0
|
||||
- U U U U U
|
||||
Displayed Entries: 1
|
||||
Displayed Services: 0
|
||||
|
||||
TH4435-1#sh run router bgp
|
||||
!Section configuration: 221 bytes
|
||||
!
|
||||
router bgp 64513
|
||||
<skip>
|
||||
network 10.10.10.10/32
|
||||
<skip>
|
||||
!
|
||||
TH4435-1#
|
||||
TH4435-1#sh run router bgp | i 10.10.10.10
|
||||
network 10.10.10.10/32
|
||||
TH4435-1#
|
||||
```
|
||||
|
||||
- Unban action:
|
||||
|
||||
a10-ubuntu3:~/fastnetmon/src/a10_plugin$ sudo python fastnetmon_a10_v0.2.py "10.10.10.10" "outgoing" "111111" "unban"
|
||||
|
||||
```
|
||||
TH4435-1#sh run router bgp | i 10.10.10.10
|
||||
TH4435-1#
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
43
src/a10_plugin/a10.py
Executable file
43
src/a10_plugin/a10.py
Executable file
@ -0,0 +1,43 @@
|
||||
|
||||
#
|
||||
# v0.1
|
||||
# ericc@a10networks.com
|
||||
#
|
||||
|
||||
import json, urllib2
|
||||
|
||||
def axapi_auth(host, username, password):
|
||||
base_uri = 'https://'+host
|
||||
auth_payload = {"credentials": {"username": username, "password": password}}
|
||||
r = axapi_action(base_uri + '/axapi/v3/auth', payload=auth_payload)
|
||||
signature = json.loads(r)['authresponse']['signature']
|
||||
return base_uri, signature
|
||||
|
||||
|
||||
def axapi_action(uri, payload='', signature='', method='POST'):
|
||||
try:
|
||||
if method == 'POST':
|
||||
req = urllib2.Request(uri)
|
||||
req.add_header('content-type', 'application/json')
|
||||
if signature:
|
||||
req.add_header('Authorization', 'A10 {0}'.format(signature))
|
||||
response = urllib2.urlopen(req, json.dumps(payload))
|
||||
elif method == 'GET':
|
||||
req = urllib2.Request(uri)
|
||||
req.add_header('content-type', 'application/json')
|
||||
if signature:
|
||||
req.add_header('Authorization', 'A10 {0}'.format(signature))
|
||||
response = urllib2.urlopen(req)
|
||||
elif method == 'DELETE':
|
||||
req = urllib2.Request(uri)
|
||||
req.add_header('content-type', 'application/json')
|
||||
req.get_method = lambda: 'DELETE'
|
||||
if signature:
|
||||
req.add_header('Authorization', 'A10 {0}'.format(signature))
|
||||
response = urllib2.urlopen(req)
|
||||
return response.read()
|
||||
except Exception as e:
|
||||
raise
|
||||
|
||||
|
||||
|
14
src/a10_plugin/configs/dns_test_server.txt
Normal file
14
src/a10_plugin/configs/dns_test_server.txt
Normal file
@ -0,0 +1,14 @@
|
||||
!
|
||||
ddos dst zone Test-Server
|
||||
ip 210.0.0.10
|
||||
operational-mode monitor
|
||||
port 53 udp
|
||||
level 0
|
||||
zone-escalation-score 1
|
||||
indicator pkt-rate
|
||||
score 50
|
||||
zone-threshold 1
|
||||
zone-violation-actions bmf_a10_script
|
||||
level 1
|
||||
!
|
||||
|
94
src/a10_plugin/fastnetmon_a10_v0.2.py
Executable file
94
src/a10_plugin/fastnetmon_a10_v0.2.py
Executable file
@ -0,0 +1,94 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
#
|
||||
# v0.2 created [ban | unban] [on ramp | off ramp action] for A10 TPS
|
||||
# v0.3 offload URI path and json_body into separate json_config files
|
||||
# Eric Chou (ericc@a10networks.com)
|
||||
#
|
||||
|
||||
import sys
|
||||
from sys import stdin
|
||||
import optparse
|
||||
import logging, json
|
||||
from a10 import axapi_auth, axapi_action
|
||||
from json_config.logoff import logoff_path
|
||||
from json_config.write_memory import write_mem_path
|
||||
from json_config.ddos_dst_zone import ddos_dst_zone_path, ddos_dst_zone
|
||||
from json_config.bgp import bgp_advertisement_path, bgp_advertisement
|
||||
|
||||
LOG_FILE = "/var/log/fastnetmon-notify.log"
|
||||
|
||||
|
||||
logger = logging.getLogger("DaemonLog")
|
||||
logger.setLevel(logging.INFO)
|
||||
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
||||
handler = logging.FileHandler(LOG_FILE)
|
||||
handler.setFormatter(formatter)
|
||||
logger.addHandler(handler)
|
||||
|
||||
|
||||
|
||||
client_ip_as_string=sys.argv[1]
|
||||
data_direction=sys.argv[2]
|
||||
pps_as_string=int(sys.argv[3])
|
||||
action=sys.argv[4]
|
||||
|
||||
logger.info(" - " . join(sys.argv))
|
||||
|
||||
|
||||
# A10 Mitigator Information
|
||||
mitigator_ip = "192.168.199.152"
|
||||
zone_name = client_ip_as_string + "_zone"
|
||||
ip_addr = client_ip_as_string
|
||||
asn="65003"
|
||||
mitigator_base_url, signature = axapi_auth(mitigator_ip, "admin", "a10")
|
||||
|
||||
|
||||
if action == "unban":
|
||||
try:
|
||||
r = axapi_action(mitigator_base_url+'/axapi/v3/router/bgp/'+asn+'/network/ip-cidr/172.31.201.2%2F32', method="DELETE", signature=signature)
|
||||
except Exception as e:
|
||||
logger.info("route not removed in unban, returned: " + str(e))
|
||||
|
||||
# Commit config
|
||||
axapi_action(mitigator_base_url+write_mem_path, signature=signature)
|
||||
# Logoff
|
||||
axapi_action(mitigator_base_url+logoff_path, signature=signature)
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
elif action == "ban" or action == "attack_details":
|
||||
|
||||
r = axapi_action(mitigator_base_url+ddos_dst_zone_path, method='GET', signature=signature)
|
||||
if zone_name in [i['zone-name'] for i in json.loads(r)['zone-list']]:
|
||||
r = axapi_action(mitigator_base_url+ddos_dst_zone_path+zone_name, method="DELETE", signature=signature)
|
||||
logger.info(str(r))
|
||||
|
||||
# A10 Mitigation On Ramp
|
||||
zone_name = client_ip_as_string + "_zone"
|
||||
ip_addr = client_ip_as_string
|
||||
returned_body = ddos_dst_zone(zone_name, ip_addr)
|
||||
try:
|
||||
r = axapi_action(mitigator_base_url+ddos_dst_zone_path, signature=signature, payload=returned_body)
|
||||
except Exception as e:
|
||||
logger("zone not created: " + str(e))
|
||||
|
||||
route_advertisement = bgp_advertisement(ip_addr)
|
||||
try:
|
||||
r = axapi_action(mitigator_base_url+bgp_advertisement_path+asn, payload=route_advertisement, signature=signature)
|
||||
except Exception as e:
|
||||
logger("route not added: " + str(e))
|
||||
|
||||
# Commit changes
|
||||
axapi_action(mitigator_base_url+write_mem_path, signature=signature)
|
||||
# Log off
|
||||
axapi_action(mitigator_base_url+logoff_path, signature=signature)
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
else:
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
|
||||
|
0
src/a10_plugin/json_config/__init__.py
Normal file
0
src/a10_plugin/json_config/__init__.py
Normal file
16
src/a10_plugin/json_config/bgp.py
Normal file
16
src/a10_plugin/json_config/bgp.py
Normal file
@ -0,0 +1,16 @@
|
||||
bgp_advertisement_path = '/axapi/v3/router/bgp/'
|
||||
|
||||
def bgp_advertisement(ip_addr):
|
||||
route_advertisement = {
|
||||
"bgp":
|
||||
{
|
||||
"network": {
|
||||
"ip-cidr-list": [
|
||||
{
|
||||
"network-ipv4-cidr":ip_addr+"/32",
|
||||
}
|
||||
]
|
||||
},
|
||||
}
|
||||
}
|
||||
return route_advertisement
|
44
src/a10_plugin/json_config/ddos_dst_zone.py
Normal file
44
src/a10_plugin/json_config/ddos_dst_zone.py
Normal file
@ -0,0 +1,44 @@
|
||||
|
||||
ddos_dst_zone_path = '/axapi/v3/ddos/dst/zone/'
|
||||
|
||||
def ddos_dst_zone(zone_name, ip_addr):
|
||||
port_num = 53
|
||||
port_protocol = 'udp'
|
||||
ddos_dst_zone_payload = {
|
||||
"zone-list": [
|
||||
{
|
||||
"zone-name":zone_name,
|
||||
"ip": [
|
||||
{
|
||||
"ip-addr":ip_addr
|
||||
}
|
||||
],
|
||||
"operational-mode":"monitor",
|
||||
"port": {
|
||||
"zone-service-list": [
|
||||
{
|
||||
"port-num":port_num,
|
||||
"protocol":port_protocol,
|
||||
"level-list": [
|
||||
{
|
||||
"level-num":"0",
|
||||
"zone-escalation-score":1,
|
||||
"indicator-list": [
|
||||
{
|
||||
"type":"pkt-rate",
|
||||
"score":50,
|
||||
"zone-threshold-num":1,
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"level-num":"1",
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
return ddos_dst_zone_payload
|
3
src/a10_plugin/json_config/logoff.py
Normal file
3
src/a10_plugin/json_config/logoff.py
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
logoff_path = '/axapi/v3/logoff'
|
||||
|
1
src/a10_plugin/json_config/write_memory.py
Normal file
1
src/a10_plugin/json_config/write_memory.py
Normal file
@ -0,0 +1 @@
|
||||
write_mem_path = '/axapi/v3/write/memory'
|
0
src/a10_plugin/tests/__init__.py
Normal file
0
src/a10_plugin/tests/__init__.py
Normal file
43
src/a10_plugin/tests/helperTests.py
Normal file
43
src/a10_plugin/tests/helperTests.py
Normal file
@ -0,0 +1,43 @@
|
||||
import unittest,sys
|
||||
sys.path.append('../')
|
||||
from a10 import axapi_auth, axapi_action
|
||||
|
||||
a10_tps = "192.168.199.152"
|
||||
username = "admin"
|
||||
password = "a10"
|
||||
hostname = "TH4435"
|
||||
|
||||
class Test_Auth(unittest.TestCase):
|
||||
|
||||
def testAssertTrue(self):
|
||||
print("Testing axapi_auth")
|
||||
try:
|
||||
mitigator_base_url, signature = axapi_auth(a10_tps, username, password)
|
||||
print("base url: ", mitigator_base_url, "Signature: ", signature)
|
||||
axapi_action(mitigator_base_url+"/axapi/v3/logoff")
|
||||
|
||||
except Exception as e:
|
||||
self.fail("Not authenticated")
|
||||
|
||||
|
||||
class Test_API_Actions(unittest.TestCase):
|
||||
|
||||
def testAssertTrue(self):
|
||||
try:
|
||||
print("Testing GET")
|
||||
mitigator_base_url, signature = axapi_auth(a10_tps, username, password)
|
||||
r = axapi_action(mitigator_base_url+"/axapi/v3/version/oper", method='GET', signature=signature)
|
||||
print(str(r))
|
||||
print("Testing POST")
|
||||
hostname_payload = {"hostname": {"value": hostname}}
|
||||
r = axapi_action(mitigator_base_url+"/axapi/v3/hostname", payload=hostname_payload, signature=signature)
|
||||
print(str(r))
|
||||
axapi_action(mitigator_base_url+"/axapi/v3/logoff")
|
||||
|
||||
except Exception as e:
|
||||
self.fail("Failed")
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user