from django.shortcuts import render, get_object_or_404, redirect from django.http import HttpResponse from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm, AdminPasswordChangeForm from django.db.models import Q from django.views.decorators.csrf import csrf_exempt from django.utils import timezone from django.core.files import File from django.db.models.fields.files import FieldFile from django.contrib.auth.models import User from django.contrib.auth.decorators import login_required, user_passes_test from .models import Device, Organization, Network, Model, Wifi from distutils.dir_util import copy_tree import glob import sys import subprocess import os import socket import tempfile import crypt import tarfile import datetime import time def is_superuser(user): return user.is_superuser def is_staff(user): return user.is_staff @login_required def index(request): return redirect("/devices") @csrf_exempt def heartbeat(request): device = get_object_or_404(Device, secret=request.POST.get("secret", "")) ip = request.POST.get("ip", "") if device.update: device.reboot = False code = """ . /etc/vpnsecret if /usr/bin/wget -O/tmp/update.bin https://admin360.kumi.host/update --post-data "secret=$SECRET" --no-check-certificate 2>/var/log/wget; then /sbin/sysupgrade -F -n /tmp/update.bin fi """ elif device.lastbeat < device.changed: code = """ . /etc/vpnsecret if /usr/bin/wget -O/tmp/wireless.in https://admin360.kumi.host/wireless --post-data "secret=$SECRET" --no-check-certificate 2>/var/log/wget; then while read p do echo $p >/tmp/wireless.line if /bin/grep wifi-iface /tmp/wireless.line && ! /bin/grep radio /tmp/wireless.line; then break fi echo $p >>/tmp/wireless.og done >/tmp/wireless.og /bin/cp /tmp/wireless.og /etc/config/wireless /sbin/uci commit /etc/config/wireless /bin/rm /tmp/wireless.* fi """ device.lastbeat = timezone.now() elif device.reboot: code = "/sbin/reboot" else: code = "" device.lastbeat = timezone.now() if ip: device.lasttime = timezone.now() device.curip = ip device.save() return HttpResponse(code) def makewificonfig(device): output = "" i = 1 for wifi in device.wifi.all(): output += """config wifi-iface option network 'wwan%i' option ssid '%s' option encryption 'psk2' option device 'radio1' option mode 'sta' option key '%s' """ % (i, wifi.ssid, wifi.key) i += 1 return output @csrf_exempt def wireless(request): device = get_object_or_404(Device, secret=request.POST.get("secret", "")) return HttpResponse(makewificonfig(device)) @csrf_exempt def hosts(request): device = get_object_or_404(Device, secret=request.POST.get("secret", "")) device.reboot = False device.save() return render(request, "manager/hosts", {"device": device}) def mkfirmware(device, path): if device.firmware and device.firmware > device.model.firmware and device.firmware > device.changed and glob.glob("%s/%s.bin" % (path, device.id)): return True BEFORE = os.getcwd() DEVICEDIR = "/opt/vpnmanager/device-config/%i/" % device.model.id SRCDIR = "/opt/vpnmanager/imagebuilder/%i/" % device.model.id if glob.glob(SRCDIR + "/.kumilock"): return False with open(SRCDIR + "/.kumilock", "w") as lock: lock.write("") tempdir = tempfile.TemporaryDirectory() copy_tree(DEVICEDIR, tempdir.name) # Write OpenVPN config with open(tempdir.name + "/etc/openvpn/client.conf", "w") as vpnconf: vpnconf.write(device.vpnconfig) # Write secret with open(tempdir.name + "/etc/vpnsecret", "w") as secret: secret.write('SECRET="%s"' % device.secret) # Write password with open(tempdir.name + "/etc/shadow", "r") as shadow: password = crypt.crypt(device.password, crypt.mksalt(crypt.METHOD_MD5)) shadowin = shadow.read() with open(tempdir.name + "/etc/shadow", "w") as shadowout: shadowout.write(shadowin.replace("$PASSWORD", password)) # Write Wireless config with open(tempdir.name + "/etc/config/wireless", "r") as wireless: wirein = wireless.read() with open(tempdir.name + "/etc/config/wireless", "w") as wireout: wire = wirein.replace("$SSID", device.serial) wireout.write(wire + "\n" + makewificonfig(device)) # Create compilation environment os.system("rm -rf " + SRCDIR + "/files/") os.mkdir(SRCDIR + "/files/") os.system("cp -r " + tempdir.name + "/* " + SRCDIR + "/files/") tempdir.cleanup() os.system("rm " + SRCDIR + "/bin/targets/ar71xx/generic/*") # Build image os.chdir(SRCDIR) try: subprocess.call(["/usr/bin/make"]) except: os.remove(SRCDIR + "/.kumilock") os.chdir(BEFORE) return False os.chdir(BEFORE) os.rename(glob.glob(SRCDIR + "/bin/targets/ar71xx/generic/*squashfs-sysupgrade.bin")[0], "%s/%s.bin" % (path, device.id)) os.remove(SRCDIR + "/.kumilock") os.system("rm -rf " + SRCDIR + "/files/") os.system("rm " + SRCDIR + "/bin/targets/ar71xx/generic/*") device.firmware = datetime.datetime.now() device.save() return True @csrf_exempt def update(request): FWDIR = "/opt/vpnmanager/images/" device = get_object_or_404(Device, secret=request.POST.get("secret", "")) if not mkfirmware(device, FWDIR): return HttpResponse(status=503) device.update = False device.save() with open("%s/%s.bin" % (FWDIR, device.id), "rb") as download: response = HttpResponse(download.read(), content_type="application/octet-stream") response['Content-Disposition'] = 'inline; filename=%s.bin' % device.serial return response def ping(request, device_id): if request.user.is_authenticated: device = None ajax = '{\n "status": ' for organization in Organization.objects.filter(users=request.user): device = device or Device.objects.filter(id=device_id, organization=organization) if not device: ajax += "-1" else: try: socket.inet_aton(device[0].curip) ajax += str(1 if not os.WEXITSTATUS(os.system("ping -c1 -w1 " + device[0].curip + " > /dev/null 2>&1")) else 2 if (timezone.now() - device[0].lasttime).total_seconds() > 120 and (timezone.now() - device[0].lastbeat).total_seconds() < 60 else 0) ajax += ',\n "serial": "%s"' % device[0].serial ajax += ',\n "name": "%s"' % device[0].name if device[0].name else "" ajax += ',\n "ip": "%s"' % device[0].curip ajax += ',\n "time": "%i"' % (int(time.mktime(timezone.make_naive(device[0].lasttime, timezone.get_current_timezone()).timetuple())) * 1000) ajax += ',\n "lastbeat": "%s"' % (int(time.mktime(timezone.make_naive(device[0].lastbeat, timezone.get_current_timezone()).timetuple())) * 1000) ajax += ',\n "reboot": %i' % (1 if device[0].reboot else 0) ajax += ',\n "update": %i' % (1 if device[0].update else 0) ajax += ',\n "network": {' ajax += '\n "intip": "%s"' % device[0].network.intip ajax += ',\n "extip": "%s"' % device[0].network.extip ajax += ',\n "name": "%s"' % (device[0].network.name if device[0].network.name else "") ajax += '\n }' except Exception as e: ajax += "-3" else: ajax += "-2" ajax += "\n}" return HttpResponse(ajax, content_type="application/json") @login_required def devices(request): user = request.user devices = set() wifis = set() users = set() orga = ", ".join([x.__str__() for x in Organization.objects.filter(users=user)]) for organization in Organization.objects.filter(users=user): for device in Device.objects.filter(organization=organization): devices.add(device) for wifi in Wifi.objects.filter(organization=organization): wifis.add(wifi) if user.is_staff: for orgauser in User.objects.filter(organization=organization): orgauser.organizations = set() for orga in Organization.objects.filter(users=orgauser): orgauser.organizations.add(orga) users.add(orgauser) return render(request, "manager/index.html", { "title": "Device Administration", "user": user, "organization": orga, "devices": sorted(devices, key=lambda x: x.serial), "wifis": sorted(wifis, key=lambda x: x.serial), "users": sorted(users, key=lambda x: x.username) } ) @login_required def editdevice(request, device_id): device = None subnets = set() wifis = set() for organization in Organization.objects.filter(users=request.user): device = device or Device.objects.filter(id=device_id, organization=organization) if not device: return redirect("/") for subnet in Network.objects.filter(organizations=device[0].organization): subnets.add(subnet) for wifi in Wifi.objects.filter(organization=device[0].organization): wifis.add(wifi) if request.POST.get("subnet", ""): subnet = Network.objects.filter(intip=request.POST.get("subnet", device[0].network.intip if device[0].network else "No VPN")) if subnet[0] in subnets: device[0].name = request.POST.get("name", "") device[0].network = subnet[0] device[0].reboot = True if request.POST.get("reboot", "0") == "True" else False device[0].update = True if request.POST.get("update", "0") == "True" else False device[0].wifi.set(request.POST.getlist("wifi", [])) device[0].changed = timezone.now() device[0].save() return redirect("/") return render(request, "manager/edit.html", { "title": "Edit Device", "device": device[0], "subnets": subnets, "user": request.user, "wifis": wifis, "curfis": Wifi.objects.filter(device=device[0]) } ) @login_required def edituser(request, user_id): if request.user.is_staff or request.user.id == user_id: user = None orgas = Organization.objects.filter(users=request.user) for organization in orgas: user = user or User.objects.filter(id=user_id, organization=organization) if not user: return redirect("/") if request.POST.get("form", ""): user[0].first_name = request.POST.get("firstname", "") user[0].last_name = request.POST.get("lastname", "") if request.user.is_staff or request.user.is_superuser: user[0].is_staff = True if request.POST.get("staff", "0") == "True" else False if request.user.is_superuser: user[0].is_superuser = True if request.POST.get("superuser", "0") == "True" else False user[0].email = request.POST.get("email", "") user[0].save() return redirect("/") return render(request, "manager/edituser.html", { "title": "Edit User", "auser": user[0] } ) else: return redirect('/account/login/?next=%s' % request.path) @login_required def editwifi(request, wifi_id): wifi = None for organization in Organization.objects.filter(users=request.user): wifi = wifi or Wifi.objects.filter(id=wifi_id, organization=organization) if not wifi: return redirect("/") if request.POST.get("serial", ""): wifi[0].serial = request.POST.get("serial", "") wifi[0].ssid = request.POST.get("ssid", "") wifi[0].key = request.POST.get("key", "") wifi[0].save() return redirect("/") return render(request, "manager/editwifi.html", { "title": "Edit WiFi", "wifi": wifi[0] } ) @user_passes_test(is_superuser) def getconfig(request, device_id): FWDIR = "/opt/vpnmanager/images/" device = get_object_or_404(Device, id=device_id) if not mkfirmware(device, FWDIR): return HttpResponse("Something went wrong generating the firmware image. The server may be busy, please try again later.") device.update = False device.save() with open("%s/%s.bin" % (FWDIR, device.id), "rb") as download: response = HttpResponse(download.read(), content_type="application/octet-stream") response['Content-Disposition'] = 'inline; filename=%s.bin' % device.serial return response @login_required def rebootdevice(request, device_id): device = None for organization in Organization.objects.filter(users=request.user): device = device or Device.objects.filter(id=device_id, organization=organization) if device: device[0].reboot = True device[0].save() return redirect("/") @user_passes_test(is_staff) def updatedevice(request, device_id): device = None for organization in Organization.objects.filter(users=request.user): device = device or Device.objects.filter(id=device_id, organization=organization) if device: device[0].update = True device[0].save() return redirect("/") @user_passes_test(is_superuser) def deletedevice(request, device_id): CADIR = "/etc/openvpn/ca/" BEFORE = os.getcwd() device = get_object_or_404(Device, id=device_id) os.chdir(CADIR) subprocess.call(CADIR + "/revoke " + device.serial, shell=True) os.system("rm " + CADIR + "/keys/" + device.serial + ".{crt,csr,key}") os.chdir(BEFORE) device.delete() return redirect("/") @user_passes_test(is_staff) def deletewifi(request, wifi_id): wifi = get_object_or_404(Wifi, id=wifi_id) for organization in Organization.objects.filter(users=request.user): if organization == wifi.organization: wifi.delete() break return redirect("/") @user_passes_test(is_staff) def makewifi(request): wifi_serial = request.POST.get("serial", "") wifi_ssid = request.POST.get("ssid", "") wifi_key = request.POST.get("key", "") wifi_organization = request.POST.get("organization", "") if not wifi_serial: orga = Organization.objects.filter(users=request.user) return render(request, "manager/addwifi.html", { "title": "Add WiFi", "organizations": orga } ) wifi = Wifi.objects.create( serial = wifi_serial, ssid = wifi_ssid, key = wifi_key, organization = Organization.objects.filter(id=wifi_organization)[0] ) return redirect("/") @user_passes_test(is_superuser) def makedevice(request): CADIR = "/etc/openvpn/ca/" CONFIGDIR = "/etc/openvpn/client-configs/" BEFORE = os.getcwd() device_serial = request.POST.get("serial", "") device_name = request.POST.get("name", "") device_organization = request.POST.get("organization", "") device_model = request.POST.get("model", "") if not request.user.is_superuser: return redirect("/") if not device_serial: orga = Organization.objects.all() models = Model.objects.all() return render(request, "manager/add.html", { "title": "Add Device", "organizations": orga, "models": models } ) if glob.glob(CADIR + "/keys/" + device_serial + "*"): return HttpResponse("This key already exists.") os.chdir(CADIR) if subprocess.call(CADIR + "/generate-key " + device_serial, shell=True): os.chdir(BEFORE) return HttpResponse("Something went wrong trying to generate the key.") if glob.glob(CONFIGDIR + "/files/" + device_serial + "*"): os.chdir(BEFORE) return HttpResponse("This configuration file already exists.") os.chdir(CONFIGDIR) if subprocess.call(CONFIGDIR + "/make_config " + device_serial, shell=True): os.chdir(BEFORE) return HttpResponse("Something went wrong trying to generate the config file.") os.chdir(BEFORE) device = Device.objects.create( serial=device_serial, name=device_name, model=Model.objects.filter(id=device_model)[0], network=Network.objects.filter(intip="No VPN")[0], organization=Organization.objects.filter(id=device_organization)[0], vpnconfig = open(CONFIGDIR + "/files/" + device_serial + ".ovpn").read() ) return redirect("/")