Skip to content

Instantly share code, notes, and snippets.

@hisnameisjimmy
Last active March 14, 2022 22:14
Show Gist options
  • Save hisnameisjimmy/56f9414076ca39a79bfa07eefa89759e to your computer and use it in GitHub Desktop.
Save hisnameisjimmy/56f9414076ca39a79bfa07eefa89759e to your computer and use it in GitHub Desktop.
Unifi Controller one-shot install script for Ubuntu 16.04 with Lets Encrypt
#!/bin/sh
#
# This script stands on the shoulders of giants.
#
# You can always find the most recent version here: https://gist.github.com/hisnameisjimmy/56f9414076ca39a79bfa07eefa89759e
#
# It is written and tested for Ubuntu 16.04 on Digital Ocean using a 1GB droplet.
# Anything less than 1GB of memory may cause issues with anything memory intensive
# like imports/exports.
#
# It does the following:
# 1) Opens the appropriate ports for Unifi, SSH, Web/SSL traffic via iptables
# 2) Makes the Unifi/Certbot software available as a package
# 3) Installs haveged to prevent entropy (see jeff-ferguson.com reference below)
# 4) Installs fail2ban as a basic security measure
# 5) Asks to install unattended security upgrades for long-term security
# 6) Uses Certbot to request a Lets Encrypt Certificate, and then installs it
# 7) Writes an NGINX proxy config
# 8) Writes out an automatic renewal cron for Lets Encrypt (as the certs expire every 3 months)
#
# I recommend running it from /opt on your server. In my installation I called it 'le-install.sh'
# Run it with the following:
# bash /opt/le-install.sh
#
# Alternatively you can make it executable and run it without specifying bash, but this is a one
# time script, so it seems unnecessary.
#
# Thanks to these resources below:
# https://community.ubnt.com/t5/UniFi-Wireless/Ubuntu-single-script-LetsEncrypt-Nginx-Proxy-UniFi-5-Repo/m-p/1626526/highlight/false#M172872
# http://www.jeff-ferguson.com/2016/11/21/unifi-5-2-9-installation-script-for-digital-ocean/
# https://murfy.nz/2017/01/ubiquiti-unifi-secure-installation/
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
# OR OTHER DEALINGS IN THE SOFTWARE.
#
RED='\033[0;31m'
CYAN='\033[0;36m'
NC='\033[0m'
# Gathering variables to use for the rest of the script
echo -en "${CYAN}Enter your domain name [my.fqdn.com]: ${NC}"
read NAME
echo -en "${CYAN}Enter your email address [[email protected]]: ${NC}"
read EMAIL
echo "These parameters are used exclusively by LetsEncrypt to register your SSL certificate and provide notifications:"
echo "Domain: $NAME"
echo "E-Mail: $EMAIL"
read -p "$(echo -e ${CYAN}"Does this look OK? [Y/N]: "${NC})" -n 1 REPLY
echo # (optional) move to a new line
if [[ ! $REPLY =~ ^[Yy]$ ]]
then
echo -e "${RED}Please re-run $0 and re-enter the params.${NC}"
exit 1
fi
# Installing UNIFI software
echo -e "${CYAN}Installing Unifi${NC}"
echo 'deb http://www.ubnt.com/downloads/unifi/debian stable ubiquiti' | sudo tee /etc/apt/sources.list.d/100-ubnt-unifi.list
echo y | apt-key adv --keyserver keyserver.ubuntu.com --recv 06E85760C0A52C50
echo y | apt-get update
echo y | apt-get install unifi
# iptables config
echo "${CYAN}Opening relevant ports via iptables${NC}"
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -I INPUT 1 -i lo -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -p udp --dport 3478 -j ACCEPT
iptables -A INPUT -p tcp --dport 6787 -j ACCEPT
iptables -A INPUT -p tcp --dport 8081 -j ACCEPT
iptables -A INPUT -p tcp --dport 8080 -j ACCEPT
iptables -A INPUT -p tcp --dport 8443 -j ACCEPT
iptables -A INPUT -p tcp --dport 8880 -j ACCEPT
iptables -A INPUT -p tcp --dport 8843 -j ACCEPT
iptables -A INPUT -p tcp --dport 27117 -j ACCEPT
iptables -A INPUT -j DROP
# Install relevant packages
echo -e "${CYAN}Updating and installing relevant packages${NC}"
echo y | apt-get upgrade
apt-get -f install
echo y | apt-get install software-properties-common
echo y | add-apt-repository ppa:certbot/certbot
apt-get update
echo y | apt-get install nginx certbot haveged iptables-persistent fail2ban unattended-upgrades openjdk-8-jre-headless
# Install unattended upgrades (much better long-term security)
read -p "$(echo -e ${CYAN}"Enable unattended upgrades for this server (with auto-reboots)? [Y/N]: "${NC})" -n 1 REPLY
echo # (optional) move to a new line
if [[ ! $REPLY =~ ^[Yy]$ ]]
then
echo -e "${RED}No security updates will be auto-installed${NC}"
else
dpkg-reconfigure --priority=low unattended-upgrades
sed -i.bak 's#// Unattended-Upgrade::Automatic-Reboot "false";#Unattended-Upgrade::Automatic-Reboot "true";#' /etc/apt/apt.conf.d/50unattended-upgrades
fi
# Lets Encrypt certificate request, run it non-interactively (-n) so we don't have to agree to anything
echo -e "${CYAN}Requesting Certificate for $NAME${NC}"
service nginx stop
certbot -n certonly -d $NAME --standalone --agree-tos --preferred-challenges http-01 --email $EMAIL
service nginx start
echo -e "${CYAN}Adding certificate to UniFi Controller for $NAME${NC}"
service unifi stop
echo aircontrolenterprise | openssl pkcs12 -export -inkey /etc/letsencrypt/live/$NAME/privkey.pem -in /etc/letsencrypt/live/$NAME/cert.pem -name unifi -out /etc/letsencrypt/live/$NAME/keys.p12 -password stdin
echo y | keytool -importkeystore -srckeystore /etc/letsencrypt/live/$NAME/keys.p12 -srcstoretype pkcs12 -destkeystore /usr/lib/unifi/data/keystore -storepass aircontrolenterprise -srcstorepass aircontrolenterprise
service unifi start
openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096
# NGINX Proxy
echo -e "${CYAN}Writing nginx proxy configuration${NC}"
service nginx stop
printf "server_tokens off;\n\
add_header X-Frame-Options SAMEORIGIN;\n\
add_header X-XSS-Protection \"1; mode=block\";\n\
server {\n\
listen 80;\n\
server_name $NAME;\n\
return 301 https://$NAME\$request_uri;\n\
}\n\
server {\n\
listen 443 ssl default_server http2;\n\
server_name $NAME;\n\
ssl_dhparam /etc/ssl/certs/dhparam.pem;\n\
ssl_certificate /etc/letsencrypt/live/$NAME/fullchain.pem;\n\
ssl_certificate_key /etc/letsencrypt/live/$NAME/privkey.pem;\n\
ssl_session_cache shared:SSL:10m;\n\
ssl_session_timeout 10m;\n\
keepalive_timeout 300;\n\
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;\n\
ssl_prefer_server_ciphers on;\n\
ssl_stapling on;\n\
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA;
add_header Strict-Transport-Security max-age=31536000;\n\
add_header X-Frame-Options DENY;\n\
error_log /var/log/unifi/nginx.log;\n\
proxy_cache off;\n\
proxy_store off;\n\
location / {\n\
proxy_set_header Referer \"\";\n\
proxy_pass https://localhost:8443;\n\
proxy_set_header Host \$host;\n\
proxy_set_header X-Real-IP \$remote_addr;\n\
proxy_set_header X-Forward-For \$proxy_add_x_forwarded_for;\n\
proxy_http_version 1.1;\n\
proxy_set_header Upgrade \$http_upgrade;\n\
proxy_set_header Connection \"upgrade\";\n\
}\n\
}\n\
" > /etc/nginx/sites-enabled/default
service nginx start
# Automatic LE Certificate renewals - This creates a crontab for you
echo -e "${CYAN}Writing Crontab for LetsEncrypt renewals to /etc/cron.monthly/le-unifi-renew${NC}"
echo -e "#!/bin/sh\n\
service nginx stop\n\
echo y | certbot renew --standalone --preferred-challenges http-01\n\
service nginx start\n\
service unifi stop\n\
echo aircontrolenterprise | openssl pkcs12 -export -inkey /etc/letsencrypt/live/$NAME/privkey.pem -in /etc/letsencrypt/live/$NAME/cert.pem -name unifi -out /etc/letsencrypt/live/$NAME/keys.p12 -password stdin\n\
echo y | keytool -importkeystore -srckeystore /etc/letsencrypt/live/$NAME/keys.p12 -srcstoretype pkcs12 -destkeystore /usr/lib/unifi/data/keystore -storepass aircontrolenterprise -srcstorepass aircontrolenterprise\n\
service unifi start\n\
" > /etc/cron.monthly/le-unifi-renew
chmod +x /etc/cron.monthly/le-unifi-renew
echo -e "${CYAN}\n\n\n\nINSTALLATION COMPLETE! \nYou may see a bad gateway error on https://$NAME/\nWhile the controller performs its first-time initialization\n${NC}"
echo -e "${CYAN}If the bad gateway persists for longer than a couple minutes, try restarting the unifi controller from the commandline${NC}"
@Tricom114
Copy link

Works great but when I try to connect my Android device to the guest portal using the secure portal and redirect using the hostname, I get the error that "The network you're trying to join has security issues." This is only happening on Android. Windows PC an Laptop works fine.

@javielico
Copy link

javielico commented May 14, 2018

Hi there, the script works great but I can't load the site just keeps getting 502 Bad Gateway even after restarting unifi service. Just to add that I'm running Ubuntu 16.04 with 256MB of RAM.

@gouthamravee
Copy link

@javidotpro 256mb is way too low for the cloud controller. You need at least 1GB, you can get away with 512MB but 1GB is recommended.

@hpogosyan
Copy link

Getting an invalid certificate error in Chrome/Safari:
https://www.dropbox.com/s/9w2q8xmoekhj201/Screenshot%202018-08-01%2017.21.14.png?dl=0

Looks like it's still using the self signed cert.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment