예제 #1
0
def server_config():
    """
    Returns the OpenVPN server configuration generated from the application
    settings.
    :return: str
    """
    conf = render_to_string(
        "openvpn/server.conf", {
            "root_dir": settings.BASE_DIR,
            "bind_address": net.interface_ip(config.get("vpn_interface")),
            "bind_port": config.get("vpn_port"),
            "ca_crt": config.get("ca_crt"),
            "crl_file": settings.PKI_CRL_FILE,
            "dh_params": config.get("vpn_dh_params"),
            "domain": config.get("domain"),
            "log_file": settings.OPENVPN_LOG_FILE,
            "managment_socket": settings.OPENVPN_MANAGEMENT_SOCKET,
            "nameservers": config.get_list("vpn_nameservers"),
            "protocol": config.get("vpn_protocol"),
            "redirect_gateway": config.get_bool("vpn_redirect_gateway", False),
            "routes": config.get_list("vpn_routes"),
            "server_crt": config.get("vpn_crt"),
            "server_key": config.get("vpn_key"),
            "status_file": settings.OPENVPN_STATUS_FILE,
            "subnet": config.get("vpn_subnet"),
            "tls_auth_key": config.get("vpn_tls_auth_key"),
        })
    return strings.remove_empty_lines(conf)
예제 #2
0
def send(recipient, subject, body):
    """
    Sends an e-mail with the given details by calling an application task.
    :return: bool
    """
    sender = config.get("smtp_reply_address", config.get("smtp_username"))
    tasks.send_email(recipient, subject, body, sender)
    return True
예제 #3
0
def is_configured():
    """
    Returns whether the application settings are configured to send e-mails.
    :return: bool
    """
    return not (not config.get("smtp_host") or not config.get("smtp_port")
                or not config.get("smtp_username")
                or not config.get("smtp_password"))
예제 #4
0
def certificate_authority():
    """
    Return's the application certificate authority keypair.
    :return: KeyPair
    """
    return load_keypair(
        crt=config.get("ca_crt"),
        key=config.get("ca_key"),
    )
예제 #5
0
def base_context_processor(request):
    """
    The base UI context processor.
    :return: dict
    """
    return {
        "error": request.session.pop("error", None),
        "form": request.session.pop("form", {}),
        "oauth2_provider": config.get("oauth2_provider", None),
        "organization": config.get("app_organization", "Mangle"),
    }
예제 #6
0
def _render_rules():
    """
    Renders and returns the web application firewall rules template.
    :return: str
    """
    return render_to_string(
        template_name="firewall/web.rules",
        context={
            "http_port": config.get("app_http_port", 80),
            "https_port": config.get("app_https_port", 443),
        },
    )
예제 #7
0
    def create(self, request, *args, **kwargs):
        """
        Creates a user account for all of the submitted e-mail addresses.
        :return: Response
        """
        serializer = serializers.UserInviteSerializer(data=request.data)

        users = serializer.save()

        # contains the e-mail and password that is returned to the UI
        emails_passwords = []

        # send e-mail notifications if desired
        for user in users:
            emails_passwords.append({
                "email": user.email,
                "password": user.temp_password,
            })

            if request.data.get("notify", False):
                organization = config.get("app_organization")

                mail.send_template(
                    recipient=user.email,
                    subject="Welcome to the {} VPN!".format(organization),
                    template="mail/Welcome.html",
                    data={
                        "email": user.email,
                        "organization": organization,
                        "password": user.temp_password,
                        "url": config.url(),
                    })

        return Response(emails_passwords, status=status.HTTP_201_CREATED)
예제 #8
0
def render_rules():
    """
    Returns the base OpenVPN rules.
    :return: str
    """
    return render_to_string(
        template_name="firewall/vpn.rules",
        context={
            "interface": config.get("vpn_interface"),
            "nat_interface": config.get("vpn_nat_interface"),
            "local_addrs": net.ip_addresses(),
            "port": config.get("vpn_port"),
            "protocol": config.get("vpn_protocol"),
            "nameservers": config.get_list("vpn_nameservers"),
            "subnet": config.get("vpn_subnet"),
        },
    )
예제 #9
0
 def get_disposition(self, device):
     """
     Returns the 'Content-Disposition' header value for the given device
     configuration file download.
     :return: str
     """
     org = config.get("app_organization")
     return 'attachment; filename="{} - {}.ovpn"'.format(org, device.name)
예제 #10
0
def init():
    """
    Sets the Django SMTP settings from the application settings and returns
    whether the application is ready to send e-mails.
    :return: None
    """
    config.reload()

    if not is_configured():
        return False

    settings.EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
    settings.EMAIL_USE_TLS = config.get("smtp_tls", True)
    settings.EMAIL_HOST = config.get("smtp_host")
    settings.EMAIL_PORT = config.get("smtp_port")
    settings.EMAIL_HOST_USER = config.get("smtp_username")
    settings.EMAIL_HOST_PASSWORD = config.get("smtp_password")
    return True
예제 #11
0
def client_config(crt, key, os="windows"):
    """
    Returns an OpenVPN client configuration for the given client certificate
    and private key. If `is_linux` is True then the proper DNS scripts commands
    are added.
    :return: str
    """
    conf = render_to_string(
        "openvpn/client.conf", {
            "ca_crt": config.get("ca_crt"),
            "client_crt": crt,
            "client_key": key,
            "hostname": config.get("vpn_hostname"),
            "os": os,
            "port": config.get("vpn_port"),
            "protocol": config.get("vpn_protocol"),
            "tls_auth_key": config.get("vpn_tls_auth_key"),
        })
    return strings.remove_empty_lines(conf)
예제 #12
0
def create_web_vhost():
    """
    Creates the web application Nginx virtual host file.
    :return: None
    """
    content = render_to_string(
        "install/nginx/mangle-web.conf", {
            "hostname": config.get("app_hostname"),
            "http_port": config.get("app_http_port", 80),
            "https_port": config.get("app_https_port", 443),
            "root_dir": settings.BASE_DIR,
            "ssl_crt": settings.WEB_SSL_CRT_FILE,
            "ssl_key": settings.WEB_SSL_KEY_FILE,
            "ssl_dh": settings.WEB_SSL_DH_FILE,
            "wsgi_socket": settings.WEB_WSGI_SOCKET,
        })

    fs.write_file(settings.WEB_VHOST_FILE, content, 0o755)
    bash.run("systemctl", "restart", "nginx")
예제 #13
0
def web_post_stop():
    """
    Deletes the web application firewall rules.
    :return: None
    """
    rules = config.get("web_firewall_rules", "")

    for rule in rules.split("\n"):
        iptables.run(rule.replace("-A", "-D"))

    config.delete("web_firewall_rules")
예제 #14
0
def redirect_login(request):
    """
    Returns the login URL based on the current authentication backend.
    :return: Any
    """
    backend = config.get("auth_backend", "oauth2")

    if backend == "oauth2":
        url, state = oauth2.get_provider().get_login_url()
        request.session["oauth_state"] = state
        return redirect(url)
예제 #15
0
def get_provider():
    """
    Returns the OAuth2 provider from the application settings.
    :return: OAuth2Provider
    """
    provider = config.get("oauth2_provider", "google")

    if provider == "google":
        return GoogleOAuth2Provider()

    raise ValueError("unknown oauth2 provider: " + provider)
예제 #16
0
    def mfa_url(self):
        """
        Returns the user's two-factor authenticaiton provisioning URL.
        :return: str
        """
        organization = config.get("app_organization", "Mangle")

        return pyotp.TOTP(self.mfa_secret).provisioning_uri(
            name=self.email,
            issuer_name="{} VPN".format(organization),
        )
예제 #17
0
    def save(self, **kwargs):
        """
        Updates the application settings and SSL certificate and private key
        if provided.
        :return: None
        """
        old_http_port = config.get("app_http_port")
        old_https_port = config.get("app_https_port")

        # the SSL certificate and private key should not be stored in the
        # database as settings so remove them from the validated_data dict
        # prior to saving
        ssl_crt = self.initial_data.pop("app_ssl_crt", None)
        ssl_key = self.initial_data.pop("app_ssl_key", None)

        super().save(**kwargs)

        if ssl_crt and ssl_key:
            fs.write_file(settings.WEB_SSL_CRT_FILE, ssl_crt, 0o600)
            fs.write_file(settings.WEB_SSL_KEY_FILE, ssl_key, 0o600)
            bash.run("systemctl", "reload", "nginx")

        if (old_http_port != self.validated_data["app_http_port"] or
                old_https_port != self.validated_data["app_https_port"]):
            # update HTTP and HTTPs listen directives
            http = "sed -i 's/{} default_server/{} default_server/g' /etc/nginx/conf.d/mangle.conf"
            bash.run(http.format(old_http_port, self.validated_data["app_http_port"]))

            https = "sed -i 's/{} ssl/{} ssl/g' /etc/nginx/conf.d/mangle.conf"
            bash.run(https.format(old_https_port, self.validated_data["app_https_port"]))

            # update the port number in redirect directive
            bash.run("sed -i 's/$host:{}/$host:{}/g' /etc/nginx/conf.d/mangle.conf".format(
                old_https_port,
                self.validated_data["app_https_port"]
            ))

            # restart Nginx and web application
            bash.run("systemctl", "restart", "nginx")
            bash.run("systemctl", "restart", "mangle-web")
예제 #18
0
    def password(self, request, pk=None):
        """
        Resets the user's password and forces them to change it.
        :return: Response
        """
        user = self.get_object()
        password = user.reset_password()
        user.save()

        # send e-mail notifying user
        mail.send_template(recipient=user.email,
                           subject="{} VPN Password Reset".format(
                               config.get("app_organization")),
                           template="mail/Password.html",
                           data={
                               "email": user.email,
                               "organization": config.get("app_organization"),
                               "password": password,
                               "url": config.url(),
                           })

        return Response({"password": password})
예제 #19
0
 def get(self, request):
     """
     Returns general API information.
     :return: Response
     """
     return Response({
         "app_organization":
         config.get("app_organization", "Mangle"),
         "app_version":
         version(),
         "vpn_restart_pending":
         config.get_bool("vpn_restart_pending", False),
         "update_available":
         self.update_available(),
     })
예제 #20
0
    def settings(self):
        """
        Returns all of the serial
        :return:
        """
        settings = {}
        for name in self.fields:
            settings[name] = config.get(name, "")

        settings["interfaces"] = {}

        for iface in net.interface_names("lo", "tun"):
            settings["interfaces"][iface] = net.interface_ip(iface)

        return settings
예제 #21
0
def vpn_post_stop():
    """
    Deletes the OpenVPN firewall rules.
    :return: None
    """
    rules = config.get("vpn_firewall_rules", "")

    for rule in rules.split("\n"):
        iptables.run(rule.replace("-A", "-D"))

    # delete the application chains
    iptables.delete_chain("filter", "MangleVPN")
    iptables.delete_chain("filter", "MangleVPN_Clients")

    config.delete("vpn_firewall_rules")

    # delete all of the group chains
    for group in models.Group.objects.filter(is_enabled=True).all():
        group.delete_firewall_chain()

    # delete all clients to avoid potential stale clients persisting through
    # dirty restarts or reboots
    models.Client.objects.all().delete()
예제 #22
0
from datetime import datetime, timedelta

from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import dh, rsa
from mangle.common import config

KEY_SIZE = config.get("pki_key_size", 2048)
"""int: the size of generated keys.

Defines the number of bits that will be used when generating RSA private keys 
and Diffie-Hellman parameters. Read from the application settings but defaults
to 2048 bits.
"""


def certificate_authority():
    """
    Return's the application certificate authority keypair.
    :return: KeyPair
    """
    return load_keypair(
        crt=config.get("ca_crt"),
        key=config.get("ca_key"),
    )


def create_certificate_authority():
    """
    Creates and sets the application certificate authority keys.