Created
December 28, 2015 12:03
-
-
Save kmonsoor/87e2dd9dc6b1d1033cdf to your computer and use it in GitHub Desktop.
to allow verified domain users the ability to add sites to a Squid Proxy whitelist via a secure web form
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
''' | |
SOURCE: http://pastebin.com/48G4MS8E | |
The code below was used to allow verified domain users the ability to add sites to a Squid Proxy whitelist via a secure web form. | |
It deals with Flask-Apache-SSL-LDAP-Email-Sockets-Logging and other topics | |
I make no claims as to it's suitability for other purposes. | |
I am not posting this for anyones approval nor is it here to be shot down. | |
This code is provided as-is simply to assist others in trying to setup a similar Python(Flask)->Apache->SSL deployment. | |
All code was run on Windows Server 2008 R2 (64bit) { don't judge me :) } | |
The following (Win 32 bit ONLY) software versions were used: | |
Python 2.7.10 | |
Apache 2.4.17-win32-VC14 + SSL | |
mod_wsgi-4.4.21+ap24vc9-cp27-none-win32 | |
pywin32-219.win32-py2.7 | |
MS Visual C++ 2015 Redist x86 | |
Squid 3.5.5 | |
*************** | |
** webapp.py ** This file goes in your Flask App 'root' folder | |
*************** | |
''' | |
from flask import Flask, render_template, request | |
import ldap | |
import re | |
import os | |
import win32serviceutil | |
import socket | |
import time | |
import smtplib | |
from email.mime.text import MIMEText | |
app = Flask(__name__) | |
@app.route("/") | |
# display web page | |
def main(): | |
return render_template('index.htm') | |
# Sends an email to {admin_email@address} to notify the change | |
def send_email_to_ME(domain,username,url): | |
try: | |
_from = '{server_email@address}' | |
_to = '{admin_email@address}' | |
_smtp_server = '{smtp server IP or URL}' | |
msg = MIMEText('%s\%s added the URL: %s to the {your server name} whitelist on %s\n\nActions:\n\tCheck and validate the URL.' %(domain,username,url,time.strftime("%Y-%m-%d %H:%M"))) | |
msg['Subject'] = 'New Addition To Whitelist' | |
msg['From'] = _from | |
msg['To'] = _to | |
s = smtplib.SMTP(_smtp_server) | |
s.sendmail(_from, [_to], msg.as_string()) | |
s.quit | |
return True | |
except Exception as e: | |
write_to_admin_log(e) | |
return False | |
# checks the hostname is valid | |
def is_valid_hostname(hostname): | |
try: | |
if socket.gethostbyname(hostname): | |
return True | |
else: | |
return False | |
except Exception as e: | |
write_to_admin_log(e) | |
return False | |
# scans to the end of the whitelist file then adds the new URL | |
def add_to_whitelist(url): | |
try: | |
with open("{full path to whitelist}", "r+") as fo: | |
for line in fo: | |
pass | |
with open("{full path to whitelist}", "a") as fo: | |
if line.endswith('\n'): | |
fo.write(".%s\n" % url) | |
else: | |
fo.write("\n.%s\n" % url) | |
return True | |
except Exception as e: | |
write_to_admin_log(e) | |
return False | |
# returns True if URL is NOT in the whitelist | |
def not_in_whitelist(url): | |
try: | |
test = True; | |
with open("{full path to whitelist}", "r+") as fo: | |
for line in fo: | |
if '.'+url == line.replace("\n", ""): | |
test = False | |
break | |
return test | |
except Exception as e: | |
write_to_admin_log(e) | |
return False | |
# Does an LDAP bind to verify the user credentials | |
def is_user_valid (username, password, domain=None): | |
Server = "ldap://"+domain+".your.domain.com" | |
DN = domain+"\\"+username | |
Base = "dc="+domain+",dc=your,dc=domain,dc=com" | |
Scope = ldap.SCOPE_SUBTREE | |
Filter = "(&(objectClass=user)(sAMAccountName="+username+"))" | |
try: | |
l = ldap.initialize(Server) | |
l.set_option(ldap.OPT_REFERRALS, 0) | |
l.protocol_version = 3 | |
s = l.simple_bind_s(DN, password) | |
l.unbind() | |
if s: | |
return True | |
else: | |
return False | |
except Exception as e: | |
write_to_admin_log(e) | |
return False | |
# Restarts the required service by name | |
def restart_service(service): | |
try: | |
win32serviceutil.RestartService(service) | |
return True | |
except Exception as e: | |
write_to_admin_log(e) | |
return False | |
# Writes out to a log file recording the whitelist change | |
def write_to_whitelist_log(url,username): | |
try: | |
with open("{full path to whitelist log file}", "a+") as fo: | |
fo.write('%s added by %s on %s\n' %(url,username,time.strftime("%Y-%m-%d %H:%M"))) | |
return True | |
except Exception as e: | |
write_to_admin_log(e) | |
return False | |
# Writes out (mostly errors) to an admin log file | |
def write_to_admin_log(log_msg): | |
try: | |
with open("{full path to admin log file}", "a+") as fo: | |
fo.write('%s - %s\n' %(time.strftime("%Y-%m-%d %H:%M"), log_msg)) | |
return True | |
except Exception as e: | |
print e | |
return False | |
# Handles the form being submitted | |
@app.route('/addurl',methods=['POST']) | |
def addURL(): | |
_url = str(request.form['url']) | |
_user = str(request.form['user']) | |
_pass = str(request.form['pass']) | |
_dom = str(request.form['domain']) | |
if _url and _user and _pass: | |
if is_user_valid(_user,_pass,_dom): | |
if is_valid_hostname(_url): | |
if not_in_whitelist(_url): | |
if add_to_whitelist(_url): | |
write_to_whitelist_log(_url,_user) | |
restart_service('squidsrv') | |
send_email_to_ME(_dom,_user,_url) | |
return "<font style='color:#00ff00;'>URL added to whitelist.</font>" | |
else: | |
write_to_admin_log('ERROR::Failed to add URL') | |
return "<font style='color:#ff0000;'>Failed to add URL</font>" | |
else: | |
return "<font style='color:#ff0000;'>URL already whitelisted</font>" | |
else: | |
write_to_admin_log('ERROR::Invalid Hostname! - %s' % _url) | |
return "<font style='color:#ff0000;'>Invalid Hostname!</font>" | |
else: | |
write_to_admin_log('ERROR::Invalid User Credentials! - user:%s pass:%s' %(_user, _pass)) | |
return "<font style='color:#ff0000;'>Invalid User Credentials!</font>" | |
else: | |
return "<font style='color:#ff0000;'>Missing Information!</font>" | |
# starts the app locally (not used by Apache) | |
if __name__ == "__main__": | |
app.run(host="{your hostname}") | |
***************** | |
** webapp.wsgi ** Should reside in the same directory as webapp.py(above) | |
***************** | |
import sys | |
#Expand Python classes path with your app's path | |
sys.path.insert(0, "{full path to webapp.py directory}") | |
sys.path.append("{full path to squid whitelist directory}") | |
from webapp import app | |
#Initialize WSGI app object | |
application = app | |
***************** | |
** apache.conf ** | |
***************** | |
*** Note: Below are listed the changes needed to a default apache.conf file | |
Listen 443 | |
LoadModule socache_dbm_module modules/mod_socache_dbm.so | |
LoadModule ssl_module modules/mod_ssl.so | |
LoadModule wsgi_module modules/mod_wsgi.so | |
#Include conf/extra/httpd-ssl.conf | |
WSGIRestrictStdout Off | |
ServerName {server hostname} | |
SSLSessionCache "dbm:c:/Apache24/logs/ssl_scache" | |
SSLSessionCacheTimeout 300 | |
<VirtualHost *:443> | |
SSLEngine on | |
SSLProtocol all | |
SSLCipherSuite HIGH:MEDIUM | |
SSLCertificateFile "C:/apache24/SSL/certificate.cer" | |
SSLCertificateKeyFile "c:/apache24/SSL/certificate.key" | |
SSLCertificateChainFile "c:/apache24/ssl/intermediate.cer" | |
ServerName {server hostname} | |
WSGIScriptAlias / {full path to webapp.wsgi} | |
<Directory {full path to webapp.py directory}> | |
AllowOverride All | |
Order allow,deny | |
Allow from all | |
Require all granted | |
</Directory> | |
<Files *.wsgi> | |
MultiviewsMatch Any | |
SetHandler wsgi-script | |
Options ExecCGI | |
Order allow,deny | |
Allow from all | |
</Files> | |
</VirtualHost> | |
""" | |
*************** | |
** index.htm ** | |
*************** | |
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> | |
<html> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> | |
<title>Manage Whitelist</title> | |
<script src="../static/js/jquery.js"></script> | |
<link href="../static/main.css" rel="stylesheet"> | |
<script> | |
EnableSubmit = function(val) { | |
var sbmt = document.getElementById("btnAddURL"); | |
if (val.checked == true) { | |
sbmt.disabled = false; | |
} else { | |
sbmt.disabled = true; | |
} | |
} | |
</script> | |
</head> | |
<body> | |
<script> | |
$body = $("body"); | |
$(document).on({ | |
ajaxStart: function() { | |
$body.addClass("loading"); | |
}, | |
ajaxStop: function() { | |
$body.removeClass("loading"); | |
} | |
}); | |
$(function() { | |
$('#btnAddURL').click(function() { | |
$.ajax({ | |
url: '/addurl', | |
data: $('form').serialize(), | |
type: 'POST', | |
success: function(response) { | |
document.getElementById('responseMsg').innerHTML = response; | |
}, | |
error: function(response) { | |
document.getElementById('responseMsg').innerHTML = response; | |
} | |
}); | |
}); | |
}); | |
</script> | |
<div id="titles"> | |
<h2>Internet Gateway Whitelist Form</h2> | |
</div> | |
<hr> | |
<div id="content"> | |
<p>You can add a domain to the Internet Gateway Whitelist using the form below.</p> | |
<strike>https://www.google.co.uk/intl/en/about/</strike> | |
<br/> | |
<strike>https://www.google.co.uk</strike> | |
<br/> | |
<br/> | |
<form class="form-whitelist" method="post"> | |
<input type="text" name="url" id="url" class="form-control" placeholder="google.co.uk" required style="width: 485px;"> | |
<p>Please enter your domain/user credentials:</p> | |
<select name="domain"> | |
<option value="lima">LIma</option> | |
<option value="osca">OSca</option> | |
</select>\ | |
<input type="text" name="user" id="user" class="form-control" placeholder="username" required style="width: 200px;"> | |
<input type="password" name="pass" id="pass" class="form-control" placeholder="password" required style="width: 200px;"> | |
<br/> | |
<br/> | |
<input type="checkbox" id="Accept" name="agree" value="agree" onClick="EnableSubmit(this)">I agree that the website I am adding is for buisness use ONLY and as such | |
<br/>does not contain any content that under UK Law would be considered illegal | |
<br/>or inappropriate. | |
<br/> | |
<br/> | |
<div> | |
<button id="btnAddURL" class="btn" type="button" disabled>Add URL</button> <span id="responseMsg"></span> | |
</div> | |
</form> | |
</div> | |
<hr> | |
<div id="footer"> | |
<script type="text/javascript"> | |
document.write('<p>Generated <span id="date-time">', new Date().toLocaleString(), '<\/span> by the SecureCloud+ Internet Gateway<\/p>') | |
</script> | |
</div> | |
</body> | |
</html> | |
<div class="modal"></div> | |
************** | |
** main.css ** | |
************** | |
<!-- * { | |
font-family:verdana, sans-serif; | |
} | |
html body { | |
background:#efefef; | |
font-size:12px; | |
color:#1e1e1e; | |
overflow:hidden; | |
margin:0; | |
padding:0; | |
} | |
#titles { | |
margin-left:15px; | |
background:url('{path to your desired logo}') no-repeat left; | |
padding:10px 10px 10px 120px; | |
} | |
#content { | |
background:#fff; | |
padding:10px; | |
} | |
pre { | |
font-family:sans-serif; | |
} | |
hr { | |
margin:0; | |
} | |
#footer { | |
font-size:9px; | |
padding-left:10px; | |
} | |
body:lang(fa) { | |
direction:rtl; | |
font-size:100%; | |
font-family:Tahoma, Roya, sans-serif; | |
float:right; | |
} | |
:lang(he) { | |
direction:rtl; | |
} | |
.modal { | |
display:none; | |
position:fixed; | |
z-index:1000; | |
top:0; | |
left:0; | |
height:100%; | |
width:100%; | |
background:rgba(255,255,255,.8) url(loader.gif) 50% 50% no-repeat; | |
} | |
body.loading { | |
overflow:hidden; | |
} | |
body.loading .modal { | |
display:block; | |
} | |
#titles h1,#titles h2 { | |
color:#000; | |
} | |
#*********************** | |
#** ERR_ACCESS_DENIED ** | |
#*********************** | |
#*** Note: This is the squid template file usually found at C:\Squid\usr\share\squid\errors\{your language}\ | |
#*** Note: At some convenient point in the HTML code add the following line | |
#<p>Click <a href="https://{server hostname}" onclick="javascript:void window.open('https://iproxy1.scpgateway.uk','1449481426672','width=520,height=400,toolbar=0,menubar=0,location=0,status=0,scrollbars=0,resizable=0,left=0,top=0');return false;"> here </a>to allow browsing to this site.</p> | |
""" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment