Commit 0ba1819d authored by ale's avatar ale

first draft of the autovpn app

parent 392b55d6
body {
background: white;
color: #333;
font-size: 1.1em;
}
#container {
width: 50%;
padding: 50px;
margin: 10% auto;
border: 2px solid #333;
-moz-border-radius: 20px;
-webkit-border-radius: 20px;
}
#title {
height: 65px;
padding-left: 75px;
padding-top: 30px;
background: url(/static/lock_small.png) no-repeat top left;
font-size: 2.0em;
font-family: Garamond, Georgia, serif;
font-weight: bold;
line-height: 0.7em;
}
#subtitle {
color: #999;
font-size: 0.5em;
font-weight: normal;
font-family: Georgia, serif;
padding-left: 8.3em;
}
#subtitle a {
color: black;
text-decoration: none;
}
#content {
}
a:hover {
text-decoration: underline;
}
<!doctype html>
<html>
<head>
<title>A/I VPN</title>
<link href="/static/favicon.ico" type="image/x-icon" rel="shortcut icon">
<link rel="stylesheet" type="text/css" href="/static/style.css">
{% block head %}{% endblock -%}
</head>
<body>
<div id="container">
<div id="title">
Self-Service VPN
<div id="subtitle">built by
<a href="http://www.autistici.org">autistici.org</a></div>
</div>
<div id="content">
{% block content %}{% endblock %}
</div>
</div>
</body>
</html>
{% extends "_base.html" %}
{% set dl_url = url_for('vpn_admin.new_cert_dl', _csrf=csrf_token()) %}
{% block head %}
<meta http-equiv="refresh" content="2; url={{ dl_url }}">
{% endblock %}
{% block content %}
<p>
The download of the ZIP file should start in a few seconds.
If it doesn't work, try with
<a href="{{ dl_url }}">this link</a> (note: it will only work
once).
</p>
<p>
If you have any trouble downloading the ZIP file, try clearing
the cookies in your browser and
<a href="{{ url_for('vpn_admin.index') }}">starting over</a>.
</p>
<p>
<b>This ZIP file is VERY IMPORTANT, do not
share it with anyone, and save it only on trusted media.</b>
</p>
{% endblock %}
{% extends "_base.html" %}
{% block content %}
<p>
Sorry, you are trying to re-use an expired session. If you are
trying to download a certificate, it probably means that the
file is already on your computer, check in your Downloads
folder.
</p>
<p>
Otherwise, you should
<a href="{{ url_for('vpn_admin.index') }}">start the process
from scratch</a> (it's easy anyway!)
</p>
{% endblock %}
{% extends "_base.html" %}
{% block content %}
<p>
A reasonably anonymous (but low-traffic) VPN service, for when
you really need to get on the Internet from a nasty place!
</p>
<p>
You only need a SSL certificate to connect, click the link
below to download a ZIP file containing the certificate and
a private key.
</p>
<form action="{{ url_for('vpn_admin.new_cert') }}" method="post">
<div style="display:none;">
<input type="hidden" name="_csrf" value="{{ csrf_token() }}">
</div>
<p style="text-align:center;">
<input type="submit" value=" Generate new SSL certificate ">
</p>
</form>
{% endblock %}
import datetime
import functools
import logging
import os
import uuid
import zipfile
from cStringIO import StringIO
from OpenSSL import crypto
from autoca import ca
from autoca import ca_app
from autoca import ca_stub
from flask import Blueprint, Flask, abort, redirect, request, make_response, \
render_template, session, g, current_app
vpn_admin = Blueprint('vpn_admin', __name__)
log = logging.getLogger(__name__)
OPENVPN_CONFIG_TEMPLATE = '''
client
dev tun
resolv-retry infinite
nobind
persist-key
persist-tun
comp-lzo
<connection>
remote %(vpn_endpoint)s 1194 udp
</connection>
<connection>
remove %(vpn_endpoint)s 443 tcp
</connection>
; SSL configuration.
ca ca.crt
cert %(cn)s.crt
key %(cn)s.key
ns-cert-type server
'''
README_TEMPLATE = '''
VPN client configuration
========================
The ZIP file you just got contains an SSL certificate (and private
key) used to authenticate to the VPN network. This file is very
sensitive, keep it in a safe place.
The certificate will expire on %(expiry_date)s. Go back to where you got the
ZIP file in the first place to obtain another one.
The specific instructions to install the VPN connection on your device
may vary depending on your OS, some examples follow. Refer to the
official OpenVPN documentation for further information.
Linux (without NetworkManager)
------------------------------
- Install OpenVPN, i.e. on a Debian-based system::
$ sudo apt-get install openvpn
- Start the VPN connection by pointing openvpn at your config (the file
named openvpn-ai.conf in the same directory as this README)::
$ sudo openvpn --config openvpn-%(cn)s.conf
Linux (with NetworkManager)
---------------------------
If you are using NetworkManager to configure your network connections,
you are probably better off having it deal with OpenVPN itself. A
NetworkManager plugin exists for this purpose::
$ sudo apt-get install network-manager-openvpn-gnome
You can then configure the VPN using NetworkManager itself, use
"Certificates (TLS)" as the Authentication Type, and use the
certificates contained in this ZIP file.
References
----------
OpenVPN documentation:
http://openvpn.net/index.php/open-source/documentation/
Further info:
%(vpn_site)s
'''
def csrf(fn):
@functools.wraps(fn)
def _csrf_wrapper(*args, **kwargs):
query_args = (request.method == 'POST') and request.form or request.args
token = session.pop('_csrf', None)
if not token or token != query_args.get('_csrf'):
abort(400)
return fn(*args, **kwargs)
return _csrf_wrapper
def generate_csrf_token():
if '_csrf' not in session:
session['_csrf'] = os.urandom(18).encode('base64').rstrip()
return session['_csrf']
@vpn_admin.before_request
def set_ca_wrapper():
g.ca = current_app.ca
@vpn_admin.before_request
def check_userlogin():
g.username = None
if 'username' in session:
g.username = session['username']
@vpn_admin.route('/')
def index():
return render_template('index.html')
@vpn_admin.route('/newcert', methods=['POST'])
@csrf
def new_cert():
session['dl_ok'] = 'y'
return render_template('download.html')
@vpn_admin.route('/newcertdl')
@csrf
def new_cert_dl():
if session.pop('dl_ok', None) != 'y':
return render_template('download_retry.html')
# Create and sign a certificate.
cn = str(uuid.uuid4())
validity = int(current_app.config.get('VPN_CERT_VALIDITY', 7))
expiry_date = datetime.date.today() + datetime.timedelta(validity)
subject = current_app.config.get('VPN_DEFAULT_SUBJECT_ATTRS', {}).copy()
subject['CN'] = cn
pkey, cert = g.ca.make_certificate(subject, days=validity)
# Create the zipfile in-memory, with all the files the user needs.
vars = {'cn': cn,
'vpn_endpoint': current_app.config['VPN_ENDPOINT'],
'vpn_site': current_app.config['VPN_SITE_URL'],
'expiry_date': expiry_date.strftime('%Y/%m/%d')}
manifest = [
('ca.crt', g.ca.get_ca()),
('%s.crt' % cn,
crypto.dump_certificate(crypto.FILETYPE_PEM, cert)),
('%s.key' % cn,
crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey)),
('openvpn-%s.conf' % cn, OPENVPN_CONFIG_TEMPLATE % vars),
('README.txt', README_TEMPLATE % vars),
]
zbuf = StringIO()
zf = zipfile.ZipFile(zbuf, mode='w',
compression=zipfile.ZIP_DEFLATED)
for filename, contents in manifest:
zf.writestr(filename, contents)
zf.close()
response = make_response(zbuf.getvalue())
response.headers['Content-Type'] = 'application/zip'
response.headers['Content-Disposition'] = (
'attachment; filename="%s.zip"' % cn)
response.headers['Cache-control'] = 'private'
return response
def make_app(config):
app = Flask(__name__)
app.config.update(config)
app.config.from_envvar('VPN_APP_SETTINGS', silent=True)
app.register_blueprint(vpn_admin)
# Figure out how to hook to the CA.
if app.config.get('VPN_CA_URL'):
app.ca = ca_stub.CaStub(app.config['VPN_CA_URL'],
app.config.get('CA_SHARED_SECRET'))
else:
app.ca = ca.CA(app.config['VPN_CA_ROOT'],
app.config['VPN_CA_SUBJECT'],
int(app.config.get('VPN_CA_BITS', 4096)))
app.register_blueprint(ca_app.ca_app, url_prefix='/ca')
app.jinja_env.globals['csrf_token'] = generate_csrf_token
return app
if __name__ == '__main__':
# Run a test instance with a standalone HTTP server.
import tempfile
import shutil
ca_dir = tempfile.mkdtemp()
print 'CA dir:', ca_dir
try:
make_app({'DEBUG': 'true',
'SECRET_KEY': 'somesecret',
'VPN_CA_ROOT': ca_dir,
'VPN_CA_SUBJECT': {'CN': 'test CA', 'O': 'test'},
'VPN_ENDPOINT': 'vpn.example.com',
'VPN_SITE_URL': 'http://localhost:4000/',
}).run(port=4000)
finally:
shutil.rmtree(ca_dir)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment