Change log: https://roxy-wi.org/changelog.py#6_1_3
pull/334/head
Pavel Loginov 2022-08-02 11:53:32 +03:00
parent b02acc475a
commit 1ae61a35bf
21 changed files with 994 additions and 255 deletions

109
README.md
View File

@ -1,4 +1,4 @@
# ![alt text](https://roxy-wi.org/inc/images/logo_menu.png "Logo")
# ![alt text](https://roxy-wi.org/static/images/logo_menu.png "Logo")
Web interface(user-friendly web GUI, alerting, monitoring and secure) for managing HAProxy, Nginx and Keepalived servers. Leave your [feedback](https://github.com/hap-wi/roxy-wi/issues)
# Get involved
@ -9,75 +9,76 @@ Web interface(user-friendly web GUI, alerting, monitoring and secure) for managi
# Demo site
[Demo site](https://demo.roxy-wi.org) Login/password: admin/admin. Server resets every hour.
![alt text](https://roxy-wi.org/inc/images/viewstat.png "HAProxy state page")
![alt text](https://roxy-wi.org/static/images/viewstat.png "HAProxy state page")
# Features:
1. Installing and updating HAProxy, Nginx and Keepalived with Roxy-WI as a system service
1. Installing and updating HAProxy and Nginx with Roxy-WI as a Docker service
2. Installing and updating Grafana, Prometheus servers with Roxy-WI
3. Installing and updating HAProxy and Nginx exporters with Roxy-WI
4. Server provisioning on AWS, DigitalOcean and G-Core Labs
5. Downloading, updating and formatting GeoIP to the acceptable format for HAProxy with Roxy-WI
6. Dynamic change of Maxconn, Black/white lists and backend's IP address and port with saving changes to the config file
7. Configuring HAProxy, Nginx, Apache and Keepalived in a jiffy with Roxy-WI
8. Viewing and analysing the status of all Frontend/backend servers via Roxy-WI from a single control panel
9. Enabling/disabling servers through stats page without rebooting HAProxy
1. Viewing/Analysing HAProxy, Nginx and Apache logs right from the Roxy-WI web interface
1. Creating and visualizing the HAProxy workflow from Web Ui
1. Pushing Your changes to your HAProxy, Nginx, Apache and Keepalived servers with a single click via the web interface
1. Getting info on past changes, evaluating your config files and restoring the previous stable config at any time with a single click right from Web interface
1. Adding/Editing Frontend or backend servers via the web interface with a click
1. Editing the config of HAProxy, Nginx, Apache and Keepalived and push ingchanges to All Master/Slave servers by a single click
1. Adding Multiple server to ensure the Config Sync between servers
1. Managing the ports assigned to Frontend automatically
1. Evaluating the changes of recent configs pushed to HAProxy, Nginx and Keepalived instances right from the Web UI
1. Multiple User Roles support for privileged based Viewing and editing of Config
1. Creating Groups and adding/removing servers to ensure the proper identification for your HAProxy and Nginx Clusters
1. Sending notifications from Roxy-WI via Telegram, Slack and via the web interface
1. Supporting high Availability to ensure uptime to all Master slave servers configured
1. Support of SSL (including Let's Encrypt)
1. Support of SSH Key for managing multiple HAProxy, Nginx, Apache and Keepalived Servers straight from Roxy-WI
1. SYN flood protect
1. Alerting about changes of the state of HAProxy backends
1. Alerting about the state of HAProxy, Nginx, Apache and Keepalived service
1. Gathering metrics for incoming connections
1. Web acceleration settings
1. Firewall for web application
1. LDAP support
1. Keep active HAProxy, Nginx and Keepalived services
1. Possibility to hide parts of the config with tags for users with "guest" role: "HideBlockStart" and "HideBlockEnd"
1. Mobile-ready design
1. Simple port monitoring (SMON)
1. Backup HAProxy, Nginx and Keepalived config files through Roxy-WI
1. Managing OpenVPN3 as a client via Roxy-WI
2. Installing and updating HAProxy and Nginx with Roxy-WI as a Docker service
3. Installing and updating Grafana, Prometheus servers with Roxy-WI
4. Installing and updating HAProxy and Nginx exporters with Roxy-WI
5. Server provisioning on AWS, DigitalOcean and G-Core Labs
6. Downloading, updating and formatting GeoIP to the acceptable format for HAProxy with Roxy-WI
7. Dynamic change of Maxconn, Black/white lists and backend's IP address and port with saving changes to the config file
8. Configuring HAProxy, Nginx, Apache and Keepalived in a jiffy with Roxy-WI
9. Viewing and analysing the status of all Frontend/backend servers via Roxy-WI from a single control panel
10. Enabling/disabling servers through stats page without rebooting HAProxy
11. Viewing/Analysing HAProxy, Nginx and Apache logs right from the Roxy-WI web interface
12. Creating and visualizing the HAProxy workflow from Web Ui
13. Pushing Your changes to your HAProxy, Nginx, Apache and Keepalived servers with a single click via the web interface
14. Getting info on past changes, evaluating your config files and restoring the previous stable config at any time with a single click right from Web interface
15. Adding/Editing Frontend or backend servers via the web interface with a click
16. Editing the config of HAProxy, Nginx, Apache and Keepalived and push ingchanges to All Master/Slave servers by a single click
17. Adding Multiple server to ensure the Config Sync between servers
18. Managing the ports assigned to Frontend automatically
19. Evaluating the changes of recent configs pushed to HAProxy, Nginx, Apache and Keepalived instances right from the Web UI
20. Multiple User Roles support for privileged based Viewing and editing of Config
21. Creating Groups and adding/removing servers to ensure the proper identification for your HAProxy and Nginx Clusters
22. Sending notifications from Roxy-WI via Telegram, Slack, Email and via the web interface
23. Supporting high Availability to ensure uptime to all Master slave servers configured
24. Support of SSL (including Let's Encrypt)
25. Support of SSH Key for managing multiple HAProxy, Nginx, Apache and Keepalived Servers straight from Roxy-WI
26. SYN flood protect
27. Alerting about changes of the state of HAProxy backends
28. Alerting about the state of HAProxy, Nginx, Apache and Keepalived service
29. Gathering metrics for incoming connections
30. Web acceleration settings
31. Firewall for web application (WAF)
32. LDAP support
33. Keep active HAProxy, Nginx, Apache and Keepalived services
34. Possibility to hide parts of the config with tags for users with "guest" role: "HideBlockStart" and "HideBlockEnd"
35. Mobile-ready design
36. Simple port monitoring (SMON)
37. Backup HAProxy, Nginx, Apache and Keepalived config files through Roxy-WI
38. Managing OpenVPN3 as a client via Roxy-WI
![alt text](https://Roxy-WI.org/inc/images/roxy-wi-metrics.png "Merics")
![alt text](https://Roxy-WI.org/static/images/roxy-wi-metrics.png "Merics")
# Install
## RPM
### Read instruction on the official [site](https://roxy-wi.org/installation.py#rpm)
### Read instruction on the official [site](https://roxy-wi.org/installation#rpm)
## DEB
### Read instruction on the official [site](https://roxy-wi.org/installation.py#deb)
### Read instruction on the official [site](https://roxy-wi.org/installation#deb)
## Manual install
### Read instruction on the official [site](https://roxy-wi.org/installation.py#manual)
### Read instruction on the official [site](https://roxy-wi.org/installation#manual)
# OS support
Roxy-WI supports the following OSes:
1. EL7(RPM installation and manual installation). It must be "Infrastructure Server" at least. x86_64 only
2. EL8(RPM installation and manual installation). It must be "Infrastructure Server" at least. x86_64 only
3. Amazon Linux 2(RPM installation and manual installation). x86_64 only
4. Ubuntu(DEB installation and manual installation). x86_64 only
5. Other Linux distributions (manual installation only). x86_64 only
3. EL9(RPM installation and manual installation). It must be "Infrastructure Server" at least. x86_64 only
4. Amazon Linux 2(RPM installation and manual installation). x86_64 only
5. Ubuntu(DEB installation and manual installation). x86_64 only
6. Other Linux distributions (manual installation only). x86_64 only
![alt text](https://roxy-wi.org/inc/images/smon_dashboard.png "SMON area")
![alt text](https://roxy-wi.org/static/images/smon_dashboard.png "SMON area")
# Database support
@ -85,21 +86,21 @@ Default Roxy-WI use Sqlite, if you want use MySQL enable in config, and create d
### For MySQL support:
### Read instruction on the official [site](https://roxy-wi.org/installation.py#database)
### Read instruction on the official [site](https://roxy-wi.org/installation#database)
![alt text](https://roxy-wi.org/inc/images/roxy-wi-overview.webp "Overview page")
![alt text](https://roxy-wi.org/static/images/roxy-wi-overview.webp "Overview page")
# Settings
Login https://roxy-wi-server/users.py, and add: users, groups and servers. Default: admin/admin
### Read instruction on the official [site](https://roxy-wi.org/settings.py)
### Read instruction on the official [site](https://roxy-wi.org/settings)
![alt text](https://roxy-wi.org/inc/images/hapwi_overview.webp "HAProxy server overview page")
![alt text](https://roxy-wi.org/static/images/hapwi_overview.webp "HAProxy server overview page")
![alt text](https://roxy-wi.org/inc/images/add.png "Add proxy page")
![alt text](https://roxy-wi.org/static/images/add.png "Add proxy page")
@ -122,8 +123,8 @@ Do this:
$ cd /var/www/haproxy-wi/app
$ ./create_db.py
```
and check executeble py files
and check executable py files
If you see plain text, check section "Directory" in httpd conf
[Read more](https://roxy-wi.org/troubleshooting.py)
[Read more](https://roxy-wi.org/troubleshooting)

View File

@ -943,8 +943,24 @@ def update_db_v_6_1_0(**kwargs):
print("An error occurred:", e)
def update_db_v_6_1_3(**kwargs):
cursor = conn.cursor()
sql = list()
sql.append("ALTER TABLE `waf_rules` ADD COLUMN service VARCHAR ( 64 ) DEFAULT 'haproxy'")
sql.append("ALTER TABLE `waf_rules` drop CONSTRAINT serv")
sql.append("ALTER TABLE `waf_rules` ADD CONSTRAINT UNIQUE (serv, rule_name, service)")
for i in sql:
try:
cursor.execute(i)
except Exception:
pass
else:
if kwargs.get('silent') != 1:
print('Updating... DB has been updated to version 6.1.3.0')
def update_ver():
query = Version.update(version='6.1.2.0')
query = Version.update(version='6.1.3.0')
try:
query.execute()
except Exception:
@ -971,6 +987,7 @@ def update_all():
update_db_v_6_0()
update_db_v_6_0_1()
update_db_v_6_1_0()
update_db_v_6_1_3()
update_ver()
@ -993,6 +1010,7 @@ def update_all_silent():
update_db_v_5_4_3_1(silent=1)
update_db_v_6_0(silent=1)
update_db_v_6_0_1(silent=1)
update_db_v_6_1_3(silent=1)
update_ver()

View File

@ -280,10 +280,11 @@ class WafRules(BaseModel):
rule_file = CharField()
desc = TextField(null=True)
en = IntegerField(constraints=[SQL('DEFAULT 1')])
service = CharField(constraints=[SQL('haproxy')])
class Meta:
table_name = 'waf_rules'
constraints = [SQL('UNIQUE (serv, rule_name)')]
constraints = [SQL('UNIQUE (serv, rule_name, service)')]
class PortScannerSettings(BaseModel):
@ -550,10 +551,20 @@ class CheckerSetting(BaseModel):
constraints = [SQL('UNIQUE (server_id, service_id)')]
class WafNginx(BaseModel):
id = AutoField()
server_id = ForeignKeyField(Server, on_delete='Cascade')
class Meta:
table_name = 'waf_nginx'
constraints = [SQL('UNIQUE (server_id)')]
def create_tables():
with conn:
conn.create_tables([User, Server, Role, Telegram, Slack, UUID, Token, ApiToken, Groups, UserGroups, ConfigVersion,
Setting, Cred, Backup, Metrics, WafMetrics, Version, Option, SavedServer, Waf, ActionHistory,
PortScannerSettings, PortScannerPorts, PortScannerHistory, ProvidersCreds, ServiceSetting,
ProvisionedServers, MetricsHttpStatus, SMON, WafRules, Alerts, GeoipCodes, NginxMetrics,
SystemInfo, Services, UserName, GitSetting, CheckerSetting, ApacheMetrics, ProvisionParam])
SystemInfo, Services, UserName, GitSetting, CheckerSetting, ApacheMetrics, ProvisionParam,
WafNginx])

View File

@ -448,7 +448,10 @@ def get_config(server_ip, cfg, **kwargs):
):
config_path = kwargs.get('config_file_name')
elif kwargs.get("waf") or kwargs.get("service") == 'waf':
if kwargs.get("waf") == 'haproxy':
config_path = sql.get_setting('haproxy_dir') + '/waf/rules/' + kwargs.get("waf_rule_file")
elif kwargs.get("waf") == 'nginx':
config_path = sql.get_setting('nginx_dir') + '/waf/rules/' + kwargs.get("waf_rule_file")
else:
config_path = sql.get_setting('haproxy_config_path')
@ -783,14 +786,46 @@ def waf_install(server_ip):
output, error = subprocess_execute(commands[0])
if show_installation_output(error, output, service):
ssh_command(server_ip, commands, print_out="1")
sql.insert_waf_metrics_enable(server_ip, "0")
sql.insert_waf_rules(server_ip)
os.system("rm -f %s" % script)
def waf_nginx_install(server_ip):
import sql
script = "waf_nginx.sh"
proxy = sql.get_setting('proxy')
nginx_dir = sql.get_setting('nginx_dir')
service = ' WAF'
ssh_enable, ssh_user_name, ssh_user_password, ssh_key_name = return_ssh_keys_path(server_ip)
ssh_port = '22'
if ssh_enable == 0:
ssh_key_name = ''
os.system("cp scripts/%s ." % script)
if proxy is not None and proxy != '' and proxy != 'None':
proxy_serv = proxy
else:
proxy_serv = ''
commands = [
"chmod +x " + script + " && ./" + script + " PROXY=" + proxy_serv + " NGINX_PATH=" + nginx_dir
+ " SSH_PORT=" + ssh_port + " HOST=" + server_ip
+ " USER=" + ssh_user_name + " PASS='" + ssh_user_password + "' KEY=" + ssh_key_name
]
output, error = subprocess_execute(commands[0])
if show_installation_output(error, output, service):
sql.insert_nginx_waf_rules(server_ip)
sql.insert_waf_nginx_server(server_ip)
os.system("rm -f %s" % script)
def install_nginx(server_ip, **kwargs):
import sql
script = "install_nginx.sh"
@ -1541,30 +1576,25 @@ def check_ver():
return sql.get_ver()
def check_new_version(**kwargs):
def check_new_version(service):
import requests
import sql
current_ver = check_ver()
proxy = sql.get_setting('proxy')
res = ''
if kwargs.get('service'):
last_ver = '_' + kwargs.get('service')
else:
last_ver = ''
user_name = sql.select_user_name()
try:
if proxy is not None and proxy != '' and proxy != 'None':
proxy_dict = {"https": proxy, "http": proxy}
response = requests.get('https://roxy-wi.org/update.py?last_ver' + last_ver + '=1', timeout=1, proxies=proxy_dict)
requests.get('https://roxy-wi.org/update.py?ver_send=' + current_ver, timeout=1, proxies=proxy_dict)
response_status = requests.get('https://roxy-wi.org/update.py?user_name=' + user_name, timeout=1, proxies=proxy_dict)
response = requests.get(f'https://roxy-wi.org/version/get/{service}', timeout=1, proxies=proxy_dict)
requests.get(f'https://roxy-wi.org/version/send/{current_ver}', timeout=1, proxies=proxy_dict)
response_status = requests.get(f'https://roxy-wi.org/user-name/{user_name}', timeout=1, proxies=proxy_dict)
else:
response = requests.get('https://roxy-wi.org/update.py?last_ver' + last_ver + '=1', timeout=1)
requests.get('https://roxy-wi.org/update.py?ver_send=' + current_ver, timeout=1)
response_status = requests.get('https://roxy-wi.org/update.py?user_name=' + user_name, timeout=1)
response = requests.get(f'https://roxy-wi.org/version/get/{service}', timeout=1)
requests.get(f'https://roxy-wi.org/version/send/{current_ver}', timeout=1)
response_status = requests.get(f'https://roxy-wi.org/user-name/{user_name}', timeout=1)
res = response.content.decode(encoding='UTF-8')
try:
@ -1595,7 +1625,7 @@ def versions():
current_ver_without_dots = 0
try:
new_ver = check_new_version()
new_ver = check_new_version('roxy-wi')
new_ver_without_dots = new_ver.split('.')
new_ver_without_dots = ''.join(new_ver_without_dots)
new_ver_without_dots = new_ver_without_dots.replace('\n', '')

View File

@ -41,6 +41,9 @@ try:
except ValueError:
print('error: Your token is not valid')
sys.exit()
except Exception:
print('error: There is no token')
sys.exit()
if not sql.check_token_exists(token):
print('error: Your token has been expired')
@ -504,10 +507,29 @@ if form.getvalue('action_waf') is not None and serv is not None:
funct.is_restarted(serv, action)
funct.logging(serv, 'WAF service has been ' + action + 'ed', haproxywi=1, login=1, keep_history=1, service='haproxy')
funct.logging(serv, 'HAProxy WAF service has been ' + action + 'ed', haproxywi=1, login=1, keep_history=1, service='haproxy')
commands = ["sudo systemctl %s waf" % action]
funct.ssh_command(serv, commands)
if form.getvalue('action_waf_nginx') is not None and serv is not None:
serv = form.getvalue('serv')
action = form.getvalue('action_waf_nginx')
config_dir = funct.return_nice_path(sql.get_setting('nginx_dir'))
if action not in ('start', 'stop'):
print('error: wrong action')
sys.exit()
funct.is_restarted(serv, action)
waf_new_state = 'on' if action == 'start' else 'off'
waf_old_state = 'off' if action == 'start' else 'on'
funct.logging(serv, 'NGINX WAF service has been ' + action + 'ed', haproxywi=1, login=1, keep_history=1, service='nginx')
commands = [ f"sudo sed -i 's/modsecurity {waf_old_state}/modsecurity {waf_new_state}/g' {config_dir}nginx.conf"
f" && sudo systemctl reload nginx" ]
funct.ssh_command(serv, commands)
if form.getvalue('action_apache') is not None and serv is not None:
action = form.getvalue('action_apache')
@ -728,32 +750,44 @@ if act == "overviewwaf":
)
template = env.get_template('overivewWaf.html')
waf_service = form.getvalue('service')
servers = sql.select_servers(server=serv)
cookie = http.cookies.SimpleCookie(os.environ.get("HTTP_COOKIE"))
user_id = cookie.get('uuid')
haproxy_path = ''
config_path = ''
returned_servers = []
waf = ''
metrics_en = 0
waf_process = ''
waf_mode = ''
is_waf_on_server = 0
for server in servers:
haproxy = sql.select_haproxy(server[2])
if haproxy == 1:
haproxy_path = sql.get_setting('haproxy_dir')
if waf_service == 'haproxy':
is_waf_on_server = sql.select_haproxy(server[2])
elif waf_service == 'nginx':
is_waf_on_server = sql.select_nginx(server[2])
if is_waf_on_server == 1:
config_path = sql.get_setting(waf_service + '_dir')
if waf_service == 'haproxy':
waf = sql.select_waf_servers(server[2])
metrics_en = sql.select_waf_metrics_enable_server(server[2])
elif waf_service == 'nginx':
waf = sql.select_waf_nginx_servers(server[2])
try:
waf_len = len(waf)
except Exception:
waf_len = 0
if waf_len >= 1:
if waf_service == 'haproxy':
command = ["ps ax |grep waf/bin/modsecurity |grep -v grep |wc -l"]
elif waf_service == 'nginx':
command = ["grep 'modsecurity on' %s* --exclude-dir=waf -Rs |wc -l" % funct.return_nice_path(config_path)]
commands1 = [
"grep SecRuleEngine %s/waf/modsecurity.conf |grep -v '#' |awk '{print $2}'" % haproxy_path]
"grep SecRuleEngine %s/waf/modsecurity.conf |grep -v '#' |awk '{print $2}'" % config_path]
waf_process = funct.ssh_command(server[2], command)
waf_mode = funct.ssh_command(server[2], commands1).strip()
@ -774,7 +808,7 @@ if act == "overviewwaf":
returned_servers.append(server_status)
servers_sorted = sorted(returned_servers, key=funct.get_key)
template = template.render(service_status=servers_sorted, role=sql.get_user_role_by_uuid(user_id.value))
template = template.render(service_status=servers_sorted, role=sql.get_user_role_by_uuid(user_id.value), waf_service=waf_service)
print(template)
if act == "overviewServers":
@ -1836,7 +1870,11 @@ if form.getvalue('haproxyaddserv'):
hapver=form.getvalue('hapver'), docker=form.getvalue('docker'))
if form.getvalue('installwaf'):
service = form.getvalue('service')
if service == 'haproxy':
funct.waf_install(form.getvalue('installwaf'))
else:
funct.waf_nginx_install(form.getvalue('installwaf'))
if form.getvalue('update_roxy_wi'):
service = form.getvalue('service')
@ -2150,8 +2188,8 @@ if form.getvalue('get_lists'):
lib_path = funct.get_config_var('main', 'lib_path')
list_path = lib_path + "/" + sql.get_setting('lists_path') + "/" + form.getvalue('group') + "/" + form.getvalue('color')
lists = funct.get_files(dir=list_path, format="lst")
for list in lists:
print(list)
for l in lists:
print(l)
if form.getvalue('get_ldap_email'):
username = form.getvalue('get_ldap_email')
@ -2191,11 +2229,17 @@ if form.getvalue('get_ldap_email'):
ldap_bind.unbind()
if form.getvalue('change_waf_mode'):
waf_mode = form.getvalue('change_waf_mode')
waf_mode = funct.checkAjaxInput(form.getvalue('change_waf_mode'))
server_hostname = form.getvalue('server_hostname')
haproxy_dir = sql.get_setting('haproxy_dir')
service = funct.checkAjaxInput(form.getvalue('service'))
serv = sql.select_server_by_name(server_hostname)
commands = ["sudo sed -i 's/^SecRuleEngine.*/SecRuleEngine %s/' %s/waf/modsecurity.conf " % (waf_mode, haproxy_dir)]
if service == 'haproxy':
config_dir = sql.get_setting('haproxy_dir')
elif service == 'nginx':
config_dir = sql.get_setting('nginx_dir')
commands = ["sudo sed -i 's/^SecRuleEngine.*/SecRuleEngine %s/' %s/waf/modsecurity.conf " % (waf_mode, config_dir)]
funct.ssh_command(serv, commands)
funct.logging(serv, 'Has been changed WAF mod to ' + waf_mode, haproxywi=1, login=1)
@ -2259,6 +2303,8 @@ if form.getvalue('updateuser') is not None:
if form.getvalue('updatepassowrd') is not None:
password = form.getvalue('updatepassowrd')
username = ''
if form.getvalue('uuid'):
user_id = sql.get_user_id_by_uuid(form.getvalue('uuid'))
else:
@ -2478,6 +2524,9 @@ if form.getvalue('new_ssh'):
if form.getvalue('sshdel') is not None:
lib_path = funct.get_config_var('main', 'lib_path')
sshdel = funct.checkAjaxInput(form.getvalue('sshdel'))
name = ''
ssh_enable = 0
ssh_key_name = ''
for sshs in sql.select_ssh(id=sshdel):
ssh_enable = sshs.enable
@ -2501,6 +2550,7 @@ if form.getvalue('updatessh'):
group = form.getvalue('group')
username = form.getvalue('ssh_user')
password = form.getvalue('ssh_pass')
new_ssh_key_name = ''
if username is None:
print(error_mess)
@ -2553,10 +2603,6 @@ if form.getvalue('ssh_cert'):
ssh_keys = full_dir + name + '.pem'
try:
# cloud = sql.is_cloud()
# if cloud != '':
# key.write_private_key_file(ssh_keys, password=cloud)
# else:
key.write_private_key_file(ssh_keys)
except Exception as e:
print('error: Cannot save SSH key file: ', str(e))
@ -2829,17 +2875,17 @@ if form.getvalue('showBytes') is not None:
port = sql.get_setting('haproxy_sock_port')
bin_bout = []
cmd = "echo 'show stat' |nc {} {} |cut -d ',' -f 1-2,9|grep -E '[0-9]'|awk -F',' '{{sum+=$3;}}END{{print sum;}}'".format(serv, port)
bin, stderr = funct.subprocess_execute(cmd)
bin_bout.append(bin[0])
bit_in, stderr = funct.subprocess_execute(cmd)
bin_bout.append(bit_in[0])
cmd = "echo 'show stat' |nc {} {} |cut -d ',' -f 1-2,10|grep -E '[0-9]'|awk -F',' '{{sum+=$3;}}END{{print sum;}}'".format(serv, port)
bin, stderr = funct.subprocess_execute(cmd)
bin_bout.append(bin[0])
bout, stderr = funct.subprocess_execute(cmd)
bin_bout.append(bout[0])
cmd = "echo 'show stat' |nc {} {} |cut -d ',' -f 1-2,5|grep -E '[0-9]'|awk -F',' '{{sum+=$3;}}END{{print sum;}}'".format(serv, port)
bin, stderr = funct.subprocess_execute(cmd)
bin_bout.append(bin[0])
cin, stderr = funct.subprocess_execute(cmd)
bin_bout.append(cin[0])
cmd = "echo 'show stat' |nc {} {} |cut -d ',' -f 1-2,8|grep -E '[0-9]'|awk -F',' '{{sum+=$3;}}END{{print sum;}}'".format(serv, port)
bin, stderr = funct.subprocess_execute(cmd)
bin_bout.append(bin[0])
cout, stderr = funct.subprocess_execute(cmd)
bin_bout.append(cout[0])
from jinja2 import Environment, FileSystemLoader
@ -2883,7 +2929,8 @@ if form.getvalue('waf_rule_id'):
haproxy_path = sql.get_setting('haproxy_dir')
rule_file = sql.select_waf_rule_by_id(rule_id)
conf_file_path = haproxy_path + '/waf/modsecurity.conf'
rule_file_path = 'Include ' + haproxy_path + '//waf/rules/' + rule_file
rule_file_path = 'Include ' + haproxy_path + '/waf/rules/' + rule_file
print(rule_file_path)
if enable == '0':
cmd = ["sudo sed -i 's!" + rule_file_path + "!#" + rule_file_path + "!' " + conf_file_path]
@ -2901,6 +2948,32 @@ if form.getvalue('waf_rule_id'):
print(funct.ssh_command(serv, cmd))
sql.update_enable_waf_rules(rule_id, serv, enable)
if form.getvalue('new_waf_rule'):
service = form.getvalue('service')
new_waf_rule = form.getvalue('new_waf_rule')
new_rule_desc = form.getvalue('new_rule_description')
rule_file = form.getvalue('new_rule_file')
rule_file = rule_file + '.conf'
waf_path = ''
if service == 'haproxy':
waf_path = funct.return_nice_path(sql.get_setting('haproxy_dir'))
elif service == 'nginx':
waf_path = funct.return_nice_path(sql.get_setting('nginx_dir'))
conf_file_path = waf_path + 'waf/modsecurity.conf'
rule_file_path = waf_path + 'waf/rules/' + rule_file
cmd = [f"sudo echo Include {rule_file_path} >> {conf_file_path} && sudo touch {rule_file_path}"]
print(funct.ssh_command(serv, cmd))
print(sql.insert_new_waf_rule(new_waf_rule, rule_file, new_rule_desc, service, serv))
try:
funct.logging('WAF', ' A new rule has been created ' + rule_file + ' on the server ' + serv,
haproxywi=1, login=1)
except Exception:
pass
if form.getvalue('lets_domain'):
serv = form.getvalue('serv')
lets_domain = form.getvalue('lets_domain')
@ -3251,7 +3324,7 @@ if form.getvalue('show_versions'):
if form.getvalue('get_group_name_by_id'):
print(sql.get_group_name_by_id(form.getvalue('get_group_name_by_id')))
if form.getvalue('do_new_name') or form.getvalue('aws_new_name') or form.getvalue('gcore_new_name'):
if any((form.getvalue('do_new_name'), form.getvalue('aws_new_name'), form.getvalue('gcore_new_name'))):
funct.check_user_group()
is_add = False
if form.getvalue('do_new_name'):
@ -3288,6 +3361,8 @@ if form.getvalue('do_new_name') or form.getvalue('aws_new_name') or form.getvalu
cookie = http.cookies.SimpleCookie(os.environ.get("HTTP_COOKIE"))
user_uuid = cookie.get('uuid')
role_id = sql.get_user_role_by_uuid(user_uuid.value)
params = sql.select_provisioning_params()
providers = sql.select_providers(provider_group, key=provider_token)
if role_id == 1:
groups = sql.select_groups()
@ -3296,7 +3371,7 @@ if form.getvalue('do_new_name') or form.getvalue('aws_new_name') or form.getvalu
env = Environment(loader=FileSystemLoader('templates'), autoescape=True)
template = env.get_template('ajax/provisioning/providers.html')
template = template.render(providers=sql.select_providers(provider_group, key=provider_token), role=role_id, groups=groups, user_group=provider_group, adding=1)
template = template.render(providers=providers, role=role_id, groups=groups, user_group=provider_group, adding=1, params=params)
print(template)
if form.getvalue('providerdel'):
@ -3462,12 +3537,13 @@ if form.getvalue('doworkspace'):
user, user_id, role, token, servers, user_services = funct.get_users_params()
new_server = sql.select_provisioned_servers(new=workspace, group=group, type='do')
params = sql.select_provisioning_params()
env = Environment(extensions=["jinja2.ext.do"], loader=FileSystemLoader('templates'))
template = env.get_template('ajax/provisioning/provisioned_servers.html')
template = template.render(
servers=new_server, groups=sql.select_groups(), user_group=group,
providers=sql.select_providers(group), role=role, adding=1
providers=sql.select_providers(group), role=role, adding=1, params=params
)
print(template)
@ -3557,12 +3633,13 @@ if form.getvalue('awsworkspace'):
user, user_id, role, token, servers, user_services = funct.get_users_params()
new_server = sql.select_provisioned_servers(new=workspace, group=group, type='aws')
params = sql.select_provisioning_params()
env = Environment(extensions=["jinja2.ext.do"], loader=FileSystemLoader('templates'))
template = env.get_template('ajax/provisioning/provisioned_servers.html')
template = template.render(
servers=new_server, groups=sql.select_groups(), user_group=group,
providers=sql.select_providers(group), role=role, adding=1
providers=sql.select_providers(group), role=role, adding=1, params=params
)
print(template)
@ -3682,6 +3759,10 @@ if (
for ip in output:
ips += ip
ips += ' '
if cloud == 'gcore':
ips = ips.split(' ')[0]
print(ips)
sql.update_provisioning_server_status('Created', group, workspace, provider_id, update_ip=ips)
@ -3826,6 +3907,7 @@ if form.getvalue('gcoreworkspace'):
user, user_id, role, token, servers, user_services = funct.get_users_params()
new_server = sql.select_provisioned_servers(new=workspace, group=group, type='gcore')
params = sql.select_provisioning_params()
env = Environment(extensions=["jinja2.ext.do"], loader=FileSystemLoader('templates'))
template = env.get_template('ajax/provisioning/provisioned_servers.html')
@ -3834,7 +3916,8 @@ if form.getvalue('gcoreworkspace'):
user_group=group,
providers=sql.select_providers(group),
role=role,
adding=1)
adding=1,
params=params)
print(template)
if form.getvalue('gcoreeditworkspace'):
@ -3879,33 +3962,42 @@ if form.getvalue('editAwsServer'):
funct.check_user_group()
server_id = form.getvalue('editAwsServer')
user_group = form.getvalue('editAwsGroup')
params = sql.select_provisioning_params()
providers = sql.select_providers(int(user_group))
server = sql.select_gcore_server(server_id=server_id)
from jinja2 import Environment, FileSystemLoader
env = Environment(extensions=["jinja2.ext.do"], loader=FileSystemLoader('templates'))
template = env.get_template('ajax/provisioning/aws_edit_dialog.html')
template = template.render(server=sql.select_aws_server(server_id=server_id), providers=sql.select_providers(int(user_group)))
template = template.render(server=server, providers=providers, params=params)
print(template)
if form.getvalue('editGcoreServer'):
funct.check_user_group()
server_id = form.getvalue('editGcoreServer')
user_group = form.getvalue('editGcoreGroup')
params = sql.select_provisioning_params()
providers = sql.select_providers(int(user_group))
server = sql.select_gcore_server(server_id=server_id)
from jinja2 import Environment, FileSystemLoader
env = Environment(extensions=["jinja2.ext.do"], loader=FileSystemLoader('templates'))
template = env.get_template('ajax/provisioning/gcore_edit_dialog.html')
template = template.render(server=sql.select_gcore_server(server_id=server_id), providers=sql.select_providers(int(user_group)))
template = template.render(server=server, providers=providers, params=params)
print(template)
if form.getvalue('editDoServer'):
funct.check_user_group()
server_id = form.getvalue('editDoServer')
user_group = form.getvalue('editDoGroup')
params = sql.select_provisioning_params()
providers = sql.select_providers(int(user_group))
server = sql.select_do_server(server_id=server_id)
from jinja2 import Environment, FileSystemLoader
env = Environment(extensions=["jinja2.ext.do"], loader=FileSystemLoader('templates'))
template = env.get_template('ajax/provisioning/do_edit_dialog.html')
template = template.render(server=sql.select_do_server(server_id=server_id), providers=sql.select_providers(int(user_group)))
template = template.render(server=server, providers=providers, params=params)
print(template)
if form.getvalue('edit_do_provider'):
@ -4009,12 +4101,12 @@ if form.getvalue('load_update_hapwi'):
template = env.get_template('ajax/load_updatehapwi.html')
versions = funct.versions()
checker_ver = funct.check_new_version(service='checker')
smon_ver = funct.check_new_version(service='smon')
metrics_ver = funct.check_new_version(service='metrics')
keep_ver = funct.check_new_version(service='keep')
portscanner_ver = funct.check_new_version(service='portscanner')
socket_ver = funct.check_new_version(service='socket')
checker_ver = funct.check_new_version('checker')
smon_ver = funct.check_new_version('smon')
metrics_ver = funct.check_new_version('metrics')
keep_ver = funct.check_new_version('keep_alive')
portscanner_ver = funct.check_new_version('portscanner')
socket_ver = funct.check_new_version('socket')
services = funct.get_services_status()
template = template.render(services=services,

View File

@ -15,6 +15,18 @@
msg="info HAProxy WAF has already installed"
when: stat_result.stat.exists
- name: erase the RPMS for HAProxy
yum:
name:
- ssdeep
- ssdeep-devel
state: absent
when:
- ansible_facts['os_family'] == "RedHat" or ansible_facts['os_family'] == 'CentOS'
environment:
http_proxy: "{{PROXY}}"
https_proxy: "{{PROXY}}"
- name: install the el7 RPMS for HAProxy
yum:
name:
@ -65,6 +77,7 @@
- libevent-devel
- libtool
- make
- gcc-c++
state: latest
when:
- ansible_facts['os_family'] == "RedHat" or ansible_facts['os_family'] == 'CentOS'
@ -86,6 +99,7 @@
- libyajl-dev
- libxml2
- automake
- g++
- make
state: present
when: ansible_facts['os_family'] == 'Debian' or ansible_facts['os_family'] == 'Ubuntu'
@ -110,57 +124,41 @@
become: false
unarchive:
src: /tmp/modsecurity.tar.gz
dest: /tmp/modsecurity/
dest: /tmp/modsecurity
remote_src: true
- name: Copy modsecurity
copy:
src: "/tmp/modsecurity/modsecurity-{{ modsec_ver }}/"
dest: /tmp/modsecurity/
remote_src: yes
- name: Set execute permision to configure
become: true
command: chdir=/tmp/modsecurity/ chmod +x configure
args:
warn: no
- name: Set ModSec src foleder
set_fact:
mod_sec_src: /tmp/modsecurity-{{ modsec_ver }}
- name: Re configure Modsecurity
become: true
command: chdir=/tmp/modsecurity/ autoreconf -f -i
command: "chdir={{ mod_sec_src }} autoreconf -f -i"
- name: Configure Modsecurity
become: true
command: chdir=/tmp/modsecurity/ ./configure --prefix=/tmp/modsecurity --enable-standalone-module --disable-mlogc --enable-pcre-study --without-lua --enable-pcre-jit
command: "chdir={{ mod_sec_src }} ./configure --prefix=/tmp/modsecurity --enable-standalone-module --disable-mlogc --enable-pcre-study --without-lua --enable-pcre-jit"
- name: Make Modsecurity
command: chdir=/tmp/modsecurity/ make
command: "chdir={{ mod_sec_src }} make"
- name: Make Install Modsecurity
command: chdir=/tmp/modsecurity/ make -C standalone install
command: "chdir={{ mod_sec_src }} make -C standalone install"
- name: Creates directory
file:
path: /tmp/modsecurity/INSTALL/include
path: "{{ mod_sec_src }}INSTALL/include"
state: directory
- name: Copy Modsec libs
copy:
src: /tmp/modsecurity/standalone/.libs/
dest: /tmp/modsecurity/INSTALL/include/
remote_src: yes
- name: Copy Modsec files
copy:
src: /tmp/modsecurity/standalone/
dest: /tmp/modsecurity/INSTALL/include/
remote_src: yes
- name: Copy Modsec apache files
copy:
src: /tmp/modsecurity/apache2/
dest: /tmp/modsecurity/INSTALL/include/
src: "{{ mod_sec_src }}/{{ item }}"
dest: "{{ mod_sec_src }}/INSTALL/include/"
remote_src: yes
with_items:
- standalone/.libs/
- standalone/
- apache2/
- name: Install git
package:
@ -175,22 +173,20 @@
mod_sec_dir: /tmp/spoa-modsecurity
- name: Make APT Modsecurity module for HAProxy
command: "chdir={{ mod_sec_dir }} make MODSEC_INC=/tmp/modsecurity/INSTALL/include MODSEC_LIB=/tmp/modsecurity/INSTALL/include APACHE2_INC=/usr/include/apache2/ APR_INC=/usr/include/apr-1.0"
command: "chdir={{ mod_sec_dir }} make MODSEC_INC={{ mod_sec_src }}/INSTALL/include MODSEC_LIB={{ mod_sec_src }}/INSTALL/include APACHE2_INC=/usr/include/apache2/ APR_INC=/usr/include/apr-1.0"
when: ansible_facts['os_family'] == 'Debian' or ansible_facts['os_family'] == 'Ubuntu'
- name: Make EL Modsecurity module for HAProxy
command: "chdir={{ mod_sec_dir }} make MODSEC_INC=/tmp/modsecurity/INSTALL/include MODSEC_LIB=/tmp/modsecurity/INSTALL/include APACHE2_INC=/usr/include/httpd/ APR_INC=/usr/include/apr-1"
command: "chdir={{ mod_sec_dir }} make MODSEC_INC={{ mod_sec_src }}/INSTALL/include MODSEC_LIB={{ mod_sec_src }}/INSTALL/include APACHE2_INC=/usr/include/httpd/ APR_INC=/usr/include/apr-1"
when: ansible_facts['os_family'] == "RedHat" or ansible_facts['os_family'] == 'CentOS'
- name: Make WAF rules directory
file:
path: "{{ HAPROXY_PATH }}/waf/rules"
state: directory
- name: Make WAF bin directory
file:
path: "{{ HAPROXY_PATH }}/waf/bin"
path: "{{ HAPROXY_PATH }}/waf/{{ item }}"
state: directory
with_items:
- rules
- bin
- name: Copy Modsec module to HAProxy dir
copy:
@ -269,9 +265,16 @@
- name: Copy owasp files
copy:
src: /tmp/owasp-modsecurity-crs-2.2.9/owasp-modsecurity-crs-2.2.9/
src: "/tmp/owasp-modsecurity-crs-2.2.9/{{ item }}"
dest: /tmp/owasp-modsecurity-crs-2.2.9
remote_src: yes
with_items:
- owasp-modsecurity-crs-2.2.9/
- activated_rules/
- base_rules/
- experimental_rules/
- optional_rules/
- slr_rules/
- name: Copy Modsec crs conf file
copy:
@ -279,36 +282,6 @@
dest: "{{ HAPROXY_PATH }}/waf/rules/modsecurity_crs_10_setup.conf"
remote_src: true
- name: Copy Modsec crs activated_rules files
copy:
src: /tmp/owasp-modsecurity-crs-2.2.9/activated_rules/
dest: "{{ HAPROXY_PATH }}/waf/rules/"
remote_src: yes
- name: Copy Modsec crs base_rules files
copy:
src: /tmp/owasp-modsecurity-crs-2.2.9/base_rules/
dest: "{{ HAPROXY_PATH }}/waf/rules/"
remote_src: yes
- name: Copy Modsec crs experimental_rules files
copy:
src: /tmp/owasp-modsecurity-crs-2.2.9/experimental_rules/
dest: "{{ HAPROXY_PATH }}/waf/rules/"
remote_src: yes
- name: Copy Modsec crs optional_rules files
copy:
src: /tmp/owasp-modsecurity-crs-2.2.9/optional_rules/
dest: "{{ HAPROXY_PATH }}/waf/rules/"
remote_src: yes
- name: Copy Modsec crs slr_rules files
copy:
src: /tmp/owasp-modsecurity-crs-2.2.9/slr_rules/
dest: "{{ HAPROXY_PATH }}/waf/rules/"
remote_src: yes
- name: Ensure ModSec engine mode on
ansible.builtin.lineinfile:
path: "{{ HAPROXY_PATH }}/waf/modsecurity.conf"
@ -367,42 +340,14 @@
enabled: yes
always:
- name: Remove modsecurity.tar.gz
- name: Clean up
ansible.builtin.file:
path: /tmp/modsecurity.tar.gz
state: absent
- name: Remove modsecurity-2.9.2
ansible.builtin.file:
path: /tmp/modsecurity-2.9.2
state: absent
- name: Remove HAProxy
ansible.builtin.file:
path: "/tmp/haproxy-{{ VERSION }}"
state: absent
- name: Remove modsecurity
ansible.builtin.file:
path: /tmp/modsecurity
state: absent
- name: Remove modsecurity.conf
ansible.builtin.file:
path: /tmp/modsecurity.conf
state: absent
- name: Remove owasp.tar.gz
ansible.builtin.file:
path: /tmp/owasp.tar.gz
state: absent
- name: Remove owasp-modsecurity-crs-2.2.9
ansible.builtin.file:
path: /tmp/owasp-modsecurity-crs-2.2.9
state: absent
- name: Remove spoa-modsecurity
ansible.builtin.file:
path: /tmp/spoa-modsecurity
path: "{{ item }}"
state: absent
with_items:
- /tmp/modsecurity.tar.gz
- "/tmp/modsecurity-{{ modsec_ver }}"
- "/tmp/haproxy-{{ VERSION }}"
- /tmp/owasp.tar.gz
- /tmp/owasp-modsecurity-crs-2.2.9
- /tmp/spoa-modsecurity

View File

@ -0,0 +1,11 @@
---
- name: Install WAF
hosts: "{{ variable_host }}"
become: yes
become_method: sudo
gather_facts: yes
roles:
- role: waf_nginx
environment:
http_proxy: "{{PROXY}}"
https_proxy: "{{PROXY}}"

View File

@ -0,0 +1 @@
coreruleset_ver: 3.3.2

View File

@ -0,0 +1,3 @@
---
- name: reload NGINX
service: name=nginx state=reloaded

View File

@ -0,0 +1,224 @@
---
- name: Installing WAF
block:
- name: Set SSH port
set_fact:
ansible_port: "{{SSH_PORT}}"
# - debug: msg="{{ ansible_facts }}"
- name: Check that WAF has been installed
stat:
path: "{{ NGINX_PATH }}/waf/modsecurity.conf"
register: stat_result
- name: Fail if has been installed
fail:
msg="info NGINX WAF has already installed"
when: stat_result.stat.exists
- name: install the common RPMS for NGINX
yum:
name:
- libtool
- libxml2-devel
- gcc
- curl-devel
- pcre-devel
- automake
- autoconf
- libevent-devel
- libtool
- make
- gcc-c++
- git
- redhat-rpm-config
- openssl-devel
- libxslt-devel
- gd-devel
- perl-ExtUtils-Embed
- GeoIP-devel
- ssdeep-devel
state: latest
when:
- ansible_facts['os_family'] == "RedHat" or ansible_facts['os_family'] == 'CentOS'
environment:
http_proxy: "{{PROXY}}"
https_proxy: "{{PROXY}}"
- name: Install needed packages
apt:
name:
- libtool
- libevent-dev
- libpcre3-dev
- libxml2-dev
- gcc
- libpcre3-dev
- libcurl4-nss-dev
- libyajl-dev
- libxml2
- automake
- autoconf
- g++
- make
- openssl-dev
- libxslt-dev
- gd-dev
- perl-modules
- libmodsecurity3
- libgeoip-dev
- libfuzzy2
state: present
when: ansible_facts['os_family'] == 'Debian' or ansible_facts['os_family'] == 'Ubuntu'
environment:
http_proxy: "{{PROXY}}"
https_proxy: "{{PROXY}}"
- name: Download ModSec
ansible.builtin.get_url:
url: "http://repo.roxy-wi.org/modsec/modsecv3.0.7-{{ ansible_facts.distribution | lower }}{{ ansible_facts.distribution_major_version }}.tar.gz"
dest: /usr/local/modsecv3.tar.gz
when:
- ansible_facts['os_family'] == "RedHat" or ansible_facts['os_family'] == 'CentOS'
- name: Untar ModSec
unarchive:
src: /usr/local/modsecv3.tar.gz
dest: /usr/local/
remote_src: true
- name: Get NGINX version
shell: /usr/sbin/nginx -v
register: nginx_version
- name: Get NGINX parameters
shell: /usr/sbin/nginx -V 2>&1 |grep configu |awk -F":" '{print $2}'
register: nginx_params
- name: Clone NGINX connector
shell: git clone --depth 1 https://github.com/SpiderLabs/ModSecurity-nginx.git /tmp/nginx-connector
- name: Download NGINX
ansible.builtin.get_url:
url: "http://nginx.org/download/nginx-{{ nginx_version.stderr.split('/')[1] }}.tar.gz"
dest: /tmp/nginx_src.tar.gz
- name: Create nginx_src directory
become: false
file:
path: /tmp/nginx_src/
state: directory
- name: Untar NGINX
become: false
unarchive:
src: /tmp/nginx_src.tar.gz
dest: /tmp/nginx_src/
remote_src: true
- name: Configure NGINX
become: true
command: "chdir=/tmp/nginx_src/nginx-{{ nginx_version.stderr.split('/')[1] }} ./configure {{ nginx_params.stdout }} --add-dynamic-module=../../nginx-connector"
environment:
CFLAGS: -Wno-error
- name: Make NGINX modules
become: true
command: "chdir=/tmp/nginx_src/nginx-{{ nginx_version.stderr.split('/')[1] }} make modules"
- name: Copy module for CentOS
become: true
command: "chdir=/tmp/nginx_src/nginx-{{ nginx_version.stderr.split('/')[1] }} cp objs/ngx_http_modsecurity_module.so /usr/share/nginx/modules/"
when:
- ansible_facts['os_family'] == "RedHat" or ansible_facts['os_family'] == 'CentOS'
- name: Copy module for Ubuntu
become: true
command: "chdir=/tmp/nginx_src/nginx-{{ nginx_version.stderr.split('/')[1] }} cp objs/ngx_http_modsecurity_module.so /usr/lib/nginx/modules/"
when: ansible_facts['os_family'] == 'Debian' or ansible_facts['os_family'] == 'Ubuntu'
- name: Enable module for Centos
become: true
shell: echo 'load_module "modules/ngx_http_modsecurity_module.so";' > /usr/share/nginx/modules/mod-waf-connector.conf
when:
- ansible_facts['os_family'] == "RedHat" or ansible_facts['os_family'] == 'CentOS'
- name: Enable module for Ubuntu
lineinfile:
path: "{{ NGINX_PATH }}/nginx.conf"
line: load_module modules/ngx_http_modsecurity_module.so;
insertbefore: BOF
when: ansible_facts['os_family'] == 'Debian' or ansible_facts['os_family'] == 'Ubuntu'
- name: Create WAF directory
become: false
file:
path: "{{ NGINX_PATH }}/waf/"
state: directory
- name: Create WAF rules directory
become: false
file:
path: "{{ NGINX_PATH }}/waf/rules"
state: directory
- name: Download modsecurity.conf
ansible.builtin.get_url:
url: https://raw.githubusercontent.com/SpiderLabs/ModSecurity/v3/master/modsecurity.conf-recommended
dest: "{{ NGINX_PATH }}/waf/modsecurity.conf"
- name: Download unicode.mapping
ansible.builtin.get_url:
url: https://github.com/SpiderLabs/ModSecurity/blob/v3/master/unicode.mapping
dest: "{{ NGINX_PATH }}/waf/unicode.mapping"
- name: Create WAF config
template:
src: waf.conf.j2
dest: "{{ NGINX_PATH }}/waf/waf.conf"
- name: Download OWASP rules
ansible.builtin.get_url:
url: "https://github.com/coreruleset/coreruleset/archive/v{{ coreruleset_ver }}.tar.gz"
dest: /tmp/OWASP.tar.gz
- name: Untar NGINX
become: false
unarchive:
src: /tmp/OWASP.tar.gz
dest: /tmp/
remote_src: true
- name: Copy Modsec crs activated_rules files
copy:
src: "/tmp/coreruleset-{{ coreruleset_ver }}/rules/"
dest: "{{ NGINX_PATH }}/waf/rules/"
remote_src: yes
- name: Copy module
become: true
command: "chdir=/tmp/coreruleset-{{ coreruleset_ver }} cp crs-setup.conf.example {{ NGINX_PATH }}/waf/rulescrs-setup.conf"
- name: Add waf Mod on
ansible.builtin.blockinfile:
path: "{{ NGINX_PATH }}/nginx.conf"
marker: "#-- {mark} WAF BLOCK --#"
insertafter: "http {"
block: |
modsecurity off;
modsecurity_rules_file /etc/nginx/waf/waf.conf;
notify: reload NGINX
always:
- name: Clean up
ansible.builtin.file:
path: "{{ item }}"
state: absent
with_items:
- /tmp/nginx_src/
- /tmp/nginx_src.tar.gz
- /tmp/nginx-connector
- /tmp/OWASP.tar.gz
- /usr/local/modsecv3.tar.gz
# - "/tmp/coreruleset-{{ coreruleset_ver }}"

View File

@ -0,0 +1,31 @@
Include {{ NGINX_PATH }}/waf/modsecurity.conf
Include {{ NGINX_PATH }}/waf/rulescrs-setup.conf
Include {{ NGINX_PATH }}/waf/rules/REQUEST-901-INITIALIZATION.conf
Include {{ NGINX_PATH }}/waf/rules/REQUEST-903.9001-DRUPAL-EXCLUSION-RULES.conf
Include {{ NGINX_PATH }}/waf/rules/REQUEST-903.9003-NEXTCLOUD-EXCLUSION-RULES.conf
Include {{ NGINX_PATH }}/waf/rules/REQUEST-903.9004-DOKUWIKI-EXCLUSION-RULES.conf
Include {{ NGINX_PATH }}/waf/rules/REQUEST-903.9005-CPANEL-EXCLUSION-RULES.conf
Include {{ NGINX_PATH }}/waf/rules/REQUEST-903.9006-XENFORO-EXCLUSION-RULES.conf
Include {{ NGINX_PATH }}/waf/rules/REQUEST-905-COMMON-EXCEPTIONS.conf
Include {{ NGINX_PATH }}/waf/rules/REQUEST-910-IP-REPUTATION.conf
Include {{ NGINX_PATH }}/waf/rules/REQUEST-911-METHOD-ENFORCEMENT.conf
Include {{ NGINX_PATH }}/waf/rules/REQUEST-912-DOS-PROTECTION.conf
Include {{ NGINX_PATH }}/waf/rules/REQUEST-913-SCANNER-DETECTION.conf
Include {{ NGINX_PATH }}/waf/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf
Include {{ NGINX_PATH }}/waf/rules/REQUEST-921-PROTOCOL-ATTACK.conf
Include {{ NGINX_PATH }}/waf/rules/REQUEST-930-APPLICATION-ATTACK-LFI.conf
Include {{ NGINX_PATH }}/waf/rules/REQUEST-931-APPLICATION-ATTACK-RFI.conf
Include {{ NGINX_PATH }}/waf/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf
Include {{ NGINX_PATH }}/waf/rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf
Include {{ NGINX_PATH }}/waf/rules/REQUEST-934-APPLICATION-ATTACK-NODEJS.conf
Include {{ NGINX_PATH }}/waf/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf
Include {{ NGINX_PATH }}/waf/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf
Include {{ NGINX_PATH }}/waf/rules/REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION.conf
Include {{ NGINX_PATH }}/waf/rules/REQUEST-944-APPLICATION-ATTACK-JAVA.conf
Include {{ NGINX_PATH }}/waf/rules/REQUEST-949-BLOCKING-EVALUATION.conf
Include {{ NGINX_PATH }}/waf/rules/RESPONSE-951-DATA-LEAKAGES-SQL.conf
Include {{ NGINX_PATH }}/waf/rules/RESPONSE-952-DATA-LEAKAGES-JAVA.conf
Include {{ NGINX_PATH }}/waf/rules/RESPONSE-953-DATA-LEAKAGES-PHP.conf
Include {{ NGINX_PATH }}/waf/rules/RESPONSE-954-DATA-LEAKAGES-IIS.conf
Include {{ NGINX_PATH }}/waf/rules/RESPONSE-959-BLOCKING-EVALUATION.conf
Include {{ NGINX_PATH }}/waf/rules/RESPONSE-980-CORRELATION.conf

43
app/scripts/waf_nginx.sh Normal file
View File

@ -0,0 +1,43 @@
#!/bin/bash
for ARGUMENT in "$@"
do
KEY=$(echo "$ARGUMENT" | cut -f1 -d=)
VALUE=$(echo "$ARGUMENT" | cut -f2 -d=)
case "$KEY" in
PROXY) PROXY=${VALUE} ;;
NGINX_PATH) NGINX_PATH=${VALUE} ;;
HOST) HOST=${VALUE} ;;
USER) USER=${VALUE} ;;
PASS) PASS=${VALUE} ;;
KEY) KEY=${VALUE} ;;
SSH_PORT) SSH_PORT=${VALUE} ;;
*)
esac
done
export ANSIBLE_HOST_KEY_CHECKING=False
export ANSIBLE_DISPLAY_SKIPPED_HOSTS=False
export ACTION_WARNINGS=False
export LOCALHOST_WARNING=False
export COMMAND_WARNINGS=False
PWD=$(pwd)
PWD=$PWD/scripts/ansible/
echo "$HOST ansible_port=$SSH_PORT" > $PWD/$HOST
if [[ $KEY == "" ]]; then
ansible-playbook $PWD/roles/waf_nginx.yml -e "ansible_user=$USER ansible_ssh_pass='$PASS' variable_host=$HOST PROXY=$PROXY NGINX_PATH=$NGINX_PATH SSH_PORT=$SSH_PORT" -i $PWD/$HOST
else
ansible-playbook $PWD/roles/waf_nginx.yml --key-file $KEY -e "ansible_user=$USER variable_host=$HOST PROXY=$PROXY NGINX_PATH=$NGINX_PATH SSH_PORT=$SSH_PORT" -i $PWD/$HOST
fi
if [ $? -gt 0 ]
then
echo "error: Cannot install WAF"
else
echo "success"
fi
rm -f $PWD/$HOST

View File

@ -1272,6 +1272,25 @@ def select_waf_servers(serv):
return en.ip
def select_waf_nginx_servers(serv):
query = Server.select(Server.ip).join(WafNginx, on=(WafNginx.server_id == Server.server_id)).where(Server.ip == serv)
try:
query_res = query.execute()
except Exception as e:
out_error(e)
else:
for en in query_res:
return en.ip
def insert_waf_nginx_server(server_ip):
try:
server_id = Server.get(Server.ip == server_ip).server_id
WafNginx.insert(server_id=server_id).execute()
except Exception as e:
out_error(e)
def select_waf_servers_metrics_for_master():
query = Server.select(Server.ip).join(
Waf, on=(Waf.server_id == Server.server_id)
@ -1390,8 +1409,105 @@ def insert_waf_rules(serv):
return True
def select_waf_rules(serv):
query = WafRules.select(WafRules.id, WafRules.rule_name, WafRules.en, WafRules.desc).where(WafRules.serv == serv)
def insert_nginx_waf_rules(serv):
data_source = [
{'serv': serv, 'rule_name': 'Initialization', 'rule_file': 'REQUEST-901-INITIALIZATION.conf',
'desc': 'This file REQUEST-901-INITIALIZATION.conf initializes the Core Rules and performs preparatory actions. '
'It also fixes errors and omissions of variable definitions in the file crs-setup.conf The setup.conf'
'can and should be edited by the user, this file. is part of the CRS installation and should not be altered.',
'service': 'nginx'},
{'serv': serv, 'rule_name': 'Drupal exclusion rules', 'rule_file': 'REQUEST-903.9001-DRUPAL-EXCLUSION-RULES.conf',
'desc': 'These exclusions remedy false positives in a default Drupal install. The exclusions are only active '
'if crs_exclusions_drupal=1 is set. See rule 900130 in crs-setup.conf for instructions.',
'service': 'nginx'},
{'serv': serv, 'rule_name': 'Nextcloud exclusion rules', 'rule_file': 'REQUEST-903.9003-NEXTCLOUD-EXCLUSION-RULES.conf',
'desc': 'These exclusions remedy false positives in a default NextCloud install. They will likely work with OwnCloud '
'too, but you may have to modify them. The exclusions are only active if crs_exclusions_nextcloud=1 is set. '
'See rule 900130 in crs-setup.conf for instructions.',
'service': 'nginx'},
{'serv': serv, 'rule_name': 'Dokuwiki exclusion rules', 'rule_file': 'REQUEST-903.9004-DOKUWIKI-EXCLUSION-RULES.conf',
'desc': 'These exclusions remedy false positives in a default Dokuwiki install. The exclusions are only active '
'if crs_exclusions_dokuwiki=1 is set. See rule 900130 in crs-setup.conf for instructions.',
'service': 'nginx'},
{'serv': serv, 'rule_name': 'CPanel exclusion rules', 'rule_file': 'REQUEST-903.9005-CPANEL-EXCLUSION-RULES.conf',
'desc': 'These exclusions remedy false positives in a default CPanel install. The exclusions are only active '
'if crs_exclusions_cpanel=1 is set. See rule 900130 in crs-setup.conf for instructions.',
'service': 'nginx'},
{'serv': serv, 'rule_name': 'XenForo exclusion rules', 'rule_file': 'REQUEST-903.9006-XENFORO-EXCLUSION-RULES.conf',
'desc': 'These exclusions remedy false positives in a default XenForo install. The exclusions are only active '
'if crs_exclusions_xenforo=1 is set. See rule 900130 in crs-setup.conf for instructions.',
'service': 'nginx'},
{'serv': serv, 'rule_name': 'Common exceptions', 'rule_file': 'REQUEST-905-COMMON-EXCEPTIONS.conf',
'desc': 'This file is used as an exception mechanism to remove common false positives that may be encountered.',
'service': 'nginx'},
{'serv': serv, 'rule_name': 'IP reputation', 'rule_file': 'REQUEST-910-IP-REPUTATION.conf',
'desc': 'IP reputation rule.', 'service': 'nginx'},
{'serv': serv, 'rule_name': 'Method enforcement', 'rule_file': 'REQUEST-911-METHOD-ENFORCEMENT.conf',
'desc': 'Method enforcement rule.', 'service': 'nginx'},
{'serv': serv, 'rule_name': 'DDOS protection', 'rule_file': 'REQUEST-912-DOS-PROTECTION.conf',
'desc': 'Anti-Automation rules to detect Denial of Service attacks.', 'service': 'nginx'},
{'serv': serv, 'rule_name': 'Protocol enforcement', 'rule_file': 'REQUEST-920-PROTOCOL-ENFORCEMENT.conf',
'desc': 'Some protocol violations are common in application layer attacks. Validating HTTP requests eliminates '
'a large number of application layer attacks. The purpose of this rules file is to enforce HTTP RFC '
'requirements that state how the client is supposed to interact with the server.',
'service': 'nginx'},
{'serv': serv, 'rule_name': 'Protocol attack', 'rule_file': 'REQUEST-921-PROTOCOL-ATTACK.conf',
'desc': 'Protocol attack rule.', 'service': 'nginx'},
{'serv': serv, 'rule_name': 'Application attack LFI', 'rule_file': 'REQUEST-930-APPLICATION-ATTACK-LFI.conf',
'desc': 'Application attack LFI rule.', 'service': 'nginx'},
{'serv': serv, 'rule_name': 'Application attack RCE', 'rule_file': 'REQUEST-932-APPLICATION-ATTACK-RCE.conf',
'desc': 'Application attack RCE rule.', 'service': 'nginx'},
{'serv': serv, 'rule_name': 'Application attack PHP', 'rule_file': 'REQUEST-933-APPLICATION-ATTACK-PHP.conf',
'desc': 'Application attack PHP rule.', 'service': 'nginx'},
{'serv': serv, 'rule_name': 'Application attack NodeJS', 'rule_file': 'REQUEST-934-APPLICATION-ATTACK-NODEJS.conf',
'desc': 'Application attack NodeJS rule.', 'service': 'nginx'},
{'serv': serv, 'rule_name': 'Application attack SQLI', 'rule_file': 'REQUEST-942-APPLICATION-ATTACK-SQLI.conf',
'desc': 'Application attack SQLI rule.', 'service': 'nginx'},
{'serv': serv, 'rule_name': 'Application attack session-fixation', 'rule_file': 'REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION.conf',
'desc': 'Application attack session-fixation rule.', 'service': 'nginx'},
{'serv': serv, 'rule_name': 'Application attack JAVA', 'rule_file': 'REQUEST-944-APPLICATION-ATTACK-JAVA.conf',
'desc': 'Application attack JAVA rule.', 'service': 'nginx'},
{'serv': serv, 'rule_name': 'Application attack blocking evaluation', 'rule_file': 'REQUEST-949-BLOCKING-EVALUATION.conf',
'desc': 'Application attack blocking evaluation rule.', 'service': 'nginx'},
{'serv': serv, 'rule_name': 'Data leakages', 'rule_file': 'RESPONSE-950-DATA-LEAKAGES.conf',
'desc': 'The paranoia level skip rules 950020, 950021 and 950022 have odd numbers not in sync with other paranoia '
'level skip rules in other. files. This is done to avoid rule id collisions with CRSv2. This is also true '
'for rule 950130.', 'service': 'nginx'},
{'serv': serv, 'rule_name': 'Data leakages SQL', 'rule_file': 'RESPONSE-951-DATA-LEAKAGES-SQL.conf',
'desc': 'Data leakages SQL rule', 'service': 'nginx'},
{'serv': serv, 'rule_name': 'Data leakages JAVA', 'rule_file': 'RESPONSE-952-DATA-LEAKAGES-JAVA.conf',
'desc': 'Data leakages JAVA rule', 'service': 'nginx'},
{'serv': serv, 'rule_name': 'Data leakages PHP', 'rule_file': 'RESPONSE-953-DATA-LEAKAGES-PHP.conf',
'desc': 'Data leakages PHP rule', 'service': 'nginx'},
{'serv': serv, 'rule_name': 'Data leakages IIS', 'rule_file': 'RESPONSE-954-DATA-LEAKAGES-IIS.conf',
'desc': 'Data leakages IIS rule', 'service': 'nginx'},
{'serv': serv, 'rule_name': 'Blocking evaluation', 'rule_file': 'RESPONSE-959-BLOCKING-EVALUATION.conf',
'desc': 'You should set the score to the proper threshold you would prefer. If kept at "@gt 0" it will work '
'similarly to previous Mod CRS rules and will create an event in the error_log file if there are any '
'rules that match. If you would like to lessen the number of events generated in the error_log file, '
'you should increase the anomaly score threshold to something like "@gt 20". This would only generate '
'an event in the error_log file if there are multiple lower severity rule matches or if any 1 higher '
'severity item matches. You should also set the desired disruptive action (deny, redirect, etc...).',
'service': 'nginx'},
{'serv': serv, 'rule_name': 'Correlation', 'rule_file': 'RESPONSE-980-CORRELATION.conf',
'desc': 'This file is used in post processing after the response has been sent to the client (in the logging phase). '
'Its purpose is to provide inbound+outbound correlation of events to provide a more intelligent designation '
'as to the outcome or result of the transaction - meaning, was this a successful attack?',
'service': 'nginx'},
]
try:
WafRules.insert_many(data_source).execute()
except Exception as e:
out_error(e)
else:
return True
def select_waf_rules(serv, service):
query = WafRules.select(WafRules.id, WafRules.rule_name, WafRules.en, WafRules.desc).where(
(WafRules.serv == serv)
& (WafRules.service == service)
)
try:
query_res = query.execute()
except Exception as e:
@ -1425,6 +1541,22 @@ def update_enable_waf_rules(rule_id, serv, en):
out_error(e)
def insert_new_waf_rule(rule_name: str, rule_file: str, rule_description: str, service: str, serv: str) -> int:
try:
last_id = WafRules.insert(
serv=serv,
rule_name=rule_name,
rule_file=rule_file,
desc=rule_description,
service=service
).execute()
except Exception as e:
out_error(e)
else:
return last_id
def delete_waf_server(server_id):
query = Waf.delete().where(Waf.server_id == server_id)
try:

View File

@ -2,6 +2,11 @@
{% do waf_modes.append("On") %}
{% do waf_modes.append("Off") %}
{% do waf_modes.append("DetectionOnly") %}
{% if waf_service == 'haproxy' %}
{% set service_name = 'waf' %}
{% else %}
{% set service_name = 'waf_nginx' %}
{% endif %}
{% for service in service_status %}
<td class="padding10 first-collumn-wi">
{% if service.5|int() >= 1 %}
@ -17,18 +22,25 @@
<span class="serverNone server-status" title="WAF is not installed"></span> <span title="WAF is not installed">{{ service.0 }}</span>
{% endif %}
</td>
{{service.3}}
{% if service.3 == "On" or service.3 == "Off" or service.3 == "DetectionOnly" %}
<td>
{% if role <= 2 %}
<a id="{{ service.1 }}" class="start-waf" title="Start WAF service">
<span class="service-start" onclick="confirmAjaxAction('start', 'waf', '{{service.1}}')"></span>
<span class="service-start" onclick="confirmAjaxAction('start', '{{service_name}}', '{{service.1}}')"></span>
</a>
<a id="{{ service.1 }}" class="stop-waf" title="Stop WAF service">
<span class="service-stop" onclick="confirmAjaxAction('stop', 'waf', '{{service.1}}')"></span>
<span class="service-stop" onclick="confirmAjaxAction('stop', '{{service_name}}', '{{service.1}}')"></span>
</a>
{% if waf_service == 'haproxy' %}
<a id="{{ service.1 }}" class="restart-waf" title="Restart WAF service">
<span class="service-reload" onclick="confirmAjaxAction('restart', 'waf', '{{service.1}}')"></span>
</a>
{% else %}
<a id="{{ service.1 }}" class="reload-waf" title="Reload WAF service">
<span class="service-reload" onclick="confirmAjaxAction('reload', 'nginx', '{{service.1}}')"></span>
</a>
{% endif %}
{% endif %}
</td>
<td>
@ -46,6 +58,7 @@
{{ service.3 }}
{% endif %}
</td>
{% if waf_service == 'haproxy' %}
<td style="padding-top: 2px;" class="ajaxwafstatus">
{% if service.4|int() == 1 %}
<label for="metrics{{ service.0 }}"></label><input type="checkbox" id="metrics{{ service.0 }}" checked />
@ -53,13 +66,18 @@
<label for="metrics{{ service.0 }}"></label><input type="checkbox" id="metrics{{ service.0 }}" />
{% endif %}
</td>
{% endif %}
<td>
{% if role <= 2 %}
<a href="/app/waf.py?manage_rules=1&serv={{service.1}}" class="ui-button ui-widget ui-corner-all">Open</a>
<a href="/app/waf.py?service={{waf_service}}&manage_rules=1&serv={{service.1}}" class="ui-button ui-widget ui-corner-all">Open</a>
{% endif %}
</td>
<td>
{% if waf_service == 'haproxy' %}
<a href="/app/logs.py?serv={{ service.1 }}&rows=10&grep=&hour=00&minut=00&hour1=24&minut1=00&waf=1" class="ui-button ui-widget ui-corner-all" title="View log">View</a>
{% elif waf_service == 'nginx' %}
<a href="/app/logs.py?service={{waf_service}}&serv={{ service.1 }}&rows=10&grep=ModSecurity&hour=00&minut=00&hour1=24&minut1=00&file=error.log&waf=0" class="ui-button ui-widget ui-corner-all" title="View log">View</a>
{% endif %}
</td>
<td></td>
{% else %}

View File

@ -93,6 +93,7 @@
{% if role <= 3 %}
<li><a href="/app/versions.py?service=nginx" title="Working with versions NGINX configs" class="version head-submenu">Versions</a></li>
<li><a href="/app/add.py?service=nginx#ssl" title="Add proxy: Upload SSL certificates - Roxy-WI" class="cert head-submenu" id="add3">SSL</a></li>
<li><a href="/app/waf.py?service=nginx" title="Web application firewall" class="waf-menu head-submenu">WAF</a> </li>
{% endif %}
</ul>
</li>
@ -276,7 +277,7 @@
<a href="https://github.com/hap-wi/roxy-wi/issues" class="footer-link" target="_blank" title="Community help">Help</a>
<a href="https://sd.roxy-wi.org" class="footer-link" target="_blank" title="Service Desk">SD</a>
<a href="https://roxy-wi.org/contacts" class="footer-link" target="_blank">Contacts</a>
<a href="https://roxy-wi.org/cabinet.py" class="footer-link" target="_blank" title="Private cabinet">Cabinet</a>
<a href="https://roxy-wi.org/cabinet" class="footer-link" target="_blank" title="Private cabinet">Cabinet</a>
<a href="https://roxy-wi.org/legal" class="footer-link" target="_blank" title="Legal Note">Legal</a>
</div>
</div>

View File

@ -25,14 +25,29 @@
</td>
<td style="padding-top: 5px;padding-bottom: 10px;">{{r.desc}}</td>
<td style="padding: 0 10px 0 10px;">
<a href="waf.py?waf_rule_id={{r.id}}&serv={{serv}}" class="ui-button ui-widget ui-corner-all" title="View this rule versions">View</a>
<a href="waf.py?service={{service}}&waf_rule_id={{r.id}}&serv={{serv}}" class="ui-button ui-widget ui-corner-all" title="View this rule versions">View/Edit</a>
</td>
</tr>
{% endfor %}
</table>
<p><span class="add-button" title="Add a new WAF rule" onclick="addNewConfig()" style="margin-right: 20px;">+ Add</span></p><br><br>
<div id="add-new-config" style="display: none">
<table class="overview">
{% include 'include/tr_validate_tips.html' %}
<tr>
<td class="padding20">Rule name:<span class="need-field">*</span></td>
<td>{{ input('new_rule_name', type='text', placeholder='New protected rule', title='New protected rule') }}</td>
</tr>
<tr>
<td class="padding20">Rule description:<span class="need-field">*</span></td>
<td>{{ input('new_rule_description', type='text', placeholder='This rule protects again attacks', title='This rule protects again attacks') }}</td>
</tr>
</table>
</div>
{% elif waf_rule_file %}
<center>
<link rel="stylesheet" href="/inc/codemirror/codemirror.css">
<script src="/inc/configshow.js"></script>
<script src="/inc/codemirror/codemirror.js"></script>
<script src="/inc/codemirror/modsec.js"></script>
<h4>Config {{waf_rule_file}} from {{ serv }}</h4>
@ -41,13 +56,22 @@
<input type="hidden" value="{{ serv }}" name="serv">
<input type="hidden" value="{{ cfg }}.old" name="oldconfig">
<input type="hidden" value="{{ token }}" name="token">
<input type="hidden" value="{{ service }}" name="service">
<input type="hidden" value="waf_{{ service }}" name="service">
<input type="hidden" value="{{ config_file_name }}" name="config_file_name">
<div style="margin-left: 20%;width: 60%;">
<textarea name="config" id="config_text_area" class="config" rows="35" cols="100">{{ config }}</textarea>
</div>
<p>
<center>
<a href="waf.py?manage_rules=1&serv={{serv}}" class="ui-button ui-widget ui-corner-all" title="Return to rules management">Back</a>
{% if role <= 3 %}
<button type="submit" value="save" name="save" class="btn btn-default" title="Save config without reloading the service">Save</button>
{% if service == 'haproxy' %}
<button type="submit" value="" name="" class="btn btn-default">Save and restart</button>
{% elif service == 'nginx' %}
<button type="submit" value="" name="reload" class="btn btn-default">Save and reload</button>
{% endif %}
{% endif %}
</center>
</p>
</form>
@ -59,7 +83,7 @@
</style>
<script>
var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("config_text_area"),
{mode: "modsec", lineNumbers: true, autocapitalize: true, autocorrect: true, spellcheck: true, readOnly: true});
{mode: "modsec", lineNumbers: true, autocapitalize: true, autocorrect: true, spellcheck: true});
myCodeMirror.refresh();
</script>
{% else %}
@ -93,7 +117,9 @@
<td class="padding10 first-collumn">Server</td>
<td class="padding10">Actions</td>
<td class="padding10">WAF mode</td>
{% if service == 'haproxy' %}
<td>Metrics</td>
{% endif %}
<td>Manage rules</td>
<td>Log</td>
<td>
@ -106,11 +132,12 @@
<tr class="{{ loop.cycle('odd', 'even') }}" id="{{s[2]}}"></tr>
{% endfor %}
</table>
{% if service == 'haproxy' %}
<link href="/inc/chart.min.css" rel="stylesheet">
<script src="/inc/overview.js"></script>
<script src="/inc/metrics.js"></script>
<script src="/inc/chart.min.js"></script>
<div id="table_metrics"></div>
<div style="padding-left: 25px;float: left;margin-top: 3px;">
<b>Time range:</b>
<select title="Choose time range" id="time-range">
@ -129,6 +156,7 @@
<canvas id="waf_{{s.server.ip}}" role="img"></canvas>
</div>
{% endfor %}
{% endif %}
<div id="dialog-confirm" style="display: none;">
<p><span class="ui-icon ui-icon-alert" style="float:left; margin:3px 12px 20px 0;"></span>Are you sure?</p>
</div>
@ -136,21 +164,23 @@
{{s.ip}}
{% endfor %}
<script>
{%- if service == 'haproxy' %}
function showWafMetrics() {
{% for s in servers %}
{%- for s in servers %}
getWafChartData('{{s.server.ip}}');
{% endfor %}
{%- endfor %}
}
showWafMetrics();
{%- endif %}
showOverviewWaf(ip, hostnamea);
$( function() {
$("#time-range").on('selectmenuchange', function () {
removeData()
let metrics = new Promise(
(resolve, reject) => {
{% for s in servers %}
{%- for s in servers %}
getWafChartData('{{s.server.ip}}')
{% endfor %}
{%- endfor %}
});
metrics.then();
});

View File

@ -1,4 +1,7 @@
#!/usr/bin/env python3
import os
import sys
import funct
import sql
from jinja2 import Environment, FileSystemLoader
@ -8,11 +11,13 @@ template = env.get_template('waf.html')
form = funct.form
manage_rules = form.getvalue('manage_rules')
waf_rule_id = form.getvalue('waf_rule_id')
service = form.getvalue('service')
serv = form.getvalue('serv')
config_file_name = ''
waf_rule_file = ''
servers_waf = ''
autorefresh = 0
config_read = ''
serv = ''
rules = ''
cfg = ''
@ -21,7 +26,7 @@ funct.check_login(service=1)
funct.page_for_admin(level=2)
try:
user, user_id, role, token, servers, user_services = funct.get_users_params()
user, user_id, role, token, servers, user_services = funct.get_users_params(haproxy=1)
except Exception:
pass
@ -29,16 +34,22 @@ if manage_rules == '1':
serv = funct.is_ip_or_dns(form.getvalue('serv'))
funct.check_is_server_in_group(serv)
title = "Manage rules - Web application firewall"
rules = sql.select_waf_rules(serv)
elif waf_rule_id:
rules = sql.select_waf_rules(serv, service)
elif waf_rule_id and form.getvalue('config') is None:
serv = funct.is_ip_or_dns(form.getvalue('serv'))
service = form.getvalue('service')
funct.check_is_server_in_group(serv)
title = 'Edit a WAF rule'
waf_rule_file = sql.select_waf_rule_by_id(waf_rule_id)
configs_dir = sql.get_setting('tmp_config_path')
cfg = configs_dir + serv + "-" + funct.get_data('config') + "-" + waf_rule_file
error = funct.get_config(serv, cfg, waf=1, waf_rule_file=waf_rule_file)
error = funct.get_config(serv, cfg, waf=service, waf_rule_file=waf_rule_file)
if service == 'haproxy':
config_path = sql.get_setting('haproxy_dir')
elif service == 'nginx':
config_path = sql.get_setting('nginx_dir')
config_file_name = funct.return_nice_path(config_path) + 'waf/rules/' + waf_rule_file
try:
conf = open(cfg, "r")
config_read = conf.read()
@ -50,9 +61,40 @@ else:
servers_waf = sql.select_waf_servers_metrics(user_id.value)
autorefresh = 1
if serv is not None and form.getvalue('config') is not None:
funct.check_is_server_in_group(serv)
configs_dir = sql.get_setting('tmp_config_path')
cfg = configs_dir + serv + "-" + funct.get_data('config')
config_file_name = form.getvalue('config_file_name')
config = form.getvalue('config')
oldcfg = form.getvalue('oldconfig')
save = form.getvalue('save')
try:
with open(cfg, "a") as conf:
conf.write(config)
except IOError:
print("error: Cannot read imported config file")
stderr = funct.master_slave_upload_and_restart(serv, cfg, just_save=save, waf=1, oldcfg=oldcfg, config_file_name=config_file_name)
funct.diff_config(oldcfg, cfg)
try:
os.system("/bin/rm -f " + configs_dir + "*.old")
except Exception as e:
print('error: ' + str(e))
if stderr:
print(stderr)
sys.exit()
rendered_template = template.render(
h2=1, title=title, autorefresh=autorefresh, role=role, user=user, serv=serv, servers=servers_waf,
servers_all=servers, manage_rules=manage_rules, rules=rules, user_services=user_services,
waf_rule_file=waf_rule_file, waf_rule_id=waf_rule_id, config=config_read, cfg=cfg, token=token
waf_rule_file=waf_rule_file, waf_rule_id=waf_rule_id, config=config_read, cfg=cfg, token=token,
config_file_name=config_file_name, service=service
)
print(rendered_template)

View File

@ -170,6 +170,8 @@ function ajaxActionNginxServers(action, id) {
toastr.error(data);
} else if (cur_url[0] == "hapservers.py") {
location.reload()
} else if (cur_url[0] == "waf.py") {
setTimeout(showOverviewWaf(ip, hostnamea), 2000)
} else {
setTimeout(showOverview(ip, hostnamea), 2000)
}
@ -237,7 +239,6 @@ function ajaxActionApacheServers(action, id) {
} );
}
function ajaxActionWafServers(action, id) {
var bad_ans = 'Bad config, check please';
$.ajax( {
url: "options.py",
data: {
@ -260,6 +261,29 @@ function ajaxActionWafServers(action, id) {
}
} );
}
function ajaxActionWafNginxServers(action, id) {
$.ajax( {
url: "options.py",
data: {
action_waf_nginx: action,
serv: id,
token: $('#token').val()
},
success: function( data ) {
data = data.replace(/\s+/g,' ');
if (data.indexOf('error:') != '-1') {
toastr.error(data);
} else if( data == 'Bad config, check please ' ) {
toastr.error(data);
} else {
setTimeout(showOverviewWaf(ip, hostnamea), 2000)
}
},
error: function(){
alert(w.data_error);
}
} );
}
$( function() {
try {
if ((cur_url[0] == 'hapservers.py' && cur_url[1].split('&')[1].split('=')[0] == 'serv') || cur_url[0] == 'overview.py') {
@ -375,6 +399,8 @@ function confirmAjaxAction(action, service, id) {
ajaxActionKeepalivedServers(action, id)
} else if (service == "apache") {
ajaxActionApacheServers(action, id)
} else if (service == "waf_nginx") {
ajaxActionWafNginxServers(action, id)
}
},
Cancel: function() {

View File

@ -80,6 +80,10 @@ $( function() {
show_current_page($(this))
} else if(cur_url[0] == 'add.py' && cur_url[1].split('&')[0] == 'service=apache#ssl' && link2 == 'add.py?service=apache#ssl'){
show_current_page($(this))
} else if(cur_url[0] == 'waf.py' && cur_url[1].split('&')[0] == 'service=haproxy' && link2 == 'waf.py?service=haproxy'){
show_current_page($(this))
} else if(cur_url[0] == 'waf.py' && cur_url[1].split('&')[0] == 'service=nginx' && link2 == 'waf.py?service=nginx'){
show_current_page($(this))
}
});
});
@ -318,9 +322,14 @@ function openVersions() {
function showLog() {
var waf = findGetParameter('waf');
var file = $('#log_files').val();
if (file === null) {
if ((file === undefined || file === null) && waf != '1') {
var file_from_get = findGetParameter('file');
if (file_from_get === undefined || file_from_get === null) {
toastr.warning('Select a log file first')
return false;
} else {
file = file_from_get;
}
}
var rows = $('#rows').val()
var grep = $('#grep').val()

View File

@ -1176,7 +1176,7 @@ function addTelegram(dialog_id) {
function addSlack(dialog_id) {
var valid = true;
toastr.clear();
allFields = $( [] ).add( $('#slack-token-add') ).add( $('#slack-chanel-add') )
allFields = $( [] ).add( $('#slack-token-add') ).add( $('#slack-chanel-add') );
allFields.removeClass( "ui-state-error" );
valid = valid && checkLength( $('#slack-token-add'), "token", 1 );
valid = valid && checkLength( $('#slack-chanel-add'), "channel name", 1 );

View File

@ -1,7 +1,10 @@
var awesome = "/inc/fontawesome.min.js"
function showOverviewWaf(serv, hostnamea) {
var service = findGetParameter('service');
if (service == 'haproxy') {
$.getScript('/inc/chart.min.js');
showWafMetrics();
}
var i;
for (i = 0; i < serv.length; i++) {
showOverviewWafCallBack(serv[i], hostnamea[i])
@ -10,11 +13,13 @@ function showOverviewWaf(serv, hostnamea) {
$.getScript('/inc/waf.js');
}
function showOverviewWafCallBack(serv, hostnamea) {
var service = findGetParameter('service');
$.ajax( {
url: "options.py",
data: {
act: "overviewwaf",
serv: serv,
service: service,
token: $('#token').val()
},
beforeSend: function() {
@ -56,10 +61,12 @@ function metrics_waf(name) {
function installWaf(ip1) {
$("#ajax").html('')
$("#ajax").html(wait_mess);
var service = findGetParameter('service');
$.ajax( {
url: "options.py",
data: {
installwaf: ip1,
service: service,
token: $('#token').val()
},
type: "POST",
@ -72,7 +79,7 @@ function installWaf(ip1) {
toastr.info(data);
} else if (data.indexOf('success') != '-1' ){
toastr.clear();
toastr.success('WAF service has installed');
toastr.success('WAF service has been installed');
showOverviewWaf(ip, hostnamea)
}
}
@ -80,13 +87,14 @@ function installWaf(ip1) {
}
function changeWafMode(id) {
var waf_mode = $('#'+id+' option:selected').val();
console.log('1')
var server_hostname = id.split('_')[0];
var service = findGetParameter('service');
$.ajax( {
url: "options.py",
data: {
change_waf_mode: waf_mode,
server_hostname: server_hostname,
service: service,
token: $('#token').val()
},
type: "POST",
@ -106,7 +114,6 @@ $( function() {
});
});
function waf_rules_en(id) {
console.log('1')
var enable = 0;
if ($('#rule_id-'+id).is(':checked')) {
enable = '1';
@ -134,3 +141,67 @@ function waf_rules_en(id) {
}
} );
}
function addNewConfig() {
$( "#add-new-config" ).dialog({
autoOpen: true,
resizable: false,
height: "auto",
width: 600,
modal: true,
title: "Create a new rule",
show: {
effect: "fade",
duration: 200
},
hide: {
effect: "fade",
duration: 200
},
buttons: {
"Create": function() {
var valid = true;
allFields = $( [] ).add( $('#new_rule_name') ).add( $('#new_rule_description') )
allFields.removeClass( "ui-state-error" );
valid = valid && checkLength( $('#new_rule_name'), "New rule name", 1 );
valid = valid && checkLength( $('#new_rule_description'), "New rule description", 1 );
if(valid) {
let new_rule_name = $('#new_rule_name').val();
let new_rule_description = $('#new_rule_description').val();
let new_rule_file = new_rule_name.replaceAll(' ','_');
var service = findGetParameter('service');
var serv = findGetParameter('serv');
service = escapeHtml(service);
new_rule_name = escapeHtml(new_rule_name);
new_rule_description = escapeHtml(new_rule_description);
new_rule_file = escapeHtml(new_rule_file);
serv = escapeHtml(serv);
$.ajax({
url: "options.py",
data: {
new_waf_rule: new_rule_name,
new_rule_description: new_rule_description,
new_rule_file: new_rule_file,
service: service,
serv: serv,
token: $('#token').val()
},
type: "POST",
success: function (data) {
if (data.indexOf('error:') != '-1') {
toastr.error(data);
} else {
var getId = new RegExp('[0-9]+');
var id = data.match(getId) + '';
window.location.replace('waf.py?service=' + service + '&waf_rule_id=' + id + '&serv=' + serv);
}
}
});
$( this ).dialog( "close" );
}
},
Cancel: function() {
$( this ).dialog( "close" );
}
}
});
}