Partial fixes in #3359 Comment #1817258702 and others - Unifi removed keytool from OS, which breaks the ability to deploy certificates used by the console. Deciding how to fix is up to the sysadmin, inserted some advice when it fails. Comment #2061486940 - Automatically set the cipher suites in system configuration.
276 lines
12 KiB
Bash
276 lines
12 KiB
Bash
#!/usr/bin/env sh
|
|
|
|
# Here is a script to deploy cert on a Unifi Controller or Cloud Key device.
|
|
# It supports:
|
|
# - self-hosted Unifi Controller
|
|
# - Unifi Cloud Key (Gen1/2/2+)
|
|
# - Unifi Cloud Key running UnifiOS (v2.0.0+, Gen2/2+ only)
|
|
# See below regarding keytool. Not tested.
|
|
# - Unifi Dream Machine
|
|
# This has not been tested on other "all-in-one" devices such as
|
|
# UDM Pro or Unifi Express.
|
|
#
|
|
# OS Version v2.0.0+
|
|
# Network Application version 7.0.0+
|
|
# OS version ~3.1 removed java and keytool from the UnifiOS. These must be
|
|
# manually installed by the system owner at their own risk, as
|
|
# doing so may invalidate official Ubiquiti support/warranty.
|
|
# `apt update && apt install openjdk-11-jre-headless -y`
|
|
#
|
|
# Please report bugs to https://github.com/acmesh-official/acme.sh/issues/3359
|
|
|
|
#returns 0 means success, otherwise error.
|
|
|
|
# The deploy-hook automatically detects standard Unifi installations
|
|
# for each of the supported environments. Most users should not need
|
|
# to set any of these variables, but if you are running a self-hosted
|
|
# Controller with custom locations, set these as necessary before running
|
|
# the deploy hook. (Defaults shown below.)
|
|
#
|
|
# Settings for Unifi Controller:
|
|
# Location of Java keystore or unifi.keystore.jks file:
|
|
#DEPLOY_UNIFI_KEYSTORE="/usr/lib/unifi/data/keystore"
|
|
# Keystore password (built into Unifi Controller, not a user-set password):
|
|
#DEPLOY_UNIFI_KEYPASS="aircontrolenterprise"
|
|
# Command to restart Unifi Controller:
|
|
#DEPLOY_UNIFI_RELOAD="service unifi restart"
|
|
#
|
|
# Settings for Unifi Cloud Key Gen1 (nginx admin pages):
|
|
# Directory where cloudkey.crt and cloudkey.key live:
|
|
#DEPLOY_UNIFI_CLOUDKEY_CERTDIR="/etc/ssl/private"
|
|
# Command to restart maintenance pages and Controller
|
|
# (same setting as above, default is updated when running on Cloud Key Gen1):
|
|
#DEPLOY_UNIFI_RELOAD="service nginx restart && service unifi restart"
|
|
#
|
|
# Settings for UnifiOS (Cloud Key Gen2):
|
|
# Directory where unifi-core.crt and unifi-core.key live:
|
|
#DEPLOY_UNIFI_CORE_CONFIG="/data/unifi-core/config/"
|
|
# Command to restart unifi-core:
|
|
#DEPLOY_UNIFI_RELOAD="systemctl restart unifi-core"
|
|
#
|
|
# At least one of DEPLOY_UNIFI_KEYSTORE, DEPLOY_UNIFI_CLOUDKEY_CERTDIR,
|
|
# or DEPLOY_UNIFI_CORE_CONFIG must exist to receive the deployed certs.
|
|
|
|
######## Public functions #####################
|
|
|
|
#domain keyfile certfile cafile fullchain
|
|
unifi_deploy() {
|
|
_cdomain="$1"
|
|
_ckey="$2"
|
|
_ccert="$3"
|
|
_cca="$4"
|
|
_cfullchain="$5"
|
|
|
|
_debug _cdomain "$_cdomain"
|
|
_debug _ckey "$_ckey"
|
|
_debug _ccert "$_ccert"
|
|
_debug _cca "$_cca"
|
|
_debug _cfullchain "$_cfullchain"
|
|
|
|
_getdeployconf DEPLOY_UNIFI_KEYSTORE
|
|
_getdeployconf DEPLOY_UNIFI_KEYPASS
|
|
_getdeployconf DEPLOY_UNIFI_CLOUDKEY_CERTDIR
|
|
_getdeployconf DEPLOY_UNIFI_CORE_CONFIG
|
|
_getdeployconf DEPLOY_UNIFI_RELOAD
|
|
|
|
_debug2 DEPLOY_UNIFI_KEYSTORE "$DEPLOY_UNIFI_KEYSTORE"
|
|
_debug2 DEPLOY_UNIFI_KEYPASS "$DEPLOY_UNIFI_KEYPASS"
|
|
_debug2 DEPLOY_UNIFI_CLOUDKEY_CERTDIR "$DEPLOY_UNIFI_CLOUDKEY_CERTDIR"
|
|
_debug2 DEPLOY_UNIFI_CORE_CONFIG "$DEPLOY_UNIFI_CORE_CONFIG"
|
|
_debug2 DEPLOY_UNIFI_RELOAD "$DEPLOY_UNIFI_RELOAD"
|
|
|
|
# Space-separated list of environments detected and installed:
|
|
_services_updated=""
|
|
|
|
# Default reload commands accumulated as we auto-detect environments:
|
|
_reload_cmd=""
|
|
|
|
# Unifi Controller environment (self hosted or any Cloud Key) --
|
|
# auto-detect by file /usr/lib/unifi/data/keystore
|
|
# Additional detection of OS version to handle missing keytool.
|
|
_unifi_keystore="${DEPLOY_UNIFI_KEYSTORE:-/usr/lib/unifi/data/keystore}"
|
|
if [ -f "$_unifi_keystore" ]; then
|
|
_debug _unifi_keystore "$_unifi_keystore"
|
|
if ! _exists keytool; then
|
|
_err "keytool not found."
|
|
_keytool_instructions=$(cat <<-EOF
|
|
Unifi removed keytool from the console OS after version 3.
|
|
You may be able to reinstall with
|
|
"apt update && apt install openjdk-11-jre-headless -y"
|
|
THIS MAY AFFECT UBIQUITI SUPPORT, DO SO AT YOUR OWN RISK.
|
|
It's also possible that apt will not work in future versions.
|
|
|
|
If you don't want to (or can't use apt), then it's recommended
|
|
to deploy from a remote host via SSH. See
|
|
https://github.com/acmesh-official/acme.sh/wiki/deployhooks#examples-using-ssh-deploy
|
|
|
|
EOF
|
|
)
|
|
_info "$_keytool_instructions"
|
|
return 1
|
|
else
|
|
_info "Installing certificate for Unifi Controller (Java keystore)"
|
|
fi
|
|
if [ ! -w "$_unifi_keystore" ]; then
|
|
_err "The file $_unifi_keystore is not writable, please change the permission."
|
|
return 1
|
|
fi
|
|
|
|
_unifi_keypass="${DEPLOY_UNIFI_KEYPASS:-aircontrolenterprise}"
|
|
|
|
_debug "Generate import pkcs12"
|
|
_import_pkcs12="$(_mktemp)"
|
|
_toPkcs "$_import_pkcs12" "$_ckey" "$_ccert" "$_cca" "$_unifi_keypass" unifi root
|
|
# shellcheck disable=SC2181
|
|
if [ "$?" != "0" ]; then
|
|
_err "Error generating pkcs12. Please re-run with --debug and report a bug."
|
|
return 1
|
|
fi
|
|
|
|
# Save the existing keystore in case something goes wrong.
|
|
cp -f "${_unifi_keystore}" "${_unifi_keystore}"_original
|
|
_info "Previous certificate and key saved to ${_unifi_keystore}_original."
|
|
|
|
# May not be necessary, but seems more reliable
|
|
_debug "Remove existing key from keystore: $_unifi_keystore"
|
|
if keytool -delete -alias unifi -keystore "$_unifi_keystore" \
|
|
-deststorepass "$_unifi_keypass"; then
|
|
_debug "Successfully removed old certificate from keystore."
|
|
fi
|
|
|
|
_debug "Import into keystore: $_unifi_keystore"
|
|
if keytool -importkeystore \
|
|
-deststorepass "$_unifi_keypass" -destkeypass "$_unifi_keypass" -destkeystore "$_unifi_keystore" \
|
|
-srckeystore "$_import_pkcs12" -srcstoretype PKCS12 -srcstorepass "$_unifi_keypass" \
|
|
-alias unifi -noprompt; then
|
|
_debug "Import keystore success!"
|
|
else
|
|
_err "Error importing into Unifi Java keystore."
|
|
_err "Please re-run with --debug and report a bug."
|
|
rm "$_import_pkcs12"
|
|
return 1
|
|
fi
|
|
|
|
# Update unifi service for certificate cipher compatibility
|
|
if ${ACME_OPENSSL_BIN:-openssl} pkcs12 \
|
|
-in "$_import_pkcs12" \
|
|
-password pass:aircontrolenterprise \
|
|
-nokeys | ${ACME_OPENSSL_BIN:-openssl} x509 -text \
|
|
-noout | grep -i "signature" | grep -iq ecdsa > /dev/null 2>&1; then
|
|
cp -f /usr/lib/unifi/data/system.properties /usr/lib/unifi/data/system.properties_original
|
|
_info "Updating system configuration for cipher compatibility."
|
|
_info "Saved original system config to /usr/lib/unifi/data/system.properties_original"
|
|
sed -i '/unifi\.https\.ciphers/d' /usr/lib/unifi/data/system.properties
|
|
echo "unifi.https.ciphers=ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES128-GCM-SHA256" &>> /usr/lib/unifi/data/system.properties
|
|
sed -i '/unifi\.https\.sslEnabledProtocols/d' /usr/lib/unifi/data/system.properties
|
|
echo "unifi.https.sslEnabledProtocols=TLSv1.3,TLSv1.2" &>> /usr/lib/unifi/data/system.properties
|
|
_info "System configuration updated."
|
|
fi
|
|
|
|
rm "$_import_pkcs12"
|
|
|
|
if systemctl -q is-active unifi; then
|
|
_reload_cmd="${_reload_cmd:+$_reload_cmd && }service unifi restart"
|
|
fi
|
|
_services_updated="${_services_updated} unifi"
|
|
_info "Install Unifi Controller certificate success!"
|
|
elif [ "$DEPLOY_UNIFI_KEYSTORE" ]; then
|
|
_err "The specified DEPLOY_UNIFI_KEYSTORE='$DEPLOY_UNIFI_KEYSTORE' is not valid, please check."
|
|
return 1
|
|
fi
|
|
|
|
# Cloud Key environment (non-UnifiOS -- nginx serves admin pages) --
|
|
# auto-detect by file /etc/ssl/private/cloudkey.key:
|
|
_cloudkey_certdir="${DEPLOY_UNIFI_CLOUDKEY_CERTDIR:-/etc/ssl/private}"
|
|
if [ -f "${_cloudkey_certdir}/cloudkey.key" ]; then
|
|
_info "Installing certificate for Cloud Key Gen1 (nginx admin pages)"
|
|
_debug _cloudkey_certdir "$_cloudkey_certdir"
|
|
if [ ! -w "$_cloudkey_certdir" ]; then
|
|
_err "The directory $_cloudkey_certdir is not writable; please check permissions."
|
|
return 1
|
|
fi
|
|
# Cloud Key expects to load the keystore from /etc/ssl/private/unifi.keystore.jks.
|
|
# Normally /usr/lib/unifi/data/keystore is a symlink there (so the keystore was
|
|
# updated above), but if not, we don't know how to handle this installation:
|
|
if ! cmp -s "$_unifi_keystore" "${_cloudkey_certdir}/unifi.keystore.jks"; then
|
|
_err "Unsupported Cloud Key configuration: keystore not found at '${_cloudkey_certdir}/unifi.keystore.jks'"
|
|
return 1
|
|
fi
|
|
|
|
cat "$_cfullchain" >"${_cloudkey_certdir}/cloudkey.crt"
|
|
cat "$_ckey" >"${_cloudkey_certdir}/cloudkey.key"
|
|
(cd "$_cloudkey_certdir" && tar -cf cert.tar cloudkey.crt cloudkey.key unifi.keystore.jks)
|
|
|
|
if systemctl -q is-active nginx; then
|
|
_reload_cmd="${_reload_cmd:+$_reload_cmd && }service nginx restart"
|
|
fi
|
|
_info "Install Cloud Key Gen1 certificate success!"
|
|
_services_updated="${_services_updated} nginx"
|
|
elif [ "$DEPLOY_UNIFI_CLOUDKEY_CERTDIR" ]; then
|
|
_err "The specified DEPLOY_UNIFI_CLOUDKEY_CERTDIR='$DEPLOY_UNIFI_CLOUDKEY_CERTDIR' is not valid, please check."
|
|
return 1
|
|
fi
|
|
|
|
# UnifiOS environment -- auto-detect by /data/unifi-core/config/unifi-core.key:
|
|
_unifi_core_config="${DEPLOY_UNIFI_CORE_CONFIG:-/data/unifi-core/config}"
|
|
if [ -f "${_unifi_core_config}/unifi-core.key" ]; then
|
|
_info "Installing certificate for UnifiOS"
|
|
_debug _unifi_core_config "$_unifi_core_config"
|
|
if [ ! -w "$_unifi_core_config" ]; then
|
|
_err "The directory $_unifi_core_config is not writable; please check permissions."
|
|
return 1
|
|
fi
|
|
|
|
# Save the existing certs in case something goes wrong.
|
|
cp -f "${_unifi_core_config}"/unifi-core.crt "${_unifi_core_config}"/unifi-core_original.crt
|
|
cp -f "${_unifi_core_config}"/unifi-core.key "${_unifi_core_config}"/unifi-core_original.key
|
|
_info "Previous certificate and key saved to ${_unifi_core_config}/unifi-core_original.crt/key."
|
|
|
|
cat "$_cfullchain" >"${_unifi_core_config}/unifi-core.crt"
|
|
cat "$_ckey" >"${_unifi_core_config}/unifi-core.key"
|
|
|
|
if systemctl -q is-active unifi-core; then
|
|
_reload_cmd="${_reload_cmd:+$_reload_cmd && }systemctl restart unifi-core"
|
|
fi
|
|
_info "Install UnifiOS certificate success!"
|
|
_services_updated="${_services_updated} unifi-core"
|
|
elif [ "$DEPLOY_UNIFI_CORE_CONFIG" ]; then
|
|
_err "The specified DEPLOY_UNIFI_CORE_CONFIG='$DEPLOY_UNIFI_CORE_CONFIG' is not valid, please check."
|
|
return 1
|
|
fi
|
|
|
|
if [ -z "$_services_updated" ]; then
|
|
# None of the Unifi environments were auto-detected, so no deployment has occurred
|
|
# (and none of DEPLOY_UNIFI_{KEYSTORE,CLOUDKEY_CERTDIR,CORE_CONFIG} were set).
|
|
_err "Unable to detect Unifi environment in standard location."
|
|
_err "(This deploy hook must be run on the Unifi device, not a remote machine.)"
|
|
_err "For non-standard Unifi installations, set DEPLOY_UNIFI_KEYSTORE,"
|
|
_err "DEPLOY_UNIFI_CLOUDKEY_CERTDIR, and/or DEPLOY_UNIFI_CORE_CONFIG as appropriate."
|
|
return 1
|
|
fi
|
|
|
|
_reload_cmd="${DEPLOY_UNIFI_RELOAD:-$_reload_cmd}"
|
|
if [ -z "$_reload_cmd" ]; then
|
|
_err "Certificates were installed for services:${_services_updated},"
|
|
_err "but none appear to be active. Please set DEPLOY_UNIFI_RELOAD"
|
|
_err "to a command that will restart the necessary services."
|
|
return 1
|
|
fi
|
|
_info "Reload services (this may take some time): $_reload_cmd"
|
|
if eval "$_reload_cmd"; then
|
|
_info "Reload success!"
|
|
else
|
|
_err "Reload error"
|
|
return 1
|
|
fi
|
|
|
|
# Successful, so save all (non-default) config:
|
|
_savedeployconf DEPLOY_UNIFI_KEYSTORE "$DEPLOY_UNIFI_KEYSTORE"
|
|
_savedeployconf DEPLOY_UNIFI_KEYPASS "$DEPLOY_UNIFI_KEYPASS"
|
|
_savedeployconf DEPLOY_UNIFI_CLOUDKEY_CERTDIR "$DEPLOY_UNIFI_CLOUDKEY_CERTDIR"
|
|
_savedeployconf DEPLOY_UNIFI_CORE_CONFIG "$DEPLOY_UNIFI_CORE_CONFIG"
|
|
_savedeployconf DEPLOY_UNIFI_RELOAD "$DEPLOY_UNIFI_RELOAD"
|
|
|
|
return 0
|
|
}
|