├── .gitignore ├── README.md ├── install-dependencies.sh ├── legacy-install-dependencies.sh ├── lib ├── config │ ├── nginx │ │ └── default │ └── ssh │ │ └── sshd_config ├── omada │ └── py │ │ ├── create-super-admin.py │ │ ├── delete-device.py │ │ ├── delete-super-admin.py │ │ ├── find-device.py │ │ └── omada.py ├── rclone │ └── install.sh ├── uisp │ └── py │ │ ├── archived │ │ └── create-super-admin.py │ │ └── create-super-admin.py ├── unifi-video │ └── py │ │ ├── create-super-admin.py │ │ └── delete-super-admin.py ├── unifi │ ├── java │ │ ├── PruneBackup.class │ │ └── PruneBackup.java │ ├── mongodb │ │ ├── libssl1.0.0_1.0.1t-1+deb8u12_amd64.deb │ │ ├── prune.js │ │ └── upgrade.sh │ ├── py │ │ ├── check-owner.py │ │ ├── create-super-admin.py │ │ ├── delete-device.py │ │ ├── delete-old-hostifi-admins.py │ │ ├── delete-super-admin.py │ │ ├── find-device.py │ │ ├── manual-backup.py │ │ ├── prune-db.py │ │ ├── redirect.py │ │ ├── restore-backup.py │ │ ├── ugw-site-count.py │ │ └── unifi.py │ └── ssl │ │ ├── import-ssl.sh │ │ └── install-ssl.sh └── zabbix │ ├── maintenance-mode.py │ └── zabbix.py ├── omada ├── count-devices.sh ├── create-super-admin.sh ├── delete-device.sh ├── delete-super-admin.sh ├── find-device.sh └── list-admins.sh ├── uisp ├── archived │ └── create-super-admin.sh ├── count-devices.sh ├── create-super-admin.sh ├── delete-super-admin.sh └── maintenance-mode.sh ├── unifi-video ├── count-devices.sh ├── create-super-admin.sh ├── delete-super-admin.sh └── maintenance-mode.sh └── unifi ├── add-ssh-key-to-unifi-user.sh ├── archived └── prune-backup.sh ├── check-owner.sh ├── count-devices.sh ├── create-mongodb-prune-cronjob.sh ├── create-ro-super-admin.sh ├── create-super-admin.sh ├── delete-device.sh ├── delete-old-hostifi-admins.sh ├── delete-super-admin.sh ├── find-device.sh ├── install-ssl.sh ├── list-admins.sh ├── maintenance-mode.sh ├── manual-backup.sh ├── prune-backup.sh ├── prune-mongodb.sh ├── redirect.sh ├── reinstall-unifi.sh └── remove-owner.sh /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store 2 | /lib/zabbix/config.py 3 | __pycache__/ 4 | *.pyc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HostiFi/support-tools/4d96d1cb374dc469dd4b22a621d6980810d535be/README.md -------------------------------------------------------------------------------- /install-dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | apt-get update -y 3 | apt-get install python3 -y 4 | apt-get install python3-pip -y 5 | apt-get install python3-pymongo -y 6 | apt-get install build-essential libssl-dev libffi-dev python3-dev -y 7 | pip3 install bcrypt 8 | pip3 install pyzabbix 9 | pip3 uninstall pymongo bson 10 | -------------------------------------------------------------------------------- /legacy-install-dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | apt-get update -y 3 | apt-get install python3 -y 4 | apt-get install python3-pip -y 5 | apt-get install build-essential libssl-dev libffi-dev python3-dev python3-wheel -y 6 | apt-get install python3-pyasn1 -y 7 | apt-get install python3-setuptools -y 8 | pip3 install wheel 9 | pip3 install cffi 10 | pip3 install pymongo==3.5.1 11 | pip3 install bcrypt==3.1.7 12 | pip3 install pyzabbix==0.7.4 13 | -------------------------------------------------------------------------------- /lib/config/nginx/default: -------------------------------------------------------------------------------- 1 | ## 2 | # You should look at the following URL's in order to grasp a solid understanding 3 | # of Nginx configuration files in order to fully unleash the power of Nginx. 4 | # http://wiki.nginx.org/Pitfalls 5 | # http://wiki.nginx.org/QuickStart 6 | # http://wiki.nginx.org/Configuration 7 | # 8 | # Generally, you will want to move this file somewhere, and start with a clean 9 | # file but keep this around for reference. Or just disable in sites-enabled. 10 | # 11 | # Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples. 12 | ## 13 | 14 | # Default server configuration 15 | # 16 | server { 17 | listen 80 default_server; 18 | listen [::]:80 default_server; 19 | 20 | # SSL configuration 21 | # 22 | # listen 443 ssl default_server; 23 | # listen [::]:443 ssl default_server; 24 | # 25 | # Note: You should disable gzip for SSL traffic. 26 | # See: https://bugs.debian.org/773332 27 | # 28 | # Read up on ssl_ciphers to ensure a secure configuration. 29 | # See: https://bugs.debian.org/765782 30 | # 31 | # Self signed certs generated by the ssl-cert package 32 | # Don't use them in a production server! 33 | # 34 | # include snippets/snakeoil.conf; 35 | 36 | root /var/www/html; 37 | 38 | # Add index.php to the list if you are using PHP 39 | index index.html index.htm index.nginx-debian.html; 40 | 41 | server_name _; 42 | 43 | location / { 44 | # First attempt to serve request as file, then 45 | # as directory, then fall back to displaying a 404. 46 | try_files $uri $uri/ =404; 47 | # proxy_pass http://localhost:8080; 48 | # proxy_http_version 1.1; 49 | # proxy_set_header Upgrade $http_upgrade; 50 | # proxy_set_header Connection 'upgrade'; 51 | # proxy_set_header Host $host; 52 | # proxy_cache_bypass $http_upgrade; 53 | } 54 | 55 | # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 56 | # 57 | #location ~ \.php$ { 58 | # include snippets/fastcgi-php.conf; 59 | # 60 | # # With php7.0-cgi alone: 61 | # fastcgi_pass 127.0.0.1:9000; 62 | # # With php7.0-fpm: 63 | # fastcgi_pass unix:/run/php/php7.0-fpm.sock; 64 | #} 65 | 66 | # deny access to .htaccess files, if Apache's document root 67 | # concurs with nginx's one 68 | # 69 | #location ~ /\.ht { 70 | # deny all; 71 | #} 72 | } 73 | 74 | 75 | # Virtual Host configuration for example.com 76 | # 77 | # You can move that to a different file under sites-available/ and symlink that 78 | # to sites-enabled/ to enable it. 79 | # 80 | #server { 81 | # listen 80; 82 | # listen [::]:80; 83 | # 84 | # server_name example.com; 85 | # 86 | # root /var/www/example.com; 87 | # index index.html; 88 | # 89 | # location / { 90 | # try_files $uri $uri/ =404; 91 | # } 92 | # 93 | -------------------------------------------------------------------------------- /lib/config/ssh/sshd_config: -------------------------------------------------------------------------------- 1 | # $OpenBSD: sshd_config,v 1.100 2016/08/15 12:32:04 naddy Exp $ 2 | 3 | # This is the sshd server system-wide configuration file. See 4 | # sshd_config(5) for more information. 5 | 6 | # This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin 7 | 8 | # The strategy used for options in the default sshd_config shipped with 9 | # OpenSSH is to specify options with their default value where 10 | # possible, but leave them commented. Uncommented options override the 11 | # default value. 12 | 13 | #Port 22 14 | #AddressFamily any 15 | #ListenAddress 0.0.0.0 16 | #ListenAddress :: 17 | 18 | #HostKey /etc/ssh/ssh_host_rsa_key 19 | #HostKey /etc/ssh/ssh_host_ecdsa_key 20 | #HostKey /etc/ssh/ssh_host_ed25519_key 21 | 22 | # Ciphers and keying 23 | #RekeyLimit default none 24 | 25 | # Logging 26 | #SyslogFacility AUTH 27 | #LogLevel INFO 28 | 29 | # Authentication: 30 | 31 | #LoginGraceTime 2m 32 | PermitRootLogin yes 33 | #StrictModes yes 34 | #MaxAuthTries 6 35 | #MaxSessions 10 36 | 37 | PubkeyAuthentication yes 38 | 39 | # Expect .ssh/authorized_keys2 to be disregarded by default in future. 40 | #AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2 41 | 42 | #AuthorizedPrincipalsFile none 43 | 44 | #AuthorizedKeysCommand none 45 | #AuthorizedKeysCommandUser nobody 46 | 47 | # For this to work you will also need host keys in /etc/ssh/ssh_known_hosts 48 | #HostbasedAuthentication no 49 | # Change to yes if you don't trust ~/.ssh/known_hosts for 50 | # HostbasedAuthentication 51 | #IgnoreUserKnownHosts no 52 | # Don't read the user's ~/.rhosts and ~/.shosts files 53 | #IgnoreRhosts yes 54 | 55 | # To disable tunneled clear text passwords, change to no here! 56 | PasswordAuthentication no 57 | #PermitEmptyPasswords no 58 | 59 | # Change to yes to enable challenge-response passwords (beware issues with 60 | # some PAM modules and threads) 61 | ChallengeResponseAuthentication no 62 | 63 | # Kerberos options 64 | #KerberosAuthentication no 65 | #KerberosOrLocalPasswd yes 66 | #KerberosTicketCleanup yes 67 | #KerberosGetAFSToken no 68 | 69 | # GSSAPI options 70 | #GSSAPIAuthentication no 71 | #GSSAPICleanupCredentials yes 72 | #GSSAPIStrictAcceptorCheck yes 73 | #GSSAPIKeyExchange no 74 | 75 | # Set this to 'yes' to enable PAM authentication, account processing, 76 | # and session processing. If this is enabled, PAM authentication will 77 | # be allowed through the ChallengeResponseAuthentication and 78 | # PasswordAuthentication. Depending on your PAM configuration, 79 | # PAM authentication via ChallengeResponseAuthentication may bypass 80 | # the setting of "PermitRootLogin without-password". 81 | # If you just want the PAM account and session checks to run without 82 | # PAM authentication, then enable this but set PasswordAuthentication 83 | # and ChallengeResponseAuthentication to 'no'. 84 | UsePAM no 85 | 86 | #AllowAgentForwarding yes 87 | #AllowTcpForwarding yes 88 | #GatewayPorts no 89 | X11Forwarding yes 90 | #X11DisplayOffset 10 91 | #X11UseLocalhost yes 92 | #PermitTTY yes 93 | PrintMotd no 94 | #PrintLastLog yes 95 | #TCPKeepAlive yes 96 | #UseLogin no 97 | #UsePrivilegeSeparation sandbox 98 | #PermitUserEnvironment no 99 | #Compression delayed 100 | #ClientAliveInterval 0 101 | #ClientAliveCountMax 3 102 | #UseDNS no 103 | #PidFile /var/run/sshd.pid 104 | #MaxStartups 10:30:100 105 | PermitTunnel no 106 | #ChrootDirectory none 107 | #VersionAddendum none 108 | 109 | # no default banner path 110 | #Banner none 111 | 112 | # Allow client to pass locale environment variables 113 | AcceptEnv LANG LC_* 114 | 115 | # override default of no subsystems 116 | Subsystem sftp /usr/lib/openssh/sftp-server 117 | 118 | # Example of overriding settings on a per-user basis 119 | #Match User anoncvs 120 | # X11Forwarding no 121 | AllowTcpForwarding no 122 | # PermitTTY no 123 | # ForceCommand cvs server -------------------------------------------------------------------------------- /lib/omada/py/create-super-admin.py: -------------------------------------------------------------------------------- 1 | import crypt 2 | from datetime import datetime 3 | import os 4 | import string 5 | from random import SystemRandom 6 | import argparse 7 | import pymongo 8 | import random 9 | import logging 10 | import base64 11 | import hashlib 12 | 13 | import omada 14 | 15 | parser = argparse.ArgumentParser() 16 | parser.add_argument('-u','--username', help='Omada username to create', required=True) 17 | parser.add_argument('-p', '--password', help='Omada password to create') 18 | args = parser.parse_args() 19 | 20 | randchoice = SystemRandom().choice 21 | password = ''.join(random.choice(string.ascii_letters) for i in range(8)) 22 | password = password + "Hh7!" 23 | 24 | def sha256_crypt(password): 25 | iterations = 500000 26 | contents = password.encode("utf-8") 27 | for i in range(iterations): 28 | contents = hashlib.sha256(contents).digest() 29 | return "$shiro1$SHA-256$500000$$" + base64.b64encode(contents).decode('ascii') 30 | 31 | 32 | def create_super_admin(password): 33 | logging.info("Creating Omada Super Admin") 34 | logging.info("Connecting to MongoDB...") 35 | mdb = omada.db() 36 | site_ids = [] 37 | logging.info("Gathering omadac_id...") 38 | omadac_id = mdb.omadac.find_one()["_id"] 39 | logging.info("Gathering side ids...") 40 | for site in mdb.site.find(): 41 | site_ids.append(str(site["_id"])) 42 | logging.info("Inserting User...") 43 | if omada.version() < (5, 8, 0): 44 | new_user_id = mdb.user.insert_one({ 45 | "name" : args.username, 46 | "password" : sha256_crypt(password), 47 | "omadac_id" : omadac_id, 48 | "role_type" : 0, 49 | "verified" : True, 50 | "permissions" : [ "license", "site", "read", "adopt", "admin", "write", "manage" ], 51 | "site_ids" : site_ids, 52 | "alert" : True, 53 | "all_site" : True, 54 | "time_created" : int(datetime.utcnow().timestamp()), 55 | "devices_upgrade_notification" : False, 56 | }).inserted_id 57 | else: 58 | new_tenant_id = mdb.tenant.insert_one({ 59 | "name" : args.username, 60 | "password" : sha256_crypt(password), 61 | "omadacs" : [omadac_id], 62 | "type": 0, 63 | "created_time" : datetime.fromtimestamp(datetime.utcnow().timestamp(), None), 64 | }).inserted_id 65 | 66 | new_user_id = mdb.user.insert_one({ 67 | "tenant_id": str(new_tenant_id), 68 | "name" : args.username, 69 | "omadac_id" : omadac_id, 70 | "user_type": 0, 71 | "role_id" : "master_admin_id", 72 | "verified" : True, 73 | "site_ids" : site_ids, 74 | "favorites": [], 75 | "alert" : True, 76 | "all_site" : True, 77 | "devices_upgrade_notification" : False, 78 | }).inserted_id 79 | 80 | print("Omada Super Admin created") 81 | print("Username: " + args.username) 82 | print("Password: " + password) 83 | 84 | if __name__ == "__main__": 85 | create_super_admin(args.password or password) 86 | -------------------------------------------------------------------------------- /lib/omada/py/delete-device.py: -------------------------------------------------------------------------------- 1 | import pymongo 2 | import argparse 3 | import json 4 | 5 | import omada 6 | 7 | parser = argparse.ArgumentParser() 8 | parser.add_argument('-m','--mac', help='UniFi device MAC address to delete') 9 | args = parser.parse_args() 10 | 11 | mdb = omada.db() 12 | mac = "" 13 | if len(args.mac) != 12 and len(args.mac) != 17: 14 | print("Invalid MAC address. Length must be either 12 or 17 characters.") 15 | exit() 16 | elif "-" in args.mac: 17 | mac = args.mac 18 | elif ":" in args.mac: 19 | mac = args.mac.replace(":", "-") 20 | else: 21 | mac += args.mac[0] + args.mac[1] + "-" 22 | mac += args.mac[2] + args.mac[3] + "-" 23 | mac += args.mac[4] + args.mac[5] + "-" 24 | mac += args.mac[6] + args.mac[7] + "-" 25 | mac += args.mac[8] + args.mac[9] + "-" 26 | mac += args.mac[10] + args.mac[11] 27 | r = mdb.device.delete_one({"mac": mac}) 28 | 29 | if r.deleted_count == 1: 30 | print("Device deleted") 31 | elif r.deleted_count == 0: 32 | print("Device not found") 33 | else: 34 | print("Unknown error occurred") 35 | -------------------------------------------------------------------------------- /lib/omada/py/delete-super-admin.py: -------------------------------------------------------------------------------- 1 | import crypt 2 | import os 3 | import string 4 | from random import SystemRandom 5 | import argparse 6 | import pymongo 7 | import logging 8 | 9 | import omada 10 | 11 | parser = argparse.ArgumentParser() 12 | parser.add_argument('-u','--username', help='Omada username of Super Admin to delete') 13 | args = parser.parse_args() 14 | 15 | if args.username is not None: 16 | logging.info("Connecting to MongoDB...") 17 | mdb = omada.db() 18 | is_email = None 19 | if "@" in args.username: 20 | is_email = True 21 | else: 22 | is_email = False 23 | 24 | if is_email == False: 25 | logging.info("Deleting Omada Super Admin by username") 26 | logging.info("Finding Admin ID...") 27 | logging.info("Deleting Admin...") 28 | mdb.user.delete_many({'name': args.username}) 29 | user_name = args.username 30 | print("Deleted the account for: ") 31 | print(args.username) 32 | 33 | if is_email == True: 34 | logging.info("Deleting Omada Super Admin by email") 35 | logging.info("Finding Admin ID...") 36 | user_list = mdb.user.find() 37 | user_name = "" 38 | for user in user_list: 39 | try: 40 | if args.username in base64.b64decode(user["email"]).decode('utf-8'): 41 | user_name = user["name"] 42 | except: 43 | continue 44 | logging.info("Deleting Admin...") 45 | mdb.user.delete_many({'name': user_name}) 46 | print("Deleted the account for username: ") 47 | print(args.username) 48 | 49 | if omada.version() >= (5, 8, 0): 50 | mdb.tenant.delete_many({'name': user_name}) 51 | 52 | 53 | else: 54 | print("Error: Missing argument. --username is required.") 55 | -------------------------------------------------------------------------------- /lib/omada/py/find-device.py: -------------------------------------------------------------------------------- 1 | import pymongo 2 | import argparse 3 | import json 4 | from bson import ObjectId 5 | 6 | import omada 7 | 8 | parser = argparse.ArgumentParser() 9 | parser.add_argument('-m','--mac', help='UniFi device MAC address to delete') 10 | args = parser.parse_args() 11 | 12 | mdb = omada.db() 13 | mac = "" 14 | if len(args.mac) != 12 and len(args.mac) != 17: 15 | print("Invalid MAC address. Length must be either 12 or 17 characters.") 16 | exit() 17 | elif "-" in args.mac: 18 | mac = args.mac 19 | elif ":" in args.mac: 20 | mac = args.mac.replace(":", "-") 21 | else: 22 | mac += args.mac[0] + args.mac[1] + "-" 23 | mac += args.mac[2] + args.mac[3] + "-" 24 | mac += args.mac[4] + args.mac[5] + "-" 25 | mac += args.mac[6] + args.mac[7] + "-" 26 | mac += args.mac[8] + args.mac[9] + "-" 27 | mac += args.mac[10] + args.mac[11] 28 | 29 | device = mdb.device.find_one({"mac": mac}, {"_id": 0, "ip": 1, "mac": 1, "model": 1, "version": 1, "site_id": 1}) 30 | if device is None: 31 | print("Device not found") 32 | exit() 33 | 34 | device_site_id = "" 35 | if device["site_id"] == "Default": 36 | device_site_id = "Default" 37 | else: 38 | device_site_id = ObjectId(device["site_id"]) 39 | 40 | site_info = mdb.site.find_one({"_id": device_site_id}, {"_id": 0, "desc":1, "name":1}) 41 | 42 | print("MAC: " + device["mac"]) 43 | print("IP: " + device["ip"]) 44 | print("Model: " + device["model"]) 45 | print("Version: " + str(device["version"])) 46 | print("Site Name: " + site_info["name"]) 47 | -------------------------------------------------------------------------------- /lib/omada/py/omada.py: -------------------------------------------------------------------------------- 1 | import functools 2 | 3 | import pymongo 4 | 5 | @functools.lru_cache(maxsize=None) 6 | def db(): 7 | return pymongo.MongoClient("mongodb://127.0.0.1:27217").omada 8 | 9 | @functools.lru_cache(maxsize=None) 10 | def version(): 11 | version_str = db().systemsetting.find_one()["start_up_info"]["db_version"] 12 | return tuple(map(int, version_str.split("."))) 13 | -------------------------------------------------------------------------------- /lib/rclone/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # error codes 4 | # 0 - exited without problems 5 | # 1 - parameters not supported were used or some unexpected error occurred 6 | # 2 - OS not supported by this script 7 | # 3 - installed version of rclone is up to date 8 | # 4 - supported unzip tools are not available 9 | 10 | set -e 11 | 12 | #when adding a tool to the list make sure to also add its corresponding command further in the script 13 | unzip_tools_list=('unzip' '7z' 'busybox') 14 | 15 | usage() { echo "Usage: curl https://rclone.org/install.sh | sudo bash [-s beta]" 1>&2; exit 1; } 16 | 17 | #check for beta flag 18 | if [ -n "$1" ] && [ "$1" != "beta" ]; then 19 | usage 20 | fi 21 | 22 | if [ -n "$1" ]; then 23 | install_beta="beta " 24 | fi 25 | 26 | 27 | #create tmp directory and move to it with macOS compatibility fallback 28 | tmp_dir=$(mktemp -d 2>/dev/null || mktemp -d -t 'rclone-install.XXXXXXXXXX') 29 | cd "$tmp_dir" 30 | 31 | 32 | #make sure unzip tool is available and choose one to work with 33 | set +e 34 | for tool in ${unzip_tools_list[*]}; do 35 | trash=$(hash "$tool" 2>>errors) 36 | if [ "$?" -eq 0 ]; then 37 | unzip_tool="$tool" 38 | break 39 | fi 40 | done 41 | set -e 42 | 43 | # exit if no unzip tools available 44 | if [ -z "$unzip_tool" ]; then 45 | printf "\nNone of the supported tools for extracting zip archives (${unzip_tools_list[*]}) were found. " 46 | printf "Please install one of them and try again.\n\n" 47 | exit 4 48 | fi 49 | 50 | # Make sure we don't create a root owned .config/rclone directory #2127 51 | export XDG_CONFIG_HOME=config 52 | 53 | #check installed version of rclone to determine if update is necessary 54 | version=$(rclone --version 2>>errors | head -n 1) 55 | if [ -z "$install_beta" ]; then 56 | current_version=$(curl -fsS https://downloads.rclone.org/version.txt) 57 | else 58 | current_version=$(curl -fsS https://beta.rclone.org/version.txt) 59 | fi 60 | 61 | if [ "$version" = "$current_version" ]; then 62 | printf "\nThe latest ${install_beta}version of rclone ${version} is already installed.\n\n" 63 | exit 3 64 | fi 65 | 66 | 67 | #detect the platform 68 | OS="$(uname)" 69 | case $OS in 70 | Linux) 71 | OS='linux' 72 | ;; 73 | FreeBSD) 74 | OS='freebsd' 75 | ;; 76 | NetBSD) 77 | OS='netbsd' 78 | ;; 79 | OpenBSD) 80 | OS='openbsd' 81 | ;; 82 | Darwin) 83 | OS='osx' 84 | ;; 85 | SunOS) 86 | OS='solaris' 87 | echo 'OS not supported' 88 | exit 2 89 | ;; 90 | *) 91 | echo 'OS not supported' 92 | exit 2 93 | ;; 94 | esac 95 | 96 | OS_type="$(uname -m)" 97 | case "$OS_type" in 98 | x86_64|amd64) 99 | OS_type='amd64' 100 | ;; 101 | i?86|x86) 102 | OS_type='386' 103 | ;; 104 | aarch64|arm64) 105 | OS_type='arm64' 106 | ;; 107 | arm*) 108 | OS_type='arm' 109 | ;; 110 | *) 111 | echo 'OS type not supported' 112 | exit 2 113 | ;; 114 | esac 115 | 116 | 117 | #download and unzip 118 | if [ -z "$install_beta" ]; then 119 | download_link="https://downloads.rclone.org/rclone-current-${OS}-${OS_type}.zip" 120 | rclone_zip="rclone-current-${OS}-${OS_type}.zip" 121 | else 122 | download_link="https://beta.rclone.org/rclone-beta-latest-${OS}-${OS_type}.zip" 123 | rclone_zip="rclone-beta-latest-${OS}-${OS_type}.zip" 124 | fi 125 | 126 | curl -OfsS "$download_link" 127 | unzip_dir="tmp_unzip_dir_for_rclone" 128 | # there should be an entry in this switch for each element of unzip_tools_list 129 | case "$unzip_tool" in 130 | 'unzip') 131 | unzip -a "$rclone_zip" -d "$unzip_dir" 132 | ;; 133 | '7z') 134 | 7z x "$rclone_zip" "-o$unzip_dir" 135 | ;; 136 | 'busybox') 137 | mkdir -p "$unzip_dir" 138 | busybox unzip "$rclone_zip" -d "$unzip_dir" 139 | ;; 140 | esac 141 | 142 | cd $unzip_dir/* 143 | 144 | #mounting rclone to environment 145 | 146 | case "$OS" in 147 | 'linux') 148 | #binary 149 | cp rclone /usr/bin/rclone.new 150 | chmod 755 /usr/bin/rclone.new 151 | chown root:root /usr/bin/rclone.new 152 | mv /usr/bin/rclone.new /usr/bin/rclone 153 | #manual 154 | if ! [ -x "$(command -v mandb)" ]; then 155 | echo 'mandb not found. The rclone man docs will not be installed.' 156 | else 157 | mkdir -p /usr/local/share/man/man1 158 | cp rclone.1 /usr/local/share/man/man1/ 159 | mandb 160 | fi 161 | ;; 162 | 'freebsd'|'openbsd'|'netbsd') 163 | #binary 164 | cp rclone /usr/bin/rclone.new 165 | chown root:wheel /usr/bin/rclone.new 166 | mv /usr/bin/rclone.new /usr/bin/rclone 167 | #manual 168 | mkdir -p /usr/local/man/man1 169 | cp rclone.1 /usr/local/man/man1/ 170 | makewhatis 171 | ;; 172 | 'osx') 173 | #binary 174 | mkdir -p /usr/local/bin 175 | cp rclone /usr/local/bin/rclone.new 176 | mv /usr/local/bin/rclone.new /usr/local/bin/rclone 177 | #manual 178 | mkdir -p /usr/local/share/man/man1 179 | cp rclone.1 /usr/local/share/man/man1/ 180 | ;; 181 | *) 182 | echo 'OS not supported' 183 | exit 2 184 | esac 185 | 186 | 187 | #update version variable post install 188 | version=$(rclone --version 2>>errors | head -n 1) 189 | 190 | printf "\n${version} has successfully installed." 191 | printf '\nNow run "rclone config" for setup. Check https://rclone.org/docs/ for more details.\n\n' 192 | exit 0 -------------------------------------------------------------------------------- /lib/uisp/py/archived/create-super-admin.py: -------------------------------------------------------------------------------- 1 | # For creating Super Admins on UNMS before version 1.2 2 | import os 3 | import bcrypt 4 | import uuid 5 | import argparse 6 | import string 7 | import random 8 | import re 9 | parser = argparse.ArgumentParser() 10 | parser.add_argument('-u','--username', help='Username of UNMS Super Admin to create') 11 | args = parser.parse_args() 12 | letters = string.ascii_lowercase 13 | password = ''.join(random.choice(letters) for i in range(8)) 14 | random_uuid = str(uuid.uuid4()) 15 | bcrypt_hash = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()) 16 | astr = "docker exec -it unms-postgres psql -U unms -c \"INSERT INTO unms.user (id,username,email,password,role) VALUES ('" + random_uuid + "','" + args.username + "','" + args.username + "@hostifi.com','" + bcrypt_hash.decode('utf-8') + "','superadmin');\"" 17 | astr = astr.replace('$', '\\$') 18 | print(astr) 19 | os.system(astr) 20 | print("UNMS Super Admin created") 21 | print("Username: " + args.username) 22 | print("Password: " + password) -------------------------------------------------------------------------------- /lib/uisp/py/create-super-admin.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import bcrypt 3 | import uuid 4 | import argparse 5 | import string 6 | import random 7 | import re 8 | 9 | parser = argparse.ArgumentParser() 10 | parser.add_argument('-u','--username', help='Username of UISP Super Admin to create') 11 | parser.add_argument('-p', '--password', help='UISP password to create') 12 | parser.add_argument('-s', '--site-group-id', help='Site group ID (optional)') 13 | parser.add_argument('-e', '--email', help='UISP email to create') 14 | 15 | args = parser.parse_args() 16 | letters = string.ascii_lowercase 17 | if not args.password: 18 | password = ''.join(random.choice(letters) for i in range(8)) 19 | else: 20 | password = args.password 21 | if not args.email: 22 | email = args.username + "@hostifi.com" 23 | else: 24 | email = args.email 25 | random_uuid = str(uuid.uuid4()) 26 | bcrypt_hash = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()) 27 | 28 | def psql(command, **variables): 29 | return subprocess.run( 30 | [ 31 | "docker", "exec", "--interactive", "unms-postgres", 32 | "psql", "--username=unms", "--dbname=unms", "--no-align", "--tuples-only", 33 | *("--variable={}={}".format(key, value) for key, value in variables.items()), 34 | ], 35 | input=command.encode("utf-8"), 36 | capture_output=True, 37 | check=True, 38 | ).stdout.decode("utf-8").splitlines() 39 | 40 | site_group_id = psql("SELECT group_id FROM access_group_site LIMIT 1;")[0] 41 | psql( 42 | "INSERT INTO unms.user(" + 43 | " id, username, email, password, role, site_group_id" + 44 | ") VALUES (" + 45 | " :'random_uuid', :'username', :'email', :'password_hash', 'superadmin', :'site_group_id'" + 46 | ")", 47 | random_uuid=random_uuid, 48 | username=args.username, 49 | email=email, 50 | password_hash=bcrypt_hash.decode("utf-8"), 51 | site_group_id=site_group_id, 52 | ) 53 | 54 | print("UISP Super Admin created") 55 | print("Username: " + args.username) 56 | print("Password: " + password) 57 | -------------------------------------------------------------------------------- /lib/unifi-video/py/create-super-admin.py: -------------------------------------------------------------------------------- 1 | import json 2 | import random 3 | import string 4 | import time 5 | from random import SystemRandom 6 | from datetime import datetime 7 | import argparse 8 | import pymongo 9 | from bson.objectid import ObjectId 10 | import bcrypt 11 | import bson 12 | 13 | parser = argparse.ArgumentParser() 14 | parser.add_argument('-u','--username', help='UniFi Video username to create') 15 | parser.add_argument('-e','--email', help='UniFi Video username to create') 16 | args = parser.parse_args() 17 | 18 | randchoice = SystemRandom().choice 19 | client = pymongo.MongoClient("mongodb://127.0.0.1:7441/av") 20 | mdb = client.av 21 | 22 | class Server(): 23 | def __init__(self, server_name): 24 | self.server_name = server_name 25 | 26 | def _get_random_api_key(self, size=32, chars=string.ascii_uppercase + string.ascii_lowercase): 27 | return ''.join(random.choice(chars) for _ in range(size)) 28 | 29 | def _get_random_adoption_key(self, size=8, chars=string.ascii_uppercase + string.ascii_lowercase): 30 | return ''.join(random.choice(chars) for _ in range(size)) 31 | 32 | def _get_random_reset_key(self, size=32, chars=string.ascii_uppercase + string.ascii_lowercase + string.digits): 33 | return ''.join(random.choice(chars) for _ in range(size)) 34 | 35 | def get_random_password(self, size=8, chars=string.ascii_uppercase + string.ascii_lowercase + string.digits): 36 | return ''.join(random.choice(chars) for _ in range(size)) 37 | 38 | def _create_user(self, account_id, super_admin_id): 39 | print("Deleting user for") 40 | print(account_id) 41 | response = '' 42 | print(self.server_name) 43 | response = mdb.user.insert({'accountId': ObjectId(account_id), 'userGroupId': ObjectId(super_admin_id), "disabled" : False, "apiKey" : self.get_random_api_key(), "enableApiAccess" : True, "enableLocalAccess" : True, "motionAlertSchedules" : { }, "subscribedMotion" : [ ], "subscribedCameraConnection" : [ ], "adoptionKey" : self.get_random_adoption_key(), "enableEmail" : True, "enablePush" : True, "sysDisconnectEmailAlert" : True, "sysDisconnectPushAlert" : True }) 44 | print("Response:") 45 | print(response) 46 | return response 47 | 48 | def _create_account(self, username, email, hashed): 49 | print("Creating account for") 50 | print(username) 51 | response = '' 52 | print(self.server_name) 53 | response = mdb.account.insert({'email': email, 'username': username, 'password': hashed, 'name': username, 'language': 'English', "resetKey" : self.get_random_reset_key(), "lastIp" : "192.247.121.76", "lastLogInTimestamp" : bson.int64.Int64(100)}) 54 | print("Response:") 55 | print(response) 56 | return response 57 | 58 | def _get_account_id(self, username): 59 | print("Getting account id for") 60 | print(username) 61 | response = '' 62 | print(self.server_name) 63 | response = mdb.account.find({'username': username}) 64 | print("Account id:") 65 | print(response[0]["_id"]) 66 | return response[0]["_id"] 67 | 68 | def _get_super_admin_user_group_id(self): 69 | print("Getting Super Admin id") 70 | response = '' 71 | print(self.server_name) 72 | response = mdb.usergroup.find({'groupType': "SUPER_ADMIN"}, {"_id"}) 73 | print("Super Admin id:") 74 | print(response[0]["_id"]) 75 | return response[0]["_id"] 76 | 77 | def create_super_admin(self, username, email): 78 | password = self.get_random_password() 79 | salt = bcrypt.gensalt(prefix=b"2a") 80 | hashed = bcrypt.hashpw(password, salt).encode('utf-8') 81 | super_admin_id = self._get_super_admin_user_group_id() 82 | self._create_account(username, email, hashed) 83 | account_id = self._get_account_id(username) 84 | self._create_user(account_id, super_admin_id) 85 | print("UniFi Video Super Admin created") 86 | print("Username: " + args.username) 87 | print("Password: " + password) 88 | 89 | if args.email is not None and args.username is not None: 90 | unifi_video_server = Server('localhost') 91 | unifi_video_server.create_super_admin(args.username, args.email) 92 | else: 93 | print("Error: Missing arguments. --username, and --email are required.") -------------------------------------------------------------------------------- /lib/unifi-video/py/delete-super-admin.py: -------------------------------------------------------------------------------- 1 | import json 2 | import random 3 | import string 4 | import time 5 | from random import SystemRandom 6 | from datetime import datetime 7 | import argparse 8 | import pymongo 9 | from bson.objectid import ObjectId 10 | 11 | parser = argparse.ArgumentParser() 12 | parser.add_argument('-u','--username', help='UniFi Video username to delete') 13 | args = parser.parse_args() 14 | 15 | client = pymongo.MongoClient("mongodb://127.0.0.1:7441/av") 16 | mdb = client.av 17 | 18 | class Server(): 19 | def __init__(self, server_name): 20 | self.server_name = server_name 21 | 22 | def _delete_user(self, account_id): 23 | print("Deleting user for") 24 | print(account_id) 25 | response = '' 26 | print(self.server_name) 27 | response = mdb.user.remove({'accountId': ObjectId(account_id)}) 28 | print("Response:") 29 | print(response) 30 | return response 31 | 32 | def _delete_account(self, username): 33 | print("Deleting account for") 34 | print(username) 35 | response = '' 36 | print(self.server_name) 37 | response = mdb.account.remove({'username': username}) 38 | print("Response:") 39 | print(response) 40 | return response 41 | 42 | def _get_account_id(self, username): 43 | print("Getting account id for") 44 | print(username) 45 | response = '' 46 | print(self.server_name) 47 | response = mdb.account.find({'username': username}) 48 | print("Account id:") 49 | print(response[0]["_id"]) 50 | return response[0]["_id"] 51 | 52 | def delete_super_admin(self, username): 53 | account_id = self._get_account_id(username) 54 | self._delete_account(username) 55 | self._delete_user(account_id) 56 | 57 | if args.username is not None: 58 | unifi_video_server = Server('localhost') 59 | unifi_video_server.delete_super_admin(args.username) 60 | print("Deleted the account for username: ") 61 | print(args.username) 62 | 63 | else: 64 | print "Error: Missing arguments. --username is required." -------------------------------------------------------------------------------- /lib/unifi/java/PruneBackup.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HostiFi/support-tools/4d96d1cb374dc469dd4b22a621d6980810d535be/lib/unifi/java/PruneBackup.class -------------------------------------------------------------------------------- /lib/unifi/java/PruneBackup.java: -------------------------------------------------------------------------------- 1 | import java.io.EOFException; 2 | import java.io.FileInputStream; 3 | import java.io.FileOutputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.OutputStream; 7 | 8 | import java.util.zip.GZIPInputStream; 9 | import java.util.zip.GZIPOutputStream; 10 | import java.util.zip.ZipEntry; 11 | import java.util.zip.ZipInputStream; 12 | import java.util.zip.ZipOutputStream; 13 | 14 | import javax.crypto.Cipher; 15 | import javax.crypto.CipherInputStream; 16 | import javax.crypto.CipherOutputStream; 17 | import javax.crypto.spec.IvParameterSpec; 18 | import javax.crypto.spec.SecretKeySpec; 19 | 20 | import org.bson.BSONObject; 21 | import org.bson.BasicBSONDecoder; 22 | import org.bson.BasicBSONEncoder; 23 | 24 | public class PruneBackup { 25 | public static SecretKeySpec KEY = 26 | new SecretKeySpec("bcyangkmluohmars".getBytes(), "AES"); 27 | public static IvParameterSpec IV = 28 | new IvParameterSpec("ubntenterpriseap".getBytes()); 29 | 30 | private static Cipher makeCipher(int mode) { 31 | try { 32 | var cipher = Cipher.getInstance("AES/CBC/NoPadding"); 33 | cipher.init(mode, KEY, IV); 34 | return cipher; 35 | } catch (Exception ex) { 36 | throw new RuntimeException(ex); 37 | } 38 | } 39 | 40 | private static ZipInputStream openBackupInput(InputStream in) { 41 | var cis = new CipherInputStream(in, makeCipher(Cipher.DECRYPT_MODE)); 42 | return new ZipInputStream(cis); 43 | } 44 | 45 | private static ZipOutputStream openBackupOutput(OutputStream out) { 46 | var cos = new CipherOutputStream(out, makeCipher(Cipher.ENCRYPT_MODE)); 47 | return new ZipOutputStream(cos); 48 | } 49 | 50 | private static void pruneDbGz(InputStream in, OutputStream out) 51 | throws IOException { 52 | 53 | var decoder = new BasicBSONDecoder(); 54 | var encoder = new BasicBSONEncoder(); 55 | var bsonIn = new GZIPInputStream(in); 56 | var bsonOut = new GZIPOutputStream(out); 57 | try { 58 | for (String collection = null;;) { 59 | BSONObject bson; 60 | try { 61 | bson = decoder.readObject(bsonIn); 62 | } catch (EOFException ex) { 63 | break; 64 | } 65 | if (bson.containsField("__cmd")) { 66 | if (!bson.get("__cmd").equals("select")) { 67 | throw new RuntimeException("Invalid command in bson"); 68 | } 69 | collection = (String) bson.get("collection"); 70 | } else { 71 | switch (collection) { 72 | case "alert": 73 | case "rogue": 74 | case "alarm": 75 | case "event": 76 | case "voucher": 77 | case "guest": 78 | continue; 79 | 80 | case "user": 81 | if (!(bson.containsField("use_fixedip") || 82 | bson.containsField("noted") || 83 | bson.containsField("blocked"))) { 84 | continue; 85 | } 86 | } 87 | } 88 | bsonOut.write(encoder.encode(bson)); 89 | } 90 | } finally { 91 | bsonOut.finish(); 92 | } 93 | } 94 | 95 | public static void main(String... args) throws IOException { 96 | if (args.length != 2) { 97 | System.err.println("Usage: PruneBackup in_file out_file"); 98 | System.exit(2); 99 | } 100 | 101 | var in_file = args[0]; 102 | var out_file = args[1]; 103 | 104 | try (var in = openBackupInput(new FileInputStream(in_file)); 105 | var out = openBackupOutput(new FileOutputStream(out_file))) { 106 | for (;;) { 107 | ZipEntry entry = null; 108 | try { 109 | entry = in.getNextEntry(); 110 | } catch (EOFException ex) {} 111 | if (entry == null) 112 | break; 113 | 114 | out.putNextEntry(entry); 115 | 116 | switch (entry.getName()) { 117 | case "db_stat.gz": 118 | new GZIPOutputStream(out).finish(); 119 | break; 120 | 121 | case "db.gz": 122 | pruneDbGz(in, out); 123 | break; 124 | 125 | default: 126 | in.transferTo(out); 127 | } 128 | } 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /lib/unifi/mongodb/libssl1.0.0_1.0.1t-1+deb8u12_amd64.deb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HostiFi/support-tools/4d96d1cb374dc469dd4b22a621d6980810d535be/lib/unifi/mongodb/libssl1.0.0_1.0.1t-1+deb8u12_amd64.deb -------------------------------------------------------------------------------- /lib/unifi/mongodb/prune.js: -------------------------------------------------------------------------------- 1 | // keep N-day worth of data 2 | var days=0; 3 | 4 | // change to false to have the script to really exclude old records 5 | // from the database. While true, no change at all will be made to the DB 6 | var dryrun=false; 7 | 8 | var now = new Date().getTime(), 9 | time_criteria = now - days * 86400 * 1000, 10 | time_criteria_in_seconds = time_criteria / 1000; 11 | 12 | print((dryrun ? "[dryrun] " : "") + "pruning data older than " + days + " days (" + time_criteria + ")... "); 13 | 14 | use ace; 15 | var collectionNames = db.getCollectionNames(); 16 | for (i=0; i", args.username)} 17 | } 18 | else: 19 | query = {"name": args.username} 20 | 21 | db = pymongo.MongoClient("mongodb://127.0.0.1:27117").ace 22 | admin = db.admin.find_one_and_delete(query) 23 | if admin is None: 24 | print("Admin not found!") 25 | sys.exit(1) 26 | 27 | name = admin.get("name") or admin.get("ubic_name") or None 28 | if name is not None: 29 | print(f"Deleted admin: {name}") 30 | else: 31 | print("Deleted admin with unknown name") 32 | 33 | print("Removing privileges from all sites...") 34 | db.privilege.delete_many({"admin_id": str(admin["_id"])}) 35 | -------------------------------------------------------------------------------- /lib/unifi/py/find-device.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import pymongo 4 | import argparse 5 | import json 6 | from bson import ObjectId 7 | 8 | parser = argparse.ArgumentParser() 9 | parser.add_argument('-m','--mac', help='UniFi device MAC address to delete') 10 | args = parser.parse_args() 11 | 12 | client = pymongo.MongoClient("mongodb://127.0.0.1:27117/ace") 13 | mdb = client.ace 14 | mac = "" 15 | if len(args.mac) != 12 and len(args.mac) != 17: 16 | print("Invalid MAC address. Length must be either 12 or 17 characters.") 17 | exit() 18 | elif ":" in args.mac: 19 | mac = args.mac 20 | elif "-" in args.mac: 21 | mac = args.mac.replace("-", ":") 22 | else: 23 | mac += args.mac[0] + args.mac[1] + ":" 24 | mac += args.mac[2] + args.mac[3] + ":" 25 | mac += args.mac[4] + args.mac[5] + ":" 26 | mac += args.mac[6] + args.mac[7] + ":" 27 | mac += args.mac[8] + args.mac[9] + ":" 28 | mac += args.mac[10] + args.mac[11] 29 | 30 | device = mdb.device.find_one({"mac": mac}, {"_id": 0, "ip": 1, "mac": 1, "model": 1, "model_in_lts": 1, "model_in_eol": 1, "version": 1, "adopted": 1, "inform_url": 1, "inform_ip": 1, "site_id": 1}) 31 | if device is None: 32 | print("Device not found") 33 | exit() 34 | site_info = mdb.site.find_one({"_id": ObjectId(device["site_id"])}, {"_id": 0, "desc":1, "name":1}) 35 | 36 | link = "" 37 | try: 38 | protocol, rest = device["inform_url"].split("://") 39 | hostname_port = rest.split(":") 40 | hostname = hostname_port[0] 41 | link = "https://" + hostname + ":8443/manage/" + site_info["name"] + "/devices" 42 | except: 43 | link = "Unknown" 44 | print("MAC: " + device.get("mac")) 45 | print("IP: " + device.get("ip")) 46 | print("Model: " + device.get("model")) 47 | print("LTS: " + str(device.get("model_in_lts"))) 48 | print("EOL: " + str(device.get("model_in_eol"))) 49 | print("Version: " + str(device.get("version"))) 50 | print("Inform URL: " + device.get("inform_url")) 51 | print("Inform IP: " + device.get("inform_ip")) 52 | print("Site Name: " + site_info["desc"]) 53 | print("Site Code: " + site_info["name"]) 54 | print("Link: " + link) 55 | -------------------------------------------------------------------------------- /lib/unifi/py/manual-backup.py: -------------------------------------------------------------------------------- 1 | from unifi import UniFi 2 | import os 3 | import datetime 4 | import time 5 | import requests 6 | import json 7 | import subprocess 8 | import re 9 | import random 10 | import socket 11 | import string 12 | import warnings 13 | 14 | dir_path = os.path.dirname(os.path.abspath(__file__)) 15 | 16 | warnings.filterwarnings('ignore', message='Unverified HTTPS request') 17 | 18 | r = socket.gethostname() 19 | hostname = r.split('.')[0] + ".hostifi.com" 20 | 21 | username_postfix = ''.join(random.choice(string.ascii_letters) for i in range(4)) 22 | 23 | def human_readable(bytes, units=[' bytes','KB','MB','GB','TB', 'PB', 'EB']): 24 | return str(bytes) + units[0] if bytes < 1024 else human_readable(bytes>>10, units[1:]) 25 | 26 | def get_unifi_version(server): 27 | s = requests.Session() 28 | r = s.get('https://' + server + ':8443/status', verify=False) 29 | r2 = json.loads(r.text) 30 | return r2['meta']['server_version'] 31 | 32 | user = "tempbackupscript" + username_postfix 33 | r = None 34 | try: 35 | r = subprocess.check_output(["/usr/bin/python3", os.path.join(dir_path, 'create-super-admin.py'), "-u", user, "-e", "support@hostifi.com"]) 36 | except subprocess.CalledProcessError as e: 37 | r = e.r 38 | try: 39 | password = re.findall("Password: (.+)\n", r.decode('ascii'))[0] 40 | unifi_server = UniFi(hostname, user, password) 41 | unifi_server.login() 42 | unifi_version = get_unifi_version(hostname) 43 | r = unifi_server.create_manual_backup() 44 | backup_dl_link = "https://" + hostname + ":8443" + r 45 | today = datetime.date.today() 46 | path_to_backup = "/usr/lib/unifi/data/backup/manual_" + unifi_version + "_" + today.strftime("%Y%m%d") + "_" + str(int(time.time())) + ".unf" 47 | unifi_server.download_backup(path_to_backup, backup_dl_link) 48 | unifi_server.logout() 49 | os.system("/usr/bin/python3 " + dir_path + "/delete-super-admin.py -u " + user) 50 | backup_size_in_bytes = os.stat(path_to_backup).st_size 51 | print("Manual backup stored at: " + path_to_backup) 52 | print("Backup size: " + human_readable(backup_size_in_bytes)) 53 | except: 54 | # Delete the admin account if anything fails 55 | print("Error: backup failed!") 56 | os.system("/usr/bin/python3 " + dir_path + "/delete-super-admin.py -u " + user) -------------------------------------------------------------------------------- /lib/unifi/py/prune-db.py: -------------------------------------------------------------------------------- 1 | import bson 2 | import argparse 3 | 4 | parser = argparse.ArgumentParser() 5 | parser.add_argument('-f','--file', help='db file name with path') 6 | args = parser.parse_args() 7 | 8 | collection = "" 9 | 10 | with open(args.file, "rb") as file_in, open(args.file + "_new", "wb") as file_out: 11 | for doc in bson.decode_file_iter(file_in): 12 | if "__cmd" in doc: 13 | if doc["__cmd"] != "select": 14 | raise RuntimeError(f"Unknown command: " + doc["__cmd"]) 15 | collection = doc["collection"] 16 | elif collection in ("event", "alarm", "rogue", "voucher", "guest"): 17 | continue 18 | elif collection == "user" and not any(key in doc for key in ("use_fixedip", "blocked", "noted")): 19 | continue 20 | file_out.write(bson.encode(doc)) -------------------------------------------------------------------------------- /lib/unifi/py/redirect.py: -------------------------------------------------------------------------------- 1 | import os 2 | import argparse 3 | 4 | parser = argparse.ArgumentParser() 5 | parser.add_argument('-d','--domain', help='Domain name to redirect 80, 443 to 8443 for') 6 | args = parser.parse_args() 7 | 8 | os.system("rm /var/www/html/index.html") 9 | os_cmd = """cat > /var/www/html/index.html << EOF 10 | 11 | EOF 12 | """ 13 | os.system(os_cmd) 14 | print("Port 80, 443 are now redirected to https://" + args.domain + ":8443") -------------------------------------------------------------------------------- /lib/unifi/py/restore-backup.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import random 3 | import os 4 | import socket 5 | from unifi import UniFi 6 | import string 7 | import logging 8 | 9 | logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') 10 | 11 | parser = argparse.ArgumentParser() 12 | parser.add_argument('-f','--file', help='Path to backup file to be restored') 13 | parser.add_argument('-w','--wizard', help='Kill the wizard [y/n]') 14 | args = parser.parse_args() 15 | 16 | try: 17 | dir_path = os.path.dirname(os.path.abspath(__file__)) 18 | password = ''.join(random.choice(string.ascii_letters) for i in range(8)) 19 | user = "temprestorescript" + ''.join(random.choice(string.ascii_letters) for i in range(4)) 20 | email = user + "@hostifi.com" 21 | r = socket.gethostname() 22 | hostname = r.split('.')[0] + ".hostifi.com" 23 | if args.wizard == "y": 24 | unifi_server = UniFi(hostname, user, password) 25 | unifi_server.complete_unifi_wizard(email) 26 | unifi_server.login() 27 | else: 28 | print("Creating super admin...") 29 | os.system("/usr/bin/python3 " + dir_path + "/create-super-admin.py -u " + user + " -p " + password + " -e support@hostifi.com") 30 | unifi_server = UniFi(hostname, user, password) 31 | unifi_server.login() 32 | print("This is args.file:") 33 | print(args.file) 34 | print("Done") 35 | if args.file is not None: 36 | print("Restoring UniFi from " + str(args.file)) 37 | unifi_server.restore_backup(args.file) 38 | unifi_server.logout() 39 | os.system("/usr/bin/python3 " + dir_path + "/delete-super-admin.py -u " + user) 40 | except Exception as e: 41 | logging.info(e) 42 | print("Error: restore failed!") 43 | os.system("/usr/bin/python3 " + dir_path + "/delete-super-admin.py -u " + user) -------------------------------------------------------------------------------- /lib/unifi/py/ugw-site-count.py: -------------------------------------------------------------------------------- 1 | import pymongo 2 | import json 3 | 4 | client = pymongo.MongoClient("mongodb://127.0.0.1:27117/ace") 5 | mdb = client.ace 6 | sites = mdb.site.find() 7 | site_ids = [] 8 | for site in sites: 9 | site_id = str(site["_id"]) 10 | if mdb.device.count({"site_id":site_id}) > 0: 11 | site_ids.append(site_id) 12 | 13 | SITES_COUNT = len(site_ids) 14 | 15 | # UGW3 ugw USG-3P 16 | UGW3_COUNT = mdb.device.count({"model":'UGW3'}) 17 | # UGW4 ugw USG-Pro-4 18 | UGW4_COUNT = mdb.device.count({"model":'UGW4'}) 19 | # UGWHD4 ugw USG 20 | UGWHD4_COUNT = mdb.device.count({"model":'UGWHD4'}) 21 | # UGWXG ugw USG-XG-8 22 | UGWXG_COUNT = mdb.device.count({"model":'UGWXG'}) 23 | # UXGPRO uxg UniFi NeXt-Gen Gateway PRO 24 | UXGPRO_COUNT = mdb.device.count({"model":'UXGPRO'}) 25 | 26 | # Sites with gt 0 devices 27 | OUTPUT_OBJ = {'site': SITES_COUNT, 'ugw3': UGW3_COUNT, 'ugw4': UGW4_COUNT, 'ugwhd4': UGWHD4_COUNT, 'ugwxg': UGWXG_COUNT, 'uxgpro': UXGPRO_COUNT} 28 | OUTPUT_JSON = json.dumps(OUTPUT_OBJ) 29 | print(OUTPUT_JSON) -------------------------------------------------------------------------------- /lib/unifi/py/unifi.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import logging 3 | import time 4 | import json 5 | import random 6 | import string 7 | 8 | class UniFi(object): 9 | def __init__(self, hostname, username, password): 10 | self.hostname = hostname 11 | self.username = username 12 | self.password = password 13 | self.port = 8443 14 | self.site_id = "default" 15 | self.url = "https://" + self.hostname + ":" + str(self.port) + "/" 16 | self.verify_ssl = False 17 | 18 | def login(self): 19 | logging.info("Logging in to " + self.hostname) 20 | attempts = 0 21 | while attempts < 25: 22 | attempts += 1 23 | url = self.url + "api/login" 24 | self.s = requests.Session() 25 | params = {'username': self.username, 'password': self.password} 26 | params = json.dumps(params) 27 | # Login 28 | r = self.s.post(url=url, data=params, verify=self.verify_ssl, timeout=30) 29 | logging.debug(r.text) 30 | logging.debug(r.status_code) 31 | if r.text != '{"meta":{"rc":"ok"},"data":[]}': 32 | logging.info("Login failed, trying again") 33 | time.sleep(1) 34 | else: 35 | logging.info('Logged in successfully') 36 | break 37 | 38 | def _set_wizard_default_admin(self, email): 39 | time_check = 0 40 | while time_check < 120: 41 | try: 42 | url = self.url + 'api/cmd/sitemgr' 43 | payload = {'cmd': 'add-default-admin', 'email': email, 'name': self.username, 44 | 'x_password': self.password} 45 | r = requests.post(url, data=json.dumps(payload), verify=self.verify_ssl) 46 | if r.status_code == 200: 47 | logging.info("Successfully set default admin") 48 | logging.info(r.text) 49 | break 50 | else: 51 | logging.info("Failed to set default admin") 52 | logging.debug(r.text) 53 | time.sleep(1) 54 | time_check += 1 55 | except Exception as e: 56 | time_check += 1 57 | logging.info("Failed to set default admin") 58 | logging.debug(e) 59 | time.sleep(1) 60 | 61 | def _set_wizard_country(self): 62 | time_check = 0 63 | while time_check < 120: 64 | try: 65 | url = self.url + 'api/set/setting/country' 66 | payload = {'code': '840'} 67 | r = requests.post(url, data=json.dumps(payload), verify=self.verify_ssl) 68 | if r.status_code == 200: 69 | logging.info("Successfully set default country") 70 | logging.info(r.text) 71 | break 72 | else: 73 | logging.info("Failed to set country") 74 | time_check +=1 75 | time.sleep(1) 76 | except Exception as e: 77 | logging.info("Failed to set country") 78 | logging.info(e) 79 | time_check += 1 80 | time.sleep(1) 81 | 82 | def _check_if_running(self): 83 | logging.info("Checking if UniFi is running") 84 | unifi_status = 0 85 | time_check = 0 86 | while time_check < 120: 87 | try: 88 | url = self.url + "status" 89 | logging.info(url) 90 | r = requests.get(url, verify=self.verify_ssl) 91 | logging.info(r.text) 92 | logging.info(r.status_code) 93 | if r.status_code == 200: 94 | if '"up":true' not in r.text: 95 | logging.info(r.text) 96 | logging.info(r.status_code) 97 | unifi_status = 1 98 | break 99 | else: 100 | logging.info("UniFi is running") 101 | break 102 | else: 103 | logging.info("waiting") 104 | time_check += 1 105 | time.sleep(1) 106 | except: 107 | logging.info("Failed to load") 108 | time.sleep(1) 109 | if unifi_status == 1: 110 | logging.info("UniFi install failed") 111 | else: 112 | logging.info("UniFi installed successfully") 113 | 114 | def _set_wizard_installed(self): 115 | time_check = 0 116 | while time_check < 120: 117 | time_check += 1 118 | try: 119 | url = self.url + 'api/cmd/system' 120 | payload = {'cmd': "set-installed"} 121 | r = requests.post(url, data=json.dumps(payload), verify=self.verify_ssl) 122 | if r.status_code == 200: 123 | logging.info("Successfully set UniFi installed") 124 | logging.info(r.text) 125 | break 126 | else: 127 | logging.info("Failed to load set installed") 128 | time.sleep(1) 129 | time_check +=1 130 | except: 131 | logging.info("Failed to load set installed") 132 | time.sleep(1) 133 | time_check +=1 134 | 135 | def _set_wizard_locale(self): 136 | logging.info('Setting UniFi Wizard Locale') 137 | time_check = 0 138 | while time_check < 120: 139 | try: 140 | url = self.url + 'api/set/setting/locale' 141 | payload = {'timezone': 'America/New_York'} 142 | r = requests.post(url, data=json.dumps(payload), verify=self.verify_ssl) 143 | if r.status_code == 200: 144 | logging.info("Successfully set locale") 145 | logging.info(r.text) 146 | break 147 | else: 148 | logging.info("Failed to load locale") 149 | time.sleep(1) 150 | time_check += 1 151 | except Exception as e: 152 | logging.info("Failed to load locale") 153 | logging.info(e) 154 | time_check += 1 155 | time.sleep(1) 156 | 157 | def _set_wizard_device_ssh_creds(self): 158 | time_check = 0 159 | while time_check < 120: 160 | time_check += 1 161 | try: 162 | url = self.url + 'api/set/setting/mgmt' 163 | payload = {'x_ssh_username': self.username, 'x_ssh_password': ''.join(random.choice(string.ascii_letters) for i in range(8))} 164 | r = requests.post(url, data=json.dumps(payload), verify=self.verify_ssl) 165 | if r.status_code == 200: 166 | logging.info("Successfully set device SSH creds") 167 | logging.info(r.text) 168 | break 169 | else: 170 | logging.info(r.text) 171 | logging.info(r.status_code) 172 | logging.info("Failed to load device ssh creds") 173 | time.sleep(1) 174 | time_check += 1 175 | except Exception as e: 176 | logging.info(e) 177 | logging.info("Failed to load device ssh creds") 178 | time.sleep(1) 179 | time_check += 1 180 | 181 | def _set_wizard_autobackups(self): 182 | logging.info("Setting wizard autobackup settings") 183 | time_check = 0 184 | while time_check < 120: 185 | time_check += 1 186 | try: 187 | url = self.url + 'api/set/setting/super_mgmt' 188 | payload = {"autobackup_enabled": 'true', "autobackup_cron_expr": "0 2 * * *", "autobackup_timezone": "America/New_York", 189 | "autobackup_days": '0'} 190 | r = requests.post(url, data=json.dumps(payload), verify=self.verify_ssl) 191 | if r.status_code == 200: 192 | logging.info("Successfully set auto backups") 193 | logging.info(r.text) 194 | break 195 | else: 196 | logging.info("Failed to load auto backups") 197 | time.sleep(1) 198 | time_check += 1 199 | except: 200 | logging.info("Failed to load auto backups") 201 | time_check += 1 202 | time.sleep(1) 203 | 204 | def complete_unifi_wizard(self, email): 205 | logging.info('Killing the wizard') 206 | self._check_if_running() 207 | self._set_wizard_default_admin(email) 208 | self._set_wizard_country() 209 | self._set_wizard_locale() 210 | self._set_wizard_autobackups() 211 | self._set_wizard_device_ssh_creds() 212 | self._set_wizard_installed() 213 | logging.info('We killed the wizard!') 214 | 215 | def download_backup(self, storage_path, link): 216 | logging.info("Downloading backup") 217 | r = self.s.get(url=link, verify=self.verify_ssl, timeout=120) 218 | open(storage_path, 'wb').write(r.content) 219 | 220 | def create_manual_backup(self): 221 | logging.info("Creating settings only manual backup") 222 | params = {"cmd": "backup", "days": 0} 223 | params = json.dumps(params) 224 | url = self.url + "api/s/default/cmd/backup" 225 | r = self.s.post(url=url, data=params, verify=self.verify_ssl, timeout=120) 226 | return json.loads(r.text)["data"][0]["url"] 227 | 228 | def restore_backup(self, path_to_backup_file): 229 | logging.info("Restoring server to " + path_to_backup_file) 230 | r = self.upload_backup(path_to_backup_file) 231 | backup_id = r["meta"]["backup_id"] 232 | site_id = r["data"][0]["sites"][0]["_id"] 233 | logging.info(site_id) 234 | logging.info(backup_id) 235 | params = {"cmd": "restore", "backup_id": backup_id, "site_id": site_id} 236 | params = json.dumps(params) 237 | url = self.url + "api/s/default/cmd/backup" 238 | r = self.s.post(url=url, data=params, verify=self.verify_ssl, timeout=120) 239 | logging.info(r.text) 240 | return r 241 | 242 | def upload_backup(self, path_to_backup_file): 243 | logging.info("Uploading unf") 244 | logging.info("Restoring backup: " + path_to_backup_file) 245 | headers = { 246 | 'Accept': 'application/json, text/plain, */*', 247 | 'Host': self.hostname + ':8443', 248 | 'Origin': self.url, 249 | 'Referer': self.url + 'manage/site/default/dashboard', 250 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36', 251 | 'X-Requested-With': 'XMLHttpRequest', 252 | } 253 | upload_url = self.url + "upload/backup" 254 | files = {'file': open(path_to_backup_file,'rb')} 255 | r = self.s.post(upload_url, verify=self.verify_ssl, files=files, headers=headers) 256 | r = json.loads(r.text) 257 | logging.info(r) 258 | return r 259 | 260 | def logout(self): 261 | logging.info("Logging out of " + self.hostname) 262 | url = self.url + "logout" 263 | r = self.s.get(url=url, verify=self.verify_ssl, timeout=30) 264 | self.s.close() 265 | logging.info("Logged out") 266 | logging.info(r.status_code) -------------------------------------------------------------------------------- /lib/unifi/ssl/import-ssl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | while getopts d: option 3 | do 4 | case "${option}" 5 | in 6 | d) HOSTNAME=${OPTARG};; 7 | esac 8 | done 9 | 10 | UNIFI_HOSTNAME=$HOSTNAME 11 | UNIFI_SERVICE=unifi 12 | 13 | UNIFI_DIR=/var/lib/unifi 14 | JAVA_DIR=/usr/lib/unifi 15 | KEYSTORE=${UNIFI_DIR}/keystore 16 | 17 | LE_LIVE_DIR=/etc/letsencrypt/live 18 | 19 | ALIAS=unifi 20 | PASSWORD=aircontrolenterprise 21 | 22 | echo "Importing SSL to UniFi" 23 | 24 | LE_MODE=true 25 | 26 | PRIV_KEY=${LE_LIVE_DIR}/${UNIFI_HOSTNAME}/privkey.pem 27 | CHAIN_FILE=${LE_LIVE_DIR}/${UNIFI_HOSTNAME}/fullchain.pem 28 | 29 | if [[ ${LE_MODE} == "true" ]]; then 30 | echo "Checking cert to see if it changed..." 31 | if md5sum -c "${LE_LIVE_DIR}/${UNIFI_HOSTNAME}/privkey.pem.md5" &>/dev/null; then 32 | echo "Cert is unchanged, exiting..." 33 | exit 0 34 | else 35 | echo "SSL changed, updating UniFi..." 36 | fi 37 | fi 38 | 39 | if [[ ! -f ${PRIV_KEY} ]] || [[ ! -f ${CHAIN_FILE} ]]; then 40 | echo "Files are missing" 41 | exit 1 42 | else 43 | echo "Importing files" 44 | fi 45 | 46 | P12_TEMP=$(mktemp) 47 | 48 | echo "Stopping UniFi" 49 | service "${UNIFI_SERVICE}" stop 50 | 51 | if [[ ${LE_MODE} == "true" ]]; then 52 | md5sum "${PRIV_KEY}" > "${LE_LIVE_DIR}/${UNIFI_HOSTNAME}/privkey.pem.md5" 53 | fi 54 | 55 | if [[ -s "${KEYSTORE}.orig" ]]; then 56 | cp "${KEYSTORE}" "${KEYSTORE}.bak" 57 | else 58 | cp "${KEYSTORE}" "${KEYSTORE}.orig" 59 | fi 60 | 61 | if [[ -f ${SIGNED_CRT} ]]; then 62 | openssl pkcs12 -export \ 63 | -in "${CHAIN_FILE}" \ 64 | -in "${SIGNED_CRT}" \ 65 | -inkey "${PRIV_KEY}" \ 66 | -out "${P12_TEMP}" -passout pass:"${PASSWORD}" \ 67 | -name "${ALIAS}" 68 | else 69 | openssl pkcs12 -export \ 70 | -in "${CHAIN_FILE}" \ 71 | -inkey "${PRIV_KEY}" \ 72 | -out "${P12_TEMP}" -passout pass:"${PASSWORD}" \ 73 | -name "${ALIAS}" 74 | fi 75 | 76 | echo "Removing previous data from UniFi" 77 | keytool -delete -alias "${ALIAS}" -keystore "${KEYSTORE}" -deststorepass "${PASSWORD}" 78 | 79 | 80 | echo "Importing SSL into UniFi" 81 | keytool -importkeystore \ 82 | -srckeystore "${P12_TEMP}" -srcstoretype PKCS12 \ 83 | -srcstorepass "${PASSWORD}" \ 84 | -destkeystore "${KEYSTORE}" \ 85 | -deststorepass "${PASSWORD}" \ 86 | -destkeypass "${PASSWORD}" \ 87 | -alias "${ALIAS}" -trustcacerts 88 | 89 | rm -f "${P12_TEMP}" 90 | 91 | echo "Restarting UniFi" 92 | service "${UNIFI_SERVICE}" start 93 | 94 | 95 | echo "Done" 96 | 97 | exit 0 -------------------------------------------------------------------------------- /lib/unifi/ssl/install-ssl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Parse domains to an array 3 | while getopts e:d: option; do 4 | case $option in 5 | "d") DOMAINS+=("$OPTARG");; 6 | "e") EMAIL=${OPTARG};; 7 | esac 8 | done 9 | 10 | for DOMAIN in "${DOMAINS[@]}"; do 11 | stringprefix=" -d " 12 | stringpostfix=" " 13 | DOMAINSTR+=$stringprefix$DOMAIN$stringpostfix 14 | done 15 | 16 | apt-get autoremove -y 17 | modprobe ip_tables 18 | echo 'ip_tables' >> /etc/modules 19 | 20 | echo "Removing Apache2" 21 | apt-get remove apache2 -y 22 | 23 | echo "Installing NGINX" 24 | apt-get install nginx-light -y 25 | 26 | echo "Installing Let's Encrypt" 27 | apt-get update -y 28 | apt-get install python-certbot-nginx -t stretch-backports -y 29 | 30 | echo "Getting cert" 31 | certbot --nginx --email $EMAIL --agree-tos --no-eff-email --expand $DOMAINSTR --no-redirect --quiet --force-renewal 32 | 33 | echo "Importing cert to UniFi" 34 | /bin/bash /root/support-tools/lib/unifi/ssl/import-ssl.sh -d ${DOMAINS[0]} 35 | 36 | echo "Creating certbot cron" 37 | crontab -l > /root/certbotcron 38 | echo "0 22 * * * /usr/bin/certbot renew" >> /root/certbotcron 39 | crontab /root/certbotcron 40 | rm /root/certbotcron 41 | 42 | echo "Creating Let's Encrypt cron" 43 | crontab -l > /root/letsencryptcron 44 | echo "0 23 * * * /bin/bash /root/support-tools/lib/unifi/ssl/import-ssl.sh -d ${DOMAINS[0]}" >> /root/letsencryptcron 45 | crontab /root/letsencryptcron 46 | rm /root/letsencryptcron 47 | 48 | echo "Removing old SSL script" 49 | rm /root/unifi-ssl.sh 50 | 51 | echo "Restarting services" 52 | systemctl restart nginx 53 | systemctl restart unifi 54 | 55 | echo "Done!" -------------------------------------------------------------------------------- /lib/zabbix/maintenance-mode.py: -------------------------------------------------------------------------------- 1 | import zabbix 2 | import config 3 | import logging 4 | import socket 5 | 6 | # logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') 7 | 8 | r = socket.gethostname() 9 | hostname = r.split('.')[0] + ".hostifi.com" 10 | 11 | zabbix = zabbix.Zabbix() 12 | hostid = [zabbix.get_hostid_from_hostname(hostname)] 13 | logging.info("hostid:") 14 | logging.info(hostid) 15 | existing_hostids = zabbix.get_hostids_in_maintenance_mode() 16 | logging.info("Existing hostids:") 17 | logging.info(existing_hostids) 18 | 19 | if hostid[0] in existing_hostids: 20 | logging.info("Removing server from maintenance mode on Zabbix") 21 | maintenance_ids = [] 22 | maintenance_ids = zabbix.get_maintenance_ids_from_hostid(hostid[0]) 23 | logging.info("Maintenance IDs:") 24 | logging.info(maintenance_ids) 25 | for maintenanceid in maintenance_ids: 26 | logging.info("Removing " + hostid[0] + " from " + maintenanceid) 27 | zabbix.remove_hostids_from_maintenance_mode(maintenanceid, hostid) 28 | zabbix.delete_maintenance_period(maintenanceid) 29 | print("Maintenance mode disabled") 30 | else: 31 | logging.info("Placing server in maintenance period on Zabbix...") 32 | maintenanceid = zabbix.create_maintenance_period(hostname) 33 | zabbix.put_hostids_into_maintenance_mode(maintenanceid, hostid) 34 | logging.info("Server is in maintenance mode on Zabbix now") 35 | print("Maintenance mode enabled") 36 | -------------------------------------------------------------------------------- /lib/zabbix/zabbix.py: -------------------------------------------------------------------------------- 1 | from pyzabbix import ZabbixAPI 2 | import config 3 | import sys 4 | import logging 5 | import argparse 6 | from enum import Enum 7 | import warnings 8 | import time 9 | import random 10 | import string 11 | 12 | class Zabbix(object): 13 | def __init__(self): 14 | self.zapi = ZabbixAPI(config.ZABBIX_URL) 15 | self.zapi.login(config.ZABBIX_API_USER, config.ZABBIX_API_KEY) 16 | 17 | def get_maintenance_ids_from_hostid(self, hostid): 18 | logging.info("Getting maintenance ids from hostid") 19 | maintenance_ids = [] 20 | r = self.zapi.maintenance.get(selectHosts="extend") 21 | logging.info(r) 22 | for row in r: 23 | for host in row["hosts"]: 24 | if host["hostid"] == hostid: 25 | logging.info(row["maintenanceid"]) 26 | maintenance_ids.append(row["maintenanceid"]) 27 | return maintenance_ids 28 | 29 | def get_hostid_from_hostname(self, hostname): 30 | logging.info("Getting Zabbix hostid from hostname...") 31 | return self.zapi.host.get(filter={"host": [hostname]})[0]["hostid"] 32 | 33 | def delete_maintenance_period(self, maintenanceid): 34 | logging.info("Deleting maintenance period...") 35 | r = self.zapi.maintenance.delete(maintenanceid) 36 | logging.info(r) 37 | return r 38 | 39 | def get_random_string(self, size=5, chars=string.digits + string.ascii_lowercase): 40 | return ''.join(random.choice(chars) for _ in range(size)) 41 | 42 | def create_maintenance_period(self, server): 43 | logging.info("Creating maintenance period...") 44 | r = self.zapi.maintenance.create({ 45 | "name": server + " - temporary maintenance period " + self.get_random_string(), 46 | "active_since": 1358844540, 47 | "active_till": 2119446139, 48 | "groupids": [ 49 | config.ZABBIX_EMPTY_HOSTGROUPID 50 | ], 51 | "hostids": [], 52 | "timeperiods": [ 53 | { 54 | "timeperiod_type": 0, 55 | "period": 3600 56 | } 57 | ] 58 | }) 59 | logging.info(r) 60 | return r["maintenanceids"][0] 61 | 62 | def get_hostids_in_maintenance_period(self, maintenanceid): 63 | logging.info("Getting hosts in maintenance period...") 64 | r = self.zapi.maintenance.get(params={"maintenanceid": maintenanceid}, selectHosts="extend") 65 | logging.info(r) 66 | existing_hostids = [] 67 | for row in r: 68 | logging.info(row) 69 | if row["maintenanceid"] == maintenanceid: 70 | logging.info("Found matching maintenanceid") 71 | for host in row["hosts"]: 72 | logging.info(host) 73 | existing_hostids.append(host["hostid"]) 74 | logging.info("Existing hostids list:") 75 | logging.info(existing_hostids) 76 | return existing_hostids 77 | 78 | def get_hostids_in_maintenance_mode(self): 79 | logging.info("Getting hosts in maintenance mode...") 80 | r = self.zapi.maintenance.get(selectHosts="extend") 81 | logging.info(r) 82 | existing_hostids = [] 83 | for row in r: 84 | for host in row["hosts"]: 85 | logging.info(host) 86 | existing_hostids.append(host["hostid"]) 87 | return existing_hostids 88 | 89 | def put_hostids_into_maintenance_mode(self, maintenanceid, hostids): 90 | logging.info("Putting hosts into maintenance mode...") 91 | logging.info("Get existing hostids already in the maintenance period") 92 | existing_hostids = self.get_hostids_in_maintenance_period(maintenanceid) 93 | logging.info("Add the new hostids to the list, skipping duplicates") 94 | new_hostids = list(set(existing_hostids + hostids)) 95 | logging.info(new_hostids) 96 | logging.info("Updating maintenance period with new hostids") 97 | r = self.zapi.maintenance.update({"maintenanceid": maintenanceid, "hostids": new_hostids}) 98 | logging.info(r) 99 | return r 100 | 101 | def remove_hostids_from_maintenance_mode(self, maintenanceid, hostids): 102 | logging.info("Removing hosts from maintenance mode...") 103 | logging.info("Get existing hostids already in the maintenance period") 104 | existing_hostids = self.get_hostids_in_maintenance_period(maintenanceid) 105 | logging.info("Removing hostids from the list...") 106 | new_hostids = list(filter(lambda i: i not in hostids, existing_hostids)) 107 | logging.info(new_hostids) 108 | logging.info("Updating maintenance period with new hostids") 109 | r = self.zapi.maintenance.update({"maintenanceid": maintenanceid, "hostids": new_hostids}) 110 | logging.info(r) 111 | return r -------------------------------------------------------------------------------- /omada/count-devices.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | mongo omada --eval "db.device.count();" --port 27217 --quiet -------------------------------------------------------------------------------- /omada/create-super-admin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Username of Omada Super Admin to create: " 3 | read USERNAME 4 | parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) 5 | cd "$parent_path" 6 | /usr/bin/python3 ../lib/omada/py/create-super-admin.py -u $USERNAME 7 | -------------------------------------------------------------------------------- /omada/delete-device.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | read -p "MAC of device to delete: " MAC 3 | cd "$(dirname "${BASH_SOURCE[0]}")" 4 | /usr/bin/python3 ../lib/omada/py/delete-device.py -m "$MAC" 5 | -------------------------------------------------------------------------------- /omada/delete-super-admin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Username, email address, or partial email address (like @domain.com) of Omada Super Admin to delete: " 3 | read USERNAME 4 | parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) 5 | cd "$parent_path" 6 | /usr/bin/python3 ../lib/omada/py/delete-super-admin.py -u $USERNAME -------------------------------------------------------------------------------- /omada/find-device.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | read -p "MAC of device to find: " MAC 3 | cd "$(dirname "${BASH_SOURCE[0]}")" 4 | /usr/bin/python3 ../lib/omada/py/find-device.py -m "$MAC" 5 | -------------------------------------------------------------------------------- /omada/list-admins.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | mongo --port 27217 omada --eval "db.user.find().forEach(printjson);" -------------------------------------------------------------------------------- /uisp/archived/create-super-admin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Username of UNMS (before version 1.2) Super Admin to create: " 3 | read USERNAME 4 | parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) 5 | cd "$parent_path" 6 | /usr/bin/python3 ../lib/uisp/py/archived/create-super-admin.py -u $USERNAME -------------------------------------------------------------------------------- /uisp/count-devices.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker exec unms-postgres psql -U unms -c "SELECT count(*) AS value FROM device WHERE device.authorized IS TRUE AND device.type <> 'blackBox';" -t -X -A -------------------------------------------------------------------------------- /uisp/create-super-admin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Username of UISP Super Admin to create: " 3 | read USERNAME 4 | parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) 5 | cd "$parent_path" 6 | /usr/bin/python3 ../lib/uisp/py/create-super-admin.py -u $USERNAME -------------------------------------------------------------------------------- /uisp/delete-super-admin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Username of UISP Super Admin to delete: " 3 | read USERNAME 4 | docker exec --interactive unms-postgres \ 5 | psql --username=unms --dbname=unms --variable=username="$USERNAME" \ 6 | <<<"DELETE FROM nms_user_view WHERE username = :'username'" 7 | echo "Deleted UISP Super Admin for $USERNAME" 8 | -------------------------------------------------------------------------------- /uisp/maintenance-mode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) 3 | cd "$parent_path" 4 | /usr/bin/python3 ../lib/zabbix/maintenance-mode.py -------------------------------------------------------------------------------- /unifi-video/count-devices.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | mongo av --eval "db.camera.count();" --port 7441 --quiet -------------------------------------------------------------------------------- /unifi-video/create-super-admin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Username of UniFi Super Admin to create: " 3 | read USERNAME 4 | echo "Email address of UniFi Super Admin to create: " 5 | read EMAIL 6 | parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) 7 | cd "$parent_path" 8 | /usr/bin/python3 ../lib/unifi-video/py/create-super-admin.py -u $USERNAME -e $EMAIL -------------------------------------------------------------------------------- /unifi-video/delete-super-admin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Username of UniFi Video Super Admin to delete: " 3 | read USERNAME 4 | parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) 5 | cd "$parent_path" 6 | /usr/bin/python3 ..lib/unifi-video/py/delete-super-admin.py -u $USERNAME -------------------------------------------------------------------------------- /unifi-video/maintenance-mode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) 3 | cd "$parent_path" 4 | /usr/bin/python3 ../lib/zabbix/maintenance-mode.py -------------------------------------------------------------------------------- /unifi/add-ssh-key-to-unifi-user.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Still in development, use the Notion guide for now! -rchase" 3 | exit 4 | echo "Paste certificate and end with a blank line:" 5 | KEY=$(sed '/^$/q') 6 | echo $KEY -------------------------------------------------------------------------------- /unifi/archived/prune-backup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Reilly's less efficient Python3 implementation of Andrew's Java prune-backup.sh 3 | 4 | set -e 5 | 6 | echo "Input .unf file name:" 7 | read INPUT_UNF 8 | echo "Output .unf file name:" 9 | read OUTPUT_UNF 10 | 11 | OUTPUT_ZIP="unf.zip" 12 | DIR_PATH=$(cd $(dirname “${BASH_SOURCE:-$0}”) && pwd) 13 | TMP_FILE=$(mktemp) 14 | trap "rm -f ${TMP_FILE}" EXIT 15 | 16 | openssl enc -d -in "${INPUT_UNF}" -out "${TMP_FILE}" -aes-128-cbc -K 626379616e676b6d6c756f686d617273 -iv 75626e74656e74657270726973656170 -nopad 17 | yes | zip -FF "${TMP_FILE}" --out "${OUTPUT_ZIP}" > /dev/null 2>&1 18 | 19 | unzip ${OUTPUT_ZIP} -d unf_unzip > /dev/null 2>&1 20 | rm unf_unzip/db_stat.gz 21 | touch unf_unzip/db_stat 22 | gzip unf_unzip/db_stat 23 | gunzip unf_unzip/db.gz 24 | /usr/bin/python3 /root/support-tools/lib/unifi/py/prune-db.py -f $DIR_PATH/unf_unzip/db 25 | mv unf_unzip/db_new unf_unzip/db 26 | gzip unf_unzip/db 27 | cd unf_unzip 28 | zip -r ../unf_new.zip . > /dev/null 2>&1 29 | cd .. 30 | rm -R unf_unzip > /dev/null 2>&1 31 | 32 | INPUT_ZIP="unf_new.zip" 33 | 34 | openssl enc -e -in "${INPUT_ZIP}" -out "${OUTPUT_UNF}" -aes-128-cbc -K 626379616e676b6d6c756f686d617273 -iv 75626e74656e74657270726973656170 35 | 36 | rm unf_new.zip 37 | rm unf.zip -------------------------------------------------------------------------------- /unifi/check-owner.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) 3 | cd "$parent_path" 4 | /usr/bin/python3 ../lib/unifi/py/check-owner.py -------------------------------------------------------------------------------- /unifi/count-devices.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | mongo ace --eval "db.device.count();" --port 27117 --quiet -------------------------------------------------------------------------------- /unifi/create-mongodb-prune-cronjob.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | mkdir -p /etc/systemd/system 5 | cat > /etc/systemd/system/unifi-prune-mongodb.service <<'EOF' 6 | [Unit] 7 | Description=Prune statistics from the UniFi database 8 | Requires=unifi.service 9 | After=unifi.service 10 | 11 | [Service] 12 | Type=oneshot 13 | ExecStart=/bin/bash /root/support-tools/unifi/prune-mongodb.sh 14 | EOF 15 | 16 | cat > /etc/systemd/system/unifi-prune-mongodb.timer <<'EOF' 17 | [Unit] 18 | Description=Prune UniFi statistics every hour 19 | 20 | [Timer] 21 | OnCalendar=hourly 22 | 23 | [Install] 24 | WantedBy=timers.target 25 | EOF 26 | 27 | systemctl daemon-reload 28 | systemctl enable --now unifi-prune-mongodb.timer 29 | 30 | echo "This server will now prune all UniFi statistics every hour" 31 | -------------------------------------------------------------------------------- /unifi/create-ro-super-admin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | read -p "Username of UniFi Read-Only Super Admin to create: " USERNAME 3 | read -p "Email address of UniFi Super Admin to create: " EMAIL 4 | cd "$(dirname "${BASH_SOURCE[0]}")" 5 | /usr/bin/python3 ../lib/unifi/py/create-super-admin.py -u "$USERNAME" -e "$EMAIL" --read-only 6 | -------------------------------------------------------------------------------- /unifi/create-super-admin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Username of UniFi Super Admin to create: " 3 | read USERNAME 4 | echo "Email address of UniFi Super Admin to create: " 5 | read EMAIL 6 | parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) 7 | cd "$parent_path" 8 | /usr/bin/python3 ../lib/unifi/py/create-super-admin.py -u $USERNAME -e $EMAIL -------------------------------------------------------------------------------- /unifi/delete-device.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "MAC of device to delete: " 3 | read MAC 4 | parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) 5 | cd "$parent_path" 6 | /usr/bin/python3 ../lib/unifi/py/delete-device.py -m $MAC -------------------------------------------------------------------------------- /unifi/delete-old-hostifi-admins.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) 3 | cd "$parent_path" 4 | /usr/bin/python3 ../lib/unifi/py/delete-old-hostifi-admins.py 5 | -------------------------------------------------------------------------------- /unifi/delete-super-admin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Username, email address, or partial email address (like @domain.com) of UniFi Super Admin to delete: " 3 | read USERNAME 4 | parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) 5 | cd "$parent_path" 6 | /usr/bin/python3 ../lib/unifi/py/delete-super-admin.py -u $USERNAME -------------------------------------------------------------------------------- /unifi/find-device.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "MAC of device to find: " 3 | read MAC 4 | parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) 5 | cd "$parent_path" 6 | /usr/bin/python3 ../lib/unifi/py/find-device.py -m $MAC -------------------------------------------------------------------------------- /unifi/install-ssl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | printf "Possible domain names for SSL:\n" 3 | ls /etc/letsencrypt/live 4 | printf "\nEnter a comma separated list of domain names to install a UniFi SSL for:\n" 5 | read DOMAINS 6 | IFS=', ' read -r -a DOMAINLIST <<< "$DOMAINS" 7 | for DOMAIN in "${DOMAINLIST[@]}" 8 | do 9 | stringprefix=" -d " 10 | stringpostfix=" " 11 | DOMAINSTR+=$stringprefix$DOMAIN$stringpostfix 12 | done 13 | parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) 14 | cd "$parent_path" 15 | /bin/bash ../lib/unifi/ssl/install-ssl.sh $DOMAINSTR -e support@hostifi.com -------------------------------------------------------------------------------- /unifi/list-admins.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | mongo --port 27117 ace --eval "db.admin.find({},{_id:false, name:true, email:true, ubic_name:true, ubic_uuid:true, requires_new_password:true, email_alert_enabled:true, push_alert_enabled:true, role_type:true}).forEach(printjson);" -------------------------------------------------------------------------------- /unifi/maintenance-mode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) 3 | cd "$parent_path" 4 | /usr/bin/python3 ../lib/zabbix/maintenance-mode.py -------------------------------------------------------------------------------- /unifi/manual-backup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) 3 | cd "$parent_path" 4 | /usr/bin/python3 ../lib/unifi/py/manual-backup.py -------------------------------------------------------------------------------- /unifi/prune-backup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu 4 | shopt -s extglob 5 | 6 | declare in_file out_file 7 | if (( $# )); then 8 | in_file="$1"; shift 9 | else 10 | IFS= read -e -p "Input .unf file name: " in_file 11 | in_file="${in_file% }" 12 | fi 13 | 14 | if (( $# )); then 15 | out_file="$1"; shift 16 | else 17 | IFS= read -e -p "Output .unf file name: " out_file 18 | out_file="${out_file% }" 19 | fi 20 | 21 | declare -a bson_jar=(/usr/lib/unifi/lib/bson-*([0-9.]).jar) 22 | if (( ${#bson_jar[@]} == 0 )); then 23 | bson_jar=(/usr/lib/unifi/lib/mongo-java-driver-*([0-9.]).jar) 24 | fi 25 | 26 | java -classpath "${BASH_SOURCE[0]%/*}/../lib/unifi/java:${bson_jar[0]}" \ 27 | PruneBackup "$in_file" "$out_file" 28 | -------------------------------------------------------------------------------- /unifi/prune-mongodb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) 3 | cd "$parent_path" 4 | /usr/bin/mongo --port 27117 < ../lib/unifi/mongodb/prune.js -------------------------------------------------------------------------------- /unifi/redirect.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Domain name to redirect 80, 443 to 8443 for: " 3 | read DOMAIN 4 | parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) 5 | cd "$parent_path" 6 | /usr/bin/python3 ../lib/unifi/py/redirect.py -d $DOMAIN -------------------------------------------------------------------------------- /unifi/reinstall-unifi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | systemctl stop unifi && rm -rf /var/lib/unifi/* && reboot 3 | -------------------------------------------------------------------------------- /unifi/remove-owner.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | mongo ace --eval 'db.setting.updateOne({key: "super_cloudaccess"}, {$set: {enabled: false}, $unset: {device_id: "", x_certificate_arn: "", x_certificate_pem: "", x_private_key: "", device_auth: "", ubic_uuid: ""}});' --port 27117 --quiet | jq -r '.modifiedCount | if . > 0 then "Owner removed" else "No owner exists" end' --------------------------------------------------------------------------------