aboutsummaryrefslogtreecommitdiff
path: root/net/acme
diff options
context:
space:
mode:
Diffstat (limited to 'net/acme')
-rw-r--r--net/acme/Makefile3
-rw-r--r--net/acme/files/acme-cbi.lua9
-rw-r--r--net/acme/files/acme.config3
-rw-r--r--net/acme/files/run.sh125
4 files changed, 105 insertions, 35 deletions
diff --git a/net/acme/Makefile b/net/acme/Makefile
index a7f066450..88148545b 100644
--- a/net/acme/Makefile
+++ b/net/acme/Makefile
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=acme
PKG_SOURCE_VERSION:=7b40cbe8c1a52041351524bcde4b37665a7cdf79
-PKG_VERSION:=1.5
+PKG_VERSION:=1.6
PKG_RELEASE:=1
PKG_LICENSE:=GPLv3
@@ -47,6 +47,7 @@ define Build/Compile
endef
define Package/acme/install
+ $(INSTALL_DIR) $(1)/etc/acme
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_CONF) ./files/acme.config $(1)/etc/config/acme
$(INSTALL_DIR) $(1)/etc/init.d
diff --git a/net/acme/files/acme-cbi.lua b/net/acme/files/acme-cbi.lua
index a4f7956de..c20cba203 100644
--- a/net/acme/files/acme-cbi.lua
+++ b/net/acme/files/acme-cbi.lua
@@ -25,11 +25,12 @@ s.anonymous = true
st = s:option(Value, "state_dir", translate("State directory"),
translate("Where certs and other state files are kept."))
st.rmempty = false
-st.datatype = "string"
+st.datatype = "directory"
ae = s:option(Value, "account_email", translate("Account email"),
translate("Email address to associate with account key."))
ae.rmempty = false
+ae.datatype = "minlength(1)"
d = s:option(Flag, "debug", translate("Enable debug logging"))
d.rmempty = false
@@ -56,6 +57,12 @@ u = cs:option(Flag, "update_uhttpd", translate("Use for uhttpd"),
"(only select this for one certificate)."))
u.rmempty = false
+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
+
dom = cs:option(DynamicList, "domains", translate("Domain names"),
translate("Domain names to include in the certificate. " ..
"The first name will be the subject name, subsequent names will be alt names. " ..
diff --git a/net/acme/files/acme.config b/net/acme/files/acme.config
index c5cd7d3ea..af12ce1fb 100644
--- a/net/acme/files/acme.config
+++ b/net/acme/files/acme.config
@@ -5,7 +5,8 @@ config acme
config cert 'example'
option enabled 0
- option use_staging 0
+ option use_staging 1
option keylength 2048
option update_uhttpd 1
+ option webroot ""
list domains example.org
diff --git a/net/acme/files/run.sh b/net/acme/files/run.sh
index 0a4cad1c5..6bedaca16 100644
--- a/net/acme/files/run.sh
+++ b/net/acme/files/run.sh
@@ -27,45 +27,85 @@ check_cron()
/etc/init.d/cron start
}
-debug()
+log()
{
- [ "$DEBUG" -eq "1" ] && echo "$@" >&2
+ logger -t acme -s -p daemon.info "$@"
}
-pre_checks()
+err()
{
- echo "Running pre checks."
- check_cron
-
- [ -d "$STATE_DIR" ] || mkdir -p "$STATE_DIR"
-
- if [ -e /etc/init.d/uhttpd ]; then
+ logger -t acme -s -p daemon.err "$@"
+}
- UHTTPD_LISTEN_HTTP=$(uci get uhttpd.main.listen_http)
+debug()
+{
+ [ "$DEBUG" -eq "1" ] && logger -t acme -s -p daemon.debug "$@"
+}
- uci set uhttpd.main.listen_http=''
- uci commit uhttpd
- /etc/init.d/uhttpd reload || return 1
- fi
+get_listeners()
+{
+ netstat -nptl 2>/dev/null | awk 'match($4, /:80$/){split($7, parts, "/"); print parts[2];}' | uniq | tr "\n" " "
+}
- iptables -I input_rule -p tcp --dport 80 -j ACCEPT || return 1
- ip6tables -I input_rule -p tcp --dport 80 -j ACCEPT || return 1
+pre_checks()
+{
+ main_domain="$1"
+
+ 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
+
+ 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."
+ ;;
+ *)
+ 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
+
+ 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
debug "v4 input_rule: $(iptables -nvL input_rule)"
debug "v6 input_rule: $(ip6tables -nvL input_rule)"
- debug "port80 listens: $(netstat -ntpl | grep :80)"
return 0
}
post_checks()
{
- echo "Running post checks (cleanup)."
- iptables -D input_rule -p tcp --dport 80 -j ACCEPT
- ip6tables -D input_rule -p tcp --dport 80 -j ACCEPT
+ log "Running post checks (cleanup)."
+ # The comment ensures we only touch our own rules. If no rules exist, that
+ # is fine, so hide any errors
+ 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 ]; then
+ if [ -e /etc/init.d/uhttpd ] && [ -n "$UHTTPD_LISTEN_HTTP" ]; then
uci set uhttpd.main.listen_http="$UHTTPD_LISTEN_HTTP"
uci commit uhttpd
/etc/init.d/uhttpd reload
+ UHTTPD_LISTEN_HTTP=
fi
}
@@ -102,12 +142,14 @@ issue_cert()
local main_domain
local moved_staging=0
local failed_dir
+ local webroot
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 domains "$section" domains
config_get keylength "$section" keylength
+ config_get webroot "$section" webroot
[ "$enabled" -eq "1" ] || return
@@ -116,13 +158,17 @@ issue_cert()
set -- $domains
main_domain=$1
+ [ -n "$webroot" ] || pre_checks "$main_domain" || return 1
+
+ log "Running ACME for $main_domain"
+
if [ -e "$STATE_DIR/$main_domain" ]; then
if [ "$use_staging" -eq "0" ] && is_staging "$main_domain"; then
- echo "Found previous cert issued using staging server. Moving it out of the way."
+ log "Found previous cert issued using staging server. Moving it out of the way."
mv "$STATE_DIR/$main_domain" "$STATE_DIR/$main_domain.staging"
moved_staging=1
else
- echo "Found previous cert config. Issuing renew."
+ log "Found previous cert config. Issuing renew."
$ACME --home "$STATE_DIR" --renew -d "$main_domain" $acme_args || return 1
return 0
fi
@@ -130,17 +176,28 @@ issue_cert()
acme_args="$acme_args $(for d in $domains; do echo -n "-d $d "; done)"
- acme_args="$acme_args --standalone"
acme_args="$acme_args --keylength $keylength"
[ -n "$ACCOUNT_EMAIL" ] && acme_args="$acme_args --accountemail $ACCOUNT_EMAIL"
[ "$use_staging" -eq "1" ] && acme_args="$acme_args --staging"
+ if [ -z "$webroot" ]; then
+ log "Using standalone mode"
+ acme_args="$acme_args --standalone"
+ else
+ if [ ! -d "$webroot" ]; then
+ err "$main_domain: Webroot dir '$webroot' does not exist!"
+ return 1
+ fi
+ log "Using webroot dir: $webroot"
+ acme_args="$acme_args --webroot \"$webroot\""
+ fi
+
if ! $ACME --home "$STATE_DIR" --issue $acme_args; then
failed_dir="$STATE_DIR/${main_domain}.failed-$(date +%s)"
- echo "Issuing cert for $main_domain failed. Moving state to $failed_dir" >&2
+ err "Issuing cert for $main_domain failed. Moving state to $failed_dir"
[ -d "$STATE_DIR/$main_domain" ] && mv "$STATE_DIR/$main_domain" "$failed_dir"
if [ "$moved_staging" -eq "1" ]; then
- echo "Restoring staging certificate" >&2
+ err "Restoring staging certificate"
mv "$STATE_DIR/${main_domain}.staging" "$STATE_DIR/${main_domain}"
fi
return 1
@@ -152,6 +209,7 @@ issue_cert()
# commit and reload is in post_checks
fi
+ post_checks
}
load_vars()
@@ -163,19 +221,22 @@ load_vars()
DEBUG=$(config_get "$section" debug)
}
-if [ -n "$CHECK_CRON" ]; then
- check_cron
- exit 0
-fi
+check_cron
+[ -n "$CHECK_CRON" ] && exit 0
config_load acme
config_foreach load_vars acme
-pre_checks || exit 1
+if [ -z "$STATE_DIR" ] || [ -z "$ACCOUNT_EMAIL" ]; then
+ err "state_dir and account_email must be set"
+ exit 1
+fi
+
+[ -d "$STATE_DIR" ] || mkdir -p "$STATE_DIR"
+
trap err_out HUP TERM
trap int_out INT
config_foreach issue_cert cert
-post_checks
exit 0