acme: Bring up-to-date with master
authorToke Høiland-Jørgensen <toke@redhat.com>
Tue, 29 Oct 2019 08:44:47 +0000 (09:44 +0100)
committerToke Høiland-Jørgensen <toke@redhat.com>
Tue, 29 Oct 2019 08:48:37 +0000 (09:48 +0100)
There are quite a few bugfixes in the version of the ACME package in
master, and the old version in 18.06 have some issues as seen in #10328.
This commit ports over all changes from the master branch in one go.

Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
net/acme/Makefile
net/acme/files/acme-cbi.lua
net/acme/files/acme.config
net/acme/files/run.sh

index a6d618bed9facb2cc7aab8022ae9891493d7904b..fa70cdc78d972fc81b8d543a462c056be00a4635 100644 (file)
@@ -8,16 +8,17 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=acme
-PKG_VERSION:=2.7.8
-PKG_RELEASE:=4
-PKG_LICENSE:=GPLv3
-
-PKG_SOURCE_PROTO:=git
-PKG_SOURCE_URL:=https://github.com/Neilpang/acme.sh
-PKG_SOURCE_VERSION:=521d8c4b1f374c52ab1452d399a4d4910465e9fe
-PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_RELEASE).tar.xz
-PKG_MIRROR_HASH:=03e24eb41513b4d28dc42f5ae5c91be0030094149cbdbf9cdf9b6f87db9e36c0
+PKG_VERSION:=2.8.3
+PKG_RELEASE:=1
+
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
+PKG_SOURCE_URL:=https://codeload.github.com/Neilpang/acme.sh/tar.gz/$(PKG_VERSION)?
+PKG_HASH:=bdec71377a764919ac277e26d71ef7e24087f7f05171921888b70de6ab6e2cbc
+PKG_BUILD_DIR:=$(BUILD_DIR)/acme.sh-$(PKG_VERSION)
+
 PKG_MAINTAINER:=Toke Høiland-Jørgensen <toke@toke.dk>
+PKG_LICENSE:=GPL-3.0-only
+PKG_LICENSE_FILES:=LICENSE.md
 
 LUCI_DIR:=/usr/lib/lua/luci
 
@@ -26,8 +27,10 @@ include $(INCLUDE_DIR)/package.mk
 define Package/acme
   SECTION:=net
   CATEGORY:=Network
-  DEPENDS:=+curl +ca-bundle +openssl-util +netcat
+  DEPENDS:=+gnu-wget +ca-bundle +openssl-util +socat
   TITLE:=ACME (Letsencrypt) client
+  URL:=https://acme.sh
+  PKGARCH:=all
 endef
 
 define Package/acme/description
@@ -61,6 +64,7 @@ define Package/acme-dnsapi
   CATEGORY:=Network
   DEPENDS:=+acme
   TITLE:=DNS API integration for ACME (Letsencrypt) client
+  PKGARCH:=all
 endef
 
 define Package/acme-dnsapi/description
@@ -76,8 +80,9 @@ define Package/luci-app-acme
   SECTION:=luci
   CATEGORY:=LuCI
   TITLE:=ACME package - LuCI interface
-  DEPENDS:= lua luci-base +acme luci-app-uhttpd
+  DEPENDS:= lua luci-base +acme
   SUBMENU:=3. Applications
+  PKGARCH:=all
 endef
 
 define Package/luci-app-acme/description
index 264d335315f3f1a9f76e9ceb0c0ee7986d6617bf..5fc860e3218ca1848b1503b127ca9b7a5ac61124 100644 (file)
@@ -10,6 +10,11 @@ Copyright 2016 Toke Høiland-Jørgensen <toke@toke.dk>
 
 ]]--
 
+local fs = require "nixio.fs"
+
+local nginx_presence = fs.access("/usr/sbin/nginx") or false
+local uhttpd_presence = fs.access("/usr/sbin/uhttpd") or false
+
 m = Map("acme", translate("ACME certificates"),
        translate("This configures ACME (Letsencrypt) automatic certificate installation. " ..
                   "Simply fill out this to have the router configured with Letsencrypt-issued " ..
@@ -47,21 +52,38 @@ us = cs:option(Flag, "use_staging", translate("Use staging server"),
                          "(use for testing; the certificate won't be valid)."))
 us.rmempty = false
 
-kl = cs:option(Value, "keylength", translate("Key length"),
-               translate("Number of bits (minimum 2048)."))
+kl = cs:option(ListValue, "keylength", translate("Key size"),
+               translate("Key size (and type) for the generated certificate."))
+kl:value("2048", "RSA 2048 bits")
+kl:value("3072", "RSA 3072 bits")
+kl:value("4096", "RSA 4096 bits")
+kl:value("ec-256", "ECC 256 bits")
+kl:value("ec-384", "ECC 384 bits")
+kl.default = "2048"
 kl.rmempty = false
-kl.datatype = "and(uinteger,min(2048))"
 
+if uhttpd_presence then
 u = cs:option(Flag, "update_uhttpd", translate("Use for uhttpd"),
               translate("Update the uhttpd config with this certificate once issued " ..
-                        "(only select this for one certificate)."))
+                        "(only select this for one certificate)." ..
+                        "Is also available luci-app-uhttpd to configure uhttpd form the LuCI interface."))
+u.rmempty = false
+end
+
+if nginx_presence then
+u = cs:option(Flag, "update_nginx", translate("Use for nginx"),
+              translate("Update the nginx config with this certificate once issued " ..
+                        "(only select this for one certificate)." ..
+                        "Nginx must support ssl, if not it won't start as it needs to be " ..
+                        "compiled with ssl support to use cert options"))
 u.rmempty = false
+end
 
 wr = cs:option(Value, "webroot", translate("Webroot directory"),
                translate("Webserver root directory. Set this to the webserver " ..
                          "document root to run Acme in webroot mode. The web " ..
                          "server must be accessible from the internet on port 80."))
-wr.rmempty = false
+wr.optional = true
 
 dom = cs:option(DynamicList, "domains", translate("Domain names"),
                 translate("Domain names to include in the certificate. " ..
@@ -75,7 +97,7 @@ dns = cs:option(Value, "dns", translate("DNS API"),
                           "In DNS mode, the domain name does not have to resolve to the router IP. " ..
                           "DNS mode is also the only mode that supports wildcard certificates. " ..
                           "Using this mode requires the acme-dnsapi package to be installed."))
-dns.rmempty = false
+dns.optional = true
 
 cred = cs:option(DynamicList, "credentials", translate("DNS API credentials"),
                  translate("The credentials for the DNS API mode selected above. " ..
index af12ce1fb008e2f2f2f37e4dd1ae389569e7f3ab..95565c83281fbea0b037dd65206904d592e8f534 100644 (file)
@@ -8,5 +8,6 @@ config cert 'example'
        option use_staging 1
        option keylength 2048
        option update_uhttpd 1
+       option update_nginx 1
        option webroot ""
        list domains example.org
index 0d3d5c55e59e16ff4e403a2a5962306cd02fb7c3..3d25321d78debf18b518379af3e159dc88d8e6f5 100644 (file)
@@ -17,6 +17,9 @@ UHTTPD_LISTEN_HTTP=
 STATE_DIR='/etc/acme'
 ACCOUNT_EMAIL=
 DEBUG=0
+NGINX_WEBSERVER=0
+UPDATE_NGINX=0
+UPDATE_UHTTPD=0
 
 . /lib/functions.sh
 
@@ -42,9 +45,19 @@ debug()
     [ "$DEBUG" -eq "1" ] && logger -t acme -s -p daemon.debug "$@"
 }
 
-get_listeners()
+get_listeners() {
+    local proto rq sq listen remote state program
+    netstat -nptl 2>/dev/null | while read proto rq sq listen remote state program; do
+        case "$proto#$listen#$program" in
+            tcp#*:80#[0-9]*/*) echo -n "${program%% *} " ;;
+        esac
+    done
+}
+
+run_acme()
 {
-    netstat -nptl 2>/dev/null | awk 'match($4, /:80$/){split($7, parts, "/"); print parts[2];}' | uniq | tr "\n" " "
+    debug "Running acme.sh as '$ACME $@'"
+    $ACME "$@"
 }
 
 pre_checks()
@@ -54,37 +67,58 @@ pre_checks()
     log "Running pre checks for $main_domain."
 
     listeners="$(get_listeners)"
-    debug "port80 listens: $listeners"
-
-    case "$listeners" in
-        "uhttpd")
-            debug "Found uhttpd listening on port 80; trying to disable."
-
-            UHTTPD_LISTEN_HTTP=$(uci get uhttpd.main.listen_http)
 
-            if [ -z "$UHTTPD_LISTEN_HTTP" ]; then
-                err "$main_domain: Unable to find uhttpd listen config."
-                err "Manually disable uhttpd or set webroot to continue."
-                return 1
-            fi
+    debug "port80 listens: $listeners"
 
-            uci set uhttpd.main.listen_http=''
-            uci commit uhttpd || return 1
-            if ! /etc/init.d/uhttpd reload ; then
-                uci set uhttpd.main.listen_http="$UHTTPD_LISTEN_HTTP"
-                uci commit uhttpd
-                return 1
-            fi
+    for listener in $(get_listeners); do
+        pid="${listener%/*}"
+        cmd="${listener#*/}"
+
+        case "$cmd" in
+            uhttpd)
+                debug "Found uhttpd listening on port 80; trying to disable."
+
+                UHTTPD_LISTEN_HTTP=$(uci get uhttpd.main.listen_http)
+
+                if [ -z "$UHTTPD_LISTEN_HTTP" ]; then
+                    err "$main_domain: Unable to find uhttpd listen config."
+                    err "Manually disable uhttpd or set webroot to continue."
+                    return 1
+                fi
+
+                uci set uhttpd.main.listen_http=''
+                uci commit uhttpd || return 1
+                if ! /etc/init.d/uhttpd reload ; then
+                    uci set uhttpd.main.listen_http="$UHTTPD_LISTEN_HTTP"
+                    uci commit uhttpd
+                    return 1
+                fi
             ;;
-        "")
-            debug "Nothing listening on port 80."
+            nginx*)
+                debug "Found nginx listening on port 80; trying to disable."
+                NGINX_WEBSERVER=1
+                local tries=0
+                while grep -sq "$cmd" "/proc/$pid/cmdline" && kill -0 "$pid"; do
+                /etc/init.d/nginx stop
+                    if [ $tries -gt 10 ]; then
+                        debug "Can't stop nginx. Terminating script."
+                        return 1
+                    fi
+                    debug "Waiting for nginx to stop..."
+                    tries=$((tries + 1))
+                    sleep 1
+                done
             ;;
-        *)
-            err "$main_domain: Cannot run in standalone mode; another daemon is listening on port 80."
-            err "Disable other daemon or set webroot to continue."
-            return 1
+            "")
+                debug "Nothing listening on port 80."
             ;;
-    esac
+            *)
+                err "$main_domain: Cannot run in standalone mode; another daemon is listening on port 80."
+                err "Disable other daemon or set webroot to continue."
+                return 1
+            ;;
+        esac
+    done
 
     iptables -I input_rule -p tcp --dport 80 -j ACCEPT -m comment --comment "ACME" || return 1
     ip6tables -I input_rule -p tcp --dport 80 -j ACCEPT -m comment --comment "ACME" || return 1
@@ -101,11 +135,18 @@ post_checks()
     iptables -D input_rule -p tcp --dport 80 -j ACCEPT -m comment --comment "ACME" 2>/dev/null
     ip6tables -D input_rule -p tcp --dport 80 -j ACCEPT -m comment --comment "ACME" 2>/dev/null
 
-    if [ -e /etc/init.d/uhttpd ] && [ -n "$UHTTPD_LISTEN_HTTP" ]; then
-        uci set uhttpd.main.listen_http="$UHTTPD_LISTEN_HTTP"
+    if [ -e /etc/init.d/uhttpd ] && ( [ -n "$UHTTPD_LISTEN_HTTP" ] || [ "$UPDATE_UHTTPD" -eq 1 ] ); then
+        if [ -n "$UHTTPD_LISTEN_HTTP" ]; then
+            uci set uhttpd.main.listen_http="$UHTTPD_LISTEN_HTTP"
+            UHTTPD_LISTEN_HTTP=
+        fi
         uci commit uhttpd
         /etc/init.d/uhttpd reload
-        UHTTPD_LISTEN_HTTP=
+    fi
+
+    if [ -e /etc/init.d/nginx ] && ( [ "$NGINX_WEBSERVER" -eq 1 ] || [ "$UPDATE_NGINX" -eq 1 ] ); then
+        NGINX_WEBSERVER=0
+        /etc/init.d/nginx restart
     fi
 }
 
@@ -137,6 +178,7 @@ issue_cert()
     local enabled
     local use_staging
     local update_uhttpd
+    local update_nginx
     local keylength
     local domains
     local main_domain
@@ -144,15 +186,20 @@ issue_cert()
     local failed_dir
     local webroot
     local dns
+    local ret
 
     config_get_bool enabled "$section" enabled 0
     config_get_bool use_staging "$section" use_staging
     config_get_bool update_uhttpd "$section" update_uhttpd
+    config_get_bool update_nginx "$section" update_nginx
     config_get domains "$section" domains
     config_get keylength "$section" keylength
     config_get webroot "$section" webroot
     config_get dns "$section" dns
 
+    UPDATE_NGINX=$update_nginx
+    UPDATE_UHTTPD=$update_uhttpd
+
     [ "$enabled" -eq "1" ] || return
 
     [ "$DEBUG" -eq "1" ] && acme_args="$acme_args --debug"
@@ -163,7 +210,7 @@ issue_cert()
     [ -n "$webroot" ] || [ -n "$dns" ] || pre_checks "$main_domain" || return 1
 
     log "Running ACME for $main_domain"
-    
+
     handle_credentials() {
         local credential="$1"
         eval export $credential
@@ -177,8 +224,9 @@ issue_cert()
             moved_staging=1
         else
             log "Found previous cert config. Issuing renew."
-            $ACME --home "$STATE_DIR" --renew -d "$main_domain" $acme_args || return 1
-            return 0
+            run_acme --home "$STATE_DIR" --renew -d "$main_domain" $acme_args && ret=0 || ret=1
+            post_checks
+            return $ret
         fi
     fi
 
@@ -193,17 +241,18 @@ issue_cert()
         acme_args="$acme_args --dns $dns"
     elif [ -z "$webroot" ]; then
         log "Using standalone mode"
-        acme_args="$acme_args --standalone"
+        acme_args="$acme_args --standalone --listen-v6"
     else
         if [ ! -d "$webroot" ]; then
             err "$main_domain: Webroot dir '$webroot' does not exist!"
+            post_checks
             return 1
         fi
         log "Using webroot dir: $webroot"
         acme_args="$acme_args --webroot $webroot"
     fi
 
-    if ! $ACME --home "$STATE_DIR" --issue $acme_args; then
+    if ! run_acme --home "$STATE_DIR" --issue $acme_args; then
         failed_dir="$STATE_DIR/${main_domain}.failed-$(date +%s)"
         err "Issuing cert for $main_domain failed. Moving state to $failed_dir"
         [ -d "$STATE_DIR/$main_domain" ] && mv "$STATE_DIR/$main_domain" "$failed_dir"
@@ -211,15 +260,22 @@ issue_cert()
             err "Restoring staging certificate"
             mv "$STATE_DIR/${main_domain}.staging" "$STATE_DIR/${main_domain}"
         fi
+        post_checks
         return 1
     fi
 
-    if [ "$update_uhttpd" -eq "1" ]; then
+    if [ -e /etc/init.d/uhttpd ] && [ "$update_uhttpd" -eq "1" ]; then
         uci set uhttpd.main.key="$STATE_DIR/${main_domain}/${main_domain}.key"
         uci set uhttpd.main.cert="$STATE_DIR/${main_domain}/fullchain.cer"
         # commit and reload is in post_checks
     fi
 
+    if [ -e /etc/init.d/nginx ] && [ "$update_nginx" -eq "1" ]; then
+        sed -i "s#ssl_certificate\ .*#ssl_certificate $STATE_DIR/${main_domain}/fullchain.cer;#g" /etc/nginx/nginx.conf
+        sed -i "s#ssl_certificate_key\ .*#ssl_certificate_key $STATE_DIR/${main_domain}/${main_domain}.key;#g" /etc/nginx/nginx.conf
+        # commit and reload is in post_checks
+    fi
+
     post_checks
 }