REST API
pull/181/head
Pavel Loginov 2019-10-28 21:03:14 +03:00
parent 15c2582630
commit ea8b11398e
13 changed files with 370 additions and 56 deletions

View File

@ -62,14 +62,14 @@ yum install haproxy-wi
For install just clone:
```
CentOS:
$ sudo yum -y install git nmap-ncat net-tools python35u dos2unix python35u-pip mod_ssl httpd python35u-devel gcc-c++ openldap-devel python-devel python-jinja2
$ sudo yum -y install git nmap-ncat net-tools python35u dos2unix python35u-pip mod_ssl httpd python35u-devel gcc-c++ openldap-devel python-devel python-jinja2 python35u-mod_wsgi
$ cd /var/www/
$ git clone https://github.com/Aidaho12/haproxy-wi.git /var/www/haproxy-wi
$ chown -R apache:apache haproxy-wi/
Or if use Debian/Ubuntu:
$ sudo apt-get install git net-tools lshw dos2unix apache2 gcc netcat python3.5 mod_ssl python3-pip g++ freetype2-demos libatlas-base-dev openldap-dev libpq-dev python-dev libxml2-dev libxslt1-dev libldap2-dev libsasl2-dev libffi-dev python3-dev libssl-dev -y
$ sudo apt-get install git net-tools lshw dos2unix apache2 gcc netcat python3.5 python3.5-mod_wsgi mod_ssl python3-pip g++ freetype2-demos libatlas-base-dev openldap-dev libpq-dev python-dev libxml2-dev libxslt1-dev libldap2-dev libsasl2-dev libffi-dev python3-dev libssl-dev -y
$ chown -R www-data:www-data haproxy-wi/
Both
@ -103,6 +103,16 @@ For Apache do virtualhost with cgi-bin. Like this:
```
# vi /etc/httpd/conf.d/haproxy-wi.conf
<VirtualHost *:8080>
WSGIDaemonProcess api user=apache group=apache processes=1 threads=5
WSGIScriptAlias /api /var/www/haproxy-wi/api/app.wsgi
<Directory /var/www/haproxy-wi/api>
WSGIProcessGroup api
WSGIApplicationGroup %{GLOBAL}
Order deny,allow
Allow from all
</Directory>
SSLEngine on
SSLCertificateFile /var/www/haproxy-wi/app/certs/haproxy-wi.crt
SSLCertificateKeyFile /var/www/haproxy-wi/app/certs/haproxy-wi.key
@ -117,10 +127,10 @@ For Apache do virtualhost with cgi-bin. Like this:
ScriptAlias /cgi-bin/ "/var/www/haproxy-wi/app/"
<Directory /var/www/haproxy-wi/app>
Options +ExecCGI
AddHandler cgi-script .py
Order deny,allow
Allow from all
Options +ExecCGI
AddHandler cgi-script .py
Order deny,allow
Allow from all
</Directory>
<Directory /var/www/haproxy-wi/app/certs>
@ -129,34 +139,34 @@ For Apache do virtualhost with cgi-bin. Like this:
Deny from all
</Directory>
<Directory /var/www/haproxy-wi/keys>
Options +ExecCGI -Indexes +MultiViews
Order Deny,Allow
Deny from all
</Directory>
<Directory /var/www/haproxy-wi/keys>
Options +ExecCGI -Indexes +MultiViews
Order Deny,Allow
Deny from all
</Directory>
<FilesMatch "\.cfg$">
Order Deny,Allow
Deny from all
</FilesMatch>
<FilesMatch "\.db$">
Order Deny,Allow
Deny from all
</FilesMatch>
<IfModule mod_headers.c>
Header set X-XSS-Protection: 1;
Header set X-Frame-Options: deny
Header set X-Content-Type-Options: nosniff
Header set Strict-Transport-Security: max-age=3600;
Header set Cache-Control no-cache
Header set Expires: 0
<FilesMatch "\.cfg$">
Order Deny,Allow
Deny from all
</FilesMatch>
<FilesMatch "\.db$">
Order Deny,Allow
Deny from all
</FilesMatch>
<IfModule mod_headers.c>
Header set X-XSS-Protection: 1;
Header set X-Frame-Options: deny
Header set X-Content-Type-Options: nosniff
Header set Strict-Transport-Security: max-age=3600;
Header set Cache-Control no-cache
Header set Expires: 0
<filesMatch ".(ico|css|js|gif|jpeg|jpg|png|svg|woff|ttf|eot)$">
Header set Cache-Control "max-age=86400, public"
</filesMatch>
</IfModule>
<filesMatch ".(ico|css|js|gif|jpeg|jpg|png|svg|woff|ttf|eot)$">
Header set Cache-Control "max-age=86400, public"
</filesMatch>
</IfModule>
</VirtualHost>
```

132
api/api.py Normal file
View File

@ -0,0 +1,132 @@
import os
import sys
os.chdir(os.path.dirname(__file__))
sys.path.append(os.path.dirname(__file__))
sys.path.append(os.path.join(sys.path[0], '/var/www/haproxy-wi/app/'))
os.chdir(os.path.dirname(__file__))
from bottle import route, run, template, hook, response, request
import sql
import funct
import api_funct
import json
_error_auth = '403 Auth before'
_allow_origin = '*'
_allow_methods = 'PUT, GET, POST, DELETE, OPTIONS'
_allow_headers = 'Authorization, Origin, Accept, Content-Type, X-Requested-With'
@hook('before_request')
def check_login():
try:
login = request.headers.get('login')
password_from_user = request.headers.get('password')
USERS = sql.select_users(user=login)
password = funct.get_hash(password_from_user)
except:
return False
for users in USERS:
if users[7] == 0:
return False
if login in users[1] and password == users[3]:
return True
else:
return False
@hook('after_request')
def enable_cors():
'''Add headers to enable CORS'''
response.headers['Access-Control-Allow-Origin'] = _allow_origin
response.headers['Access-Control-Allow-Methods'] = _allow_methods
response.headers['Access-Control-Allow-Headers'] = _allow_headers
@route('/', method=['GET', 'POST'])
@route('/help', method=['GET', 'POST'])
def index():
if not check_login():
return dict(error=_error_auth)
data = {
'servers':'show info about all servers',
'server/<id,hostname,ip>':'show info about server by id or hostname or ip',
'server/<id,hostname,ip>/status':'show HAProxy status by id or hostname or ip',
'server/<id,hostname,ip>/runtime':'exec HAProxy runtime commands by id or hostname or ip',
'server/<id,hostname,ip>/backends':'show backends by id or hostname or ip',
'server/<id,hostname,ip>/action/start':'start HAProxy service by id or hostname or ip',
'server/<id,hostname,ip>/action/stop':'stop HAProxy service by id or hostname or ip',
'server/<id,hostname,ip>/action/restart':'restart HAProxy service by id or hostname or ip'
}
return dict(help=data)
@route('/servers', method=['GET', 'POST'])
def get_servers():
if not check_login():
return dict(error=_error_auth)
try:
login = request.headers.get('login')
servers = sql.get_dick_permit(username=login)
data = {}
for s in servers:
data[s[0]] = {
'id':s[0],
'hostname':s[1],
'ip':s[2],
'group':s[3],
'virt':s[4],
'enable':s[5],
'is_master':s[6],
'creds':s[7],
'alert':s[8],
'metrics':s[9]
}
except:
pass
return dict(servers=data)
@route('/server/<id>', method=['GET', 'POST'])
@route('/server/<id:int>', method=['GET', 'POST'])
def callback(id):
if not check_login():
return dict(error=_error_auth)
return api_funct.get_server(id)
@route('/server/<id>/status', method=['GET', 'POST'])
@route('/server/<id:int>/status', method=['GET', 'POST'])
def callback(id):
if not check_login():
return dict(error=_error_auth)
return api_funct.get_status(id)
@route('/server/<id>/action/<action:re:[a-z]+>', method=['GET', 'POST'])
@route('/server/<id:int>/action/<action:re:[a-z]+>', method=['GET', 'POST'])
def callback(id, action):
if not check_login():
return dict(error=_error_auth)
return api_funct.actions(id, action)
@route('/server/<id>/runtime', method=['GET', 'POST'])
@route('/server/<id:int>/runtime', method=['GET', 'POST'])
def callback(id):
if not check_login():
return dict(error=_error_auth)
return api_funct.runtime(id)
@route('/server/<id>/backends', method=['GET', 'POST'])
@route('/server/<id:int>/backends', method=['GET', 'POST'])
def callback(id):
if not check_login():
return dict(error=_error_auth)
return api_funct.show_backends(id)

135
api/api_funct.py Normal file
View File

@ -0,0 +1,135 @@
import os
import sys
os.chdir(os.path.dirname(__file__))
sys.path.append(os.path.dirname(__file__))
sys.path.append(os.path.join(sys.path[0], '/var/www/haproxy-wi/app/'))
from bottle import route, run, template, hook, response, request
import sql
import funct
def return_dict_from_out(id, out):
data = {}
data[id] = {}
for k in out:
if "Ncat:" not in k:
k = k.split(':')
data[id][k[0]] = k[1].strip()
else:
data[id] = {"error":"Can\'t connect to HAproxy"}
return data
def check_permit_to_server(id):
servers = sql.select_servers(id_hostname=id)
login = request.headers.get('login')
for s in servers:
servers = sql.get_dick_permit(username=login, ip=s[2])
return servers
def get_server(id):
data = {}
try:
servers = check_permit_to_server(id)
for s in servers:
data = {
'id':s[0],
'hostname':s[1],
'ip':s[2],
'group':s[3],
'virt':s[4],
'enable':s[5],
'master':s[6],
'creds':s[7],
'alert':s[8],
'metrics':s[9]
}
except:
server = data
return dict(server=data)
def get_status(id):
try:
servers = check_permit_to_server(id)
for s in servers:
cmd = 'echo "show info" |nc %s %s -w 1|grep -e "Ver\|CurrConns\|Maxco\|MB\|Uptime:"' % (s[2], sql.get_setting('haproxy_sock_port'))
out = funct.subprocess_execute(cmd)
data = return_dict_from_out(id, out[0])
except:
data = {}
data[id] = {"error":"Cannot find the server"}
return dict(error=data)
return dict(status=data)
def actions(id, action):
if action == 'start' or action == 'stop' or action == 'restart':
try:
servers = check_permit_to_server(id)
for s in servers:
cmd = [ "sudo systemctl %s haproxy" % action ]
error = funct.ssh_command(s[2], cmd)
done = error if error else 'done'
data = {'id':s[0],'ip':s[2],'action':action,'hostname':s[1],'status':done}
return dict(status=data)
except:
return dict(status='error')
else:
return dict(status='wrong action')
def runtime(id):
data = {}
try:
action = request.headers.get('action')
haproxy_sock = sql.get_setting('haproxy_sock')
servers = check_permit_to_server(id)
cmd = [ 'echo "%s" |sudo socat stdio %s' % (action, haproxy_sock) ]
for s in servers:
out = funct.ssh_command(s[2], cmd)
data = {}
data[id] = {}
sep_data = out.split('\r\n')
data[id] = {'ouput':sep_data}
return dict(status=data)
except:
return dict(status='error')
def show_backends(id):
data = {}
try:
servers = check_permit_to_server(id)
for s in servers:
out = funct.show_backends(s[2], ret=1)
data = {id: out}
except:
data = {}
data[id] = {"error":"Cannot find the server"}
return dict(error=data)
return dict(backends=data)

8
api/app.wsgi Normal file
View File

@ -0,0 +1,8 @@
import os, sys
sys.path.append(os.path.join(sys.path[0], '/var/www/haproxy-wi/app/'))
sys.path.append(os.path.dirname(__file__))
import api
import bottle
bottle.debug(True)
application = bottle.default_app()

View File

@ -14,7 +14,7 @@ if mysql_enable == '1':
from mysql.connector import errorcode
import mysql.connector as sqltool
else:
db = funct.get_app_dir()+"/haproxy-wi.db"
db = "/var/www/haproxy-wi/app/haproxy-wi.db"
import sqlite3 as sqltool
def check_db():
@ -438,7 +438,7 @@ def update_db_v_3_5_3(**kwargs):
def update_ver(**kwargs):
con, cur = get_cur()
sql = """update version set version = '3.5.8'; """
sql = """update version set version = '3.6'; """
try:
cur.execute(sql)
con.commit()

View File

@ -13,7 +13,7 @@ def get_app_dir():
def get_config_var(sec, var):
from configparser import ConfigParser, ExtendedInterpolation
try:
path_config = get_app_dir()+"/haproxy-wi.cfg"
path_config = "/var/www/haproxy-wi/app/haproxy-wi.cfg"
config = ConfigParser(interpolation=ExtendedInterpolation())
config.read(path_config)
except:
@ -120,8 +120,10 @@ def check_login(**kwargs):
sql.update_last_act_user(user_uuid.value)
if sql.get_user_name_by_uuid(user_uuid.value) is None:
print('<meta http-equiv="refresh" content="0; url=login.py?ref=%s">' % ref)
return False
else:
print('<meta http-equiv="refresh" content="0; url=login.py?ref=%s">' % ref)
return False
def is_admin(**kwargs):
import sql
@ -428,19 +430,29 @@ def upload(serv, path, file, **kwargs):
ssh = ssh_connect(serv)
except Exception as e:
error = e
logging('localhost', e, haproxywi=1)
logging('localhost', str(e.args[0]), haproxywi=1)
pass
try:
sftp = ssh.open_sftp()
except Exception as e:
logging('localhost', str(e.args[0]), haproxywi=1)
try:
file = sftp.put(file, full_path)
except Exception as e:
logging('localhost', ' Cannot upload '+file+' to '+full_path+'. Error: '+str(e.args), haproxywi=1)
pass
try:
sftp.close()
ssh.close()
except Exception as e:
error = e
logging('localhost', e, haproxywi=1)
error = e.args
logging('localhost', str(error[0]), haproxywi=1)
pass
return error
return str(error)
def upload_and_restart(serv, cfg, **kwargs):

View File

@ -34,7 +34,7 @@ def send_cookie(login):
c = http.cookies.SimpleCookie(os.environ.get("HTTP_COOKIE"))
c["uuid"] = user_uuid
c["uuid"]["path"] = "/app/"
c["uuid"]["path"] = "/"
c["uuid"]["expires"] = expires.strftime("%a, %d %b %Y %H:%M:%S GMT")
print(c)
sql.write_user_uuid(login, user_uuid)
@ -52,7 +52,7 @@ def ban():
c = http.cookies.SimpleCookie(os.environ.get("HTTP_COOKIE"))
expires = datetime.datetime.utcnow() + datetime.timedelta(seconds=10)
c["ban"] = 1
c["ban"]["path"] = "/app/"
c["ban"]["path"] = "/"
c["ban"]["expires"] = expires.strftime("%a, %d %b %Y %H:%M:%S GMT")
try:
funct.logging('locahost', login+' failed log in', haproxywi=1, login=1)
@ -123,7 +123,7 @@ if form.getvalue('logout'):
sql.delete_uuid(user_id.value)
except:
pass
print("Set-cookie: uuid=; expires=Wed May 18 03:33:20 2003; path=/app/; httponly")
print("Set-cookie: uuid=; expires=Wed, May 18 03:33:20 2003; path=/; httponly")
print("Content-type: text/html\n")
print('<meta http-equiv="refresh" content="0; url=/app/login.py">')
sys.exit()

View File

@ -78,7 +78,7 @@ if serv and form.getvalue('ssl_cert'):
os.makedirs(cert_local_dir)
if form.getvalue('ssl_name') is None:
print('<div class="alert alert-danger">Please enter desired name</div>')
print('<div class="alert alert-danger" style="float: left;">Please enter desired name</div>')
else:
name = form.getvalue('ssl_name') + '.pem'
@ -86,9 +86,9 @@ if serv and form.getvalue('ssl_cert'):
with open(name, "w") as ssl_cert:
ssl_cert.write(form.getvalue('ssl_cert'))
except IOError:
print('<div class="alert alert-danger">Can\'t save ssl keys file. Check ssh keys path in config</div>')
print('<div class="alert alert-danger style="float: left;"">Can\'t save ssl keys file. Check ssh keys path in config</div>')
else:
print('<div class="alert alert-success">SSL file was upload to %s into: %s </div>' % (serv, cert_path))
print('<div class="alert alert-success" style="float: left;">SSL file was upload to %s into: %s %s</div>' % (serv, cert_path, name))
MASTERS = sql.is_master(serv)
for master in MASTERS:
@ -96,10 +96,13 @@ if serv and form.getvalue('ssl_cert'):
funct.upload(master[0], cert_path, name)
try:
funct.upload(serv, cert_path, name)
except:
pass
os.system("mv %s %s" % (name, cert_local_dir))
except Exception as e:
funct.logging('localhost', e.args[0], haproxywi=1)
try:
os.system("mv %s %s" % (name, cert_local_dir))
except OSError as e:
funct.logging('localhost', e.args[0], haproxywi=1)
funct.logging(serv, "add.py#ssl upload new ssl cert %s" % name)

View File

@ -284,6 +284,12 @@ def select_servers(**kwargs):
left join uuid as uuid on user.id = uuid.user_id
where uuid.uuid = '%s' and servers.master = 0 and servers.type_ip = 0 and servers.enable = 1 ORDER BY servers.groups
""" % kwargs.get('uuid')
if kwargs.get("id"):
sql = """select * from servers where id='%s' """ % kwargs.get("id")
if kwargs.get("hostname"):
sql = """select * from servers where hostname='%s' """ % kwargs.get("hostname")
if kwargs.get("id_hostname"):
sql = """select * from servers where hostname='%s' or id = '%s' or ip = '%s'""" % (kwargs.get("id_hostname"), kwargs.get("id_hostname"), kwargs.get("id_hostname"))
if kwargs.get("server") and kwargs.get("keep_alive"):
sql = """select active from servers where ip='%s' """ % kwargs.get("server")
try:

View File

@ -1,7 +1,7 @@
{% for service in service_status %}
<tr class="{{ loop.cycle('odd', 'even') }}">
<td class="padding10 first-collumn">
<a href="/app/logs.py?serv={{ service.1 }}&rows=10&grep=&hour=00&minut=00&hour1=24&minut1=00&waf=0" title="View {{service.0 }}'s logs" class="logs_link">
<a href="/app/hapservers.py?serv={{ service.1 }}" title="{{service.0 }}'s overview" class="logs_link">
{{ service.0 }}
</a>
</td>
@ -18,7 +18,7 @@
{% elif service.5.0 != '' and service.4|int() == 0 %}
<span class="serverDown server-status" title="WAF down" style="margin-left: 10px !important;"></span>
{% elif service.5.0 != '' and service.4|int() >= 1 %}
<span class="serverUp server-status" title="running {{service.5 }} processes" style="margin-left: 10px !important;"></span>
<span class="serverUp server-status" title="running {{service.4 }} processes" style="margin-left: 10px !important;"></span>
{% endif %}
</td>
<td></td>

View File

@ -1,5 +1,15 @@
<VirtualHost *:443>
SSLEngine on
WSGIDaemonProcess api user=apache group=apache processes=1 threads=5
WSGIScriptAlias /api /var/www/haproxy-wi/api/app.wsgi
<Directory /var/www/haproxy-wi/api>
WSGIProcessGroup api
WSGIApplicationGroup %{GLOBAL}
Order deny,allow
Allow from all
</Directory>
SSLEngine on
SSLCertificateFile /var/www/haproxy-wi/app/certs/haproxy-wi.crt
SSLCertificateKeyFile /var/www/haproxy-wi/app/certs/haproxy-wi.key

View File

@ -791,9 +791,6 @@ $( function() {
} else if (data.indexOf('success') != '-1') {
$('.alert-danger').remove();
$( "#ajax-ssl").html(data);
setTimeout(function() {
$( "#ajax-ssl").html("");
}, 2500 );
} else {
$("#ajax-ssl").html('<div class="alert alert-danger">Something wrong, check and try again</div>');
}
@ -819,7 +816,7 @@ $( function() {
data = data.split("\n");
for (i = 0; i < data.length; i++) {
data[i] = data[i].replace(/\s+/g,' ');
new_data += ' <a onclick="view_ssl(\''+data[i]+'\')" style="cursor: pointer;" title="View this cert">'+data[i]+'</a> '
}
$("#ajax-show-ssl").html("<b>"+new_data+"</b>");

View File

@ -11,3 +11,4 @@ future==0.13.1
mysql-connector-python==8.0.11
Jinja2>=2.10.1
python-ldap>=3.1.0
bottle>=0.12.17