Browse Source

support nginx mode

neilpang 8 years ago
parent
commit
9d725af602
2 changed files with 235 additions and 11 deletions
  1. 26 6
      README.md
  2. 209 5
      acme.sh

+ 26 - 6
README.md

@@ -54,6 +54,7 @@ https://github.com/Neilpang/acmetest
 - Webroot mode
 - Webroot mode
 - Standalone mode
 - Standalone mode
 - Apache mode
 - Apache mode
+- Nginx mode
 - DNS mode
 - DNS mode
 - [Stateless mode](https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode)
 - [Stateless mode](https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode)
 
 
@@ -215,8 +216,27 @@ acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com
 
 
 More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
 More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
 
 
+# 7. Use Nginx mode
 
 
-# 7. Use DNS mode:
+**(requires you to be root/sudoer, since it is required to interact with Nginx server)**
+
+If you are running a web server, Apache or Nginx, it is recommended to use the `Webroot mode`.
+
+Particularly, if you are running an nginx server, you can use nginx mode instead. This mode doesn't write any files to your web root folder.
+
+Just set string "nginx" as the second argument.
+
+It will configure nginx server automatically to verify the domain and then restore the nginx config to the original version.
+
+So, the config is not changed.
+
+```
+acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com
+```
+
+More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
+
+# 8. Use DNS mode:
 
 
 Support the `dns-01` challenge.
 Support the `dns-01` challenge.
 
 
@@ -247,7 +267,7 @@ acme.sh --renew -d example.com
 Ok, it's finished.
 Ok, it's finished.
 
 
 
 
-# 8. Automatic DNS API integration
+# 9. Automatic DNS API integration
 
 
 If your DNS provider supports API access, we can use that API to automatically issue the certs.
 If your DNS provider supports API access, we can use that API to automatically issue the certs.
 
 
@@ -280,7 +300,7 @@ If your DNS provider is not on the supported list above, you can write your own
 For more details: [How to use DNS API](dnsapi)
 For more details: [How to use DNS API](dnsapi)
 
 
 
 
-# 9. Issue ECC certificates
+# 10. Issue ECC certificates
 
 
 `Let's Encrypt` can now issue **ECDSA** certificates.
 `Let's Encrypt` can now issue **ECDSA** certificates.
 
 
@@ -311,7 +331,7 @@ Valid values are:
 3. **ec-521 (secp521r1,  "ECDSA P-521", which is not supported by Let's Encrypt yet.)**
 3. **ec-521 (secp521r1,  "ECDSA P-521", which is not supported by Let's Encrypt yet.)**
 
 
 
 
-# 10. How to renew the issued certs
+# 11. How to renew the issued certs
 
 
 No, you don't need to renew the certs manually. All the certs will be renewed automatically every **60** days.
 No, you don't need to renew the certs manually. All the certs will be renewed automatically every **60** days.
 
 
@@ -328,7 +348,7 @@ acme.sh --renew -d example.com --force --ecc
 ```
 ```
 
 
 
 
-# 11. How to upgrade `acme.sh`
+# 12. How to upgrade `acme.sh`
 
 
 acme.sh is in constant development, so it's strongly recommended to use the latest code.
 acme.sh is in constant development, so it's strongly recommended to use the latest code.
 
 
@@ -353,7 +373,7 @@ acme.sh --upgrade --auto-upgrade 0
 ```
 ```
 
 
 
 
-# 12. Issue a cert from an existing CSR
+# 13. Issue a cert from an existing CSR
 
 
 https://github.com/Neilpang/acme.sh/wiki/Issue-a-cert-from-existing-CSR
 https://github.com/Neilpang/acme.sh/wiki/Issue-a-cert-from-existing-CSR
 
 

+ 209 - 5
acme.sh

@@ -45,6 +45,8 @@ MODE_STATELESS="stateless"
 
 
 STATE_VERIFIED="verified_ok"
 STATE_VERIFIED="verified_ok"
 
 
+NGINX="nginx:"
+
 BEGIN_CSR="-----BEGIN CERTIFICATE REQUEST-----"
 BEGIN_CSR="-----BEGIN CERTIFICATE REQUEST-----"
 END_CSR="-----END CERTIFICATE REQUEST-----"
 END_CSR="-----END CERTIFICATE REQUEST-----"
 
 
@@ -2277,10 +2279,186 @@ Allow from all
   return 0
   return 0
 }
 }
 
 
+#find the real nginx conf file
+#backup
+#set the nginx conf
+#returns the real nginx conf file
+_setNginx() {
+  _d="$1"
+  _croot="$2"
+  _thumbpt="$3"
+  if ! _exists "nginx"; then
+    _err "nginx command is not found."
+    return 1
+  fi
+  FOUND_REAL_NGINX_CONF=""
+  BACKUP_NGINX_CONF=""
+  _debug _croot "$_croot"
+  _start_f="$(echo "$_croot" | cut -d : -f 2)"
+  _debug _start_f "$_start_f"
+  if [ -z "$_start_f" ]; then
+    _debug "find start conf from nginx command"
+    if [ -z "$NGINX_CONF" ]; then
+      NGINX_CONF="$(nginx -V 2>&1 | _egrep_o "--conf-path=[^ ]* " | tr -d " ")"
+      _debug NGINX_CONF "$NGINX_CONF"
+      NGINX_CONF="$(echo "$NGINX_CONF" | cut -d = -f 2)"
+      _debug NGINX_CONF "$NGINX_CONF"
+      if [ ! -f "$NGINX_CONF" ]; then
+        _err "'$NGINX_CONF' doesn't exist."
+        NGINX_CONF=""
+        return 1
+      fi
+      _debug "Found nginx conf file:$NGINX_CONF"
+    fi
+    _start_f="$NGINX_CONF"
+  fi
+  _info "Start detect nginx conf for $_d from:$_start_f"
+  if ! _checkConf "$_d" "$_start_f"; then
+    "Can not find conf file for domain $d"
+    return 1
+  fi
+  _info "Found conf file: $FOUND_REAL_NGINX_CONF"
+
+  mkdir -p "$DOMAIN_BACKUP_PATH"
+  _backup_conf="$DOMAIN_BACKUP_PATH/$_d.nginx.conf"
+  _debug _backup_conf "$_backup_conf"
+  BACKUP_NGINX_CONF="$_backup_conf"
+  _info "Backup $FOUND_REAL_NGINX_CONF to $_backup_conf"
+  if ! cp "$FOUND_REAL_NGINX_CONF" "$_backup_conf"; then
+    _err "backup error."
+    FOUND_REAL_NGINX_CONF=""
+    return 1
+  fi
+
+  _info "Check the nginx conf before setting up."
+  if ! _exec "nginx -t" >/dev/null; then
+    _exec_err
+    return 1
+  fi
+
+  _info "OK, Set up nginx config file"
+  _ln=$(grep -n "^ *server_name.* $_d" "$_backup_conf" | cut -d : -f 1 | tr -d "\n")
+  _debug "_ln" "$_ln"
+
+  if ! sed -n "1,${_ln}p" "$_backup_conf" > "$FOUND_REAL_NGINX_CONF"; then
+    cat "$_backup_conf" > "$FOUND_REAL_NGINX_CONF"
+    _err "write nginx conf error, but don't worry, the file is restored to the original version."
+    return 1
+  fi
+
+  echo "
+location ~ \"^/\.well-known/acme-challenge/([-_a-zA-Z0-9]+)\$\" {
+  default_type text/plain;
+  return 200 \"\$1.$_thumbpt\";
+}  
+" >> "$FOUND_REAL_NGINX_CONF"
+
+  _ln=$(_math $_ln + 1)
+  if ! sed -n "${_ln},99999p" "$_backup_conf" >> "$FOUND_REAL_NGINX_CONF"; then
+    cat "$_backup_conf" > "$FOUND_REAL_NGINX_CONF"
+    _err "write nginx conf error, but don't worry, the file is restored."
+    return 1
+  fi
+
+  _info "nginx conf is done, let's check it again."
+  if ! _exec "nginx -t" >/dev/null; then
+    _exec_err
+    _err "It seems that nginx conf was broken, let's restore."
+     cat "$_backup_conf" > "$FOUND_REAL_NGINX_CONF"
+    return 1
+  fi
+
+  _info "Reload nginx"
+  if ! _exec "nginx -s reload" >/dev/null; then
+    _exec_err
+    _err "It seems that nginx reload error, let's restore."
+     cat "$_backup_conf" > "$FOUND_REAL_NGINX_CONF"
+    return 1
+  fi
+
+  return 0
+}
+
+#d , conf
+_checkConf() {
+  _d="$1"
+  _c_file="$2"
+  _debug "Start _checkConf from:$_c_file"
+  if [ ! -f "$2" ] && ! echo "$2" | grep '*$' >/dev/null && echo "$2" | grep '*' >/dev/null; then
+    _debug "wildcard"
+    for _w_f in $2; do
+      if _checkConf "$1" "$_w_f"; then
+        return 0
+      fi
+    done
+    #not found
+    return 1
+  elif [ -f "$2" ]; then
+    _debug "single"
+    if _isRealNginxConf "$1" "$2"; then
+      _debug "$2 is found."
+      FOUND_REAL_NGINX_CONF="$2"
+      return 0
+    fi
+    if grep "^ *include *.*;" "$2" >/dev/null; then
+      _debug "Try include files"
+      for included in $(grep "^ *include *.*;" "$2"| sed "s/include //" | tr -d " ;" ); do
+        _debug "check included $included"
+        if _checkConf "$1" "$included"; then
+          return 0
+        fi
+      done
+    fi
+    return 1
+  else
+    _debug "$2 not found."
+    return 1
+  fi
+  return 1
+}
+
+#d , conf
+_isRealNginxConf() {
+  _debug "_isRealNginxConf $1 $2"
+  if [ -f "$2" ] && grep "^ *server_name " "$2" | grep " $1" >/dev/null; then
+    return 0
+  else
+    return 1
+  fi
+}
+
+#restore all the nginx conf
+_restoreNginx() {
+  if [ -z "$NGINX_VLIST" ]; then
+    _debug "No need to restore nginx, skip."
+    return
+  fi
+  _debug "_restoreNginx"
+  _debug "NGINX_VLIST" "$NGINX_VLIST"
+
+  for ng_entry in $(echo "$NGINX_VLIST" | tr "$dvsep" ' '); do
+    _debug "ng_entry" "$ng_entry"
+    _nd=$(echo "$ng_entry" | cut -d "$sep" -f 1)
+    _ngconf=$(echo "$ng_entry" | cut -d "$sep" -f 2)
+    _ngbackupconf=$(echo "$ng_entry" | cut -d "$sep" -f 3)
+    _info "Restoring from $_ngbackupconf to $_ngconf"
+    cat "$_ngbackupconf" > "$_ngconf"
+  done
+
+  _info "Reload nginx"
+  if ! _exec "nginx -s reload" >/dev/null; then
+    _exec_err
+    _err "It seems that nginx reload error, please report bug."
+    return 1
+  fi
+  return 0
+}
+
 _clearup() {
 _clearup() {
   _stopserver "$serverproc"
   _stopserver "$serverproc"
   serverproc=""
   serverproc=""
   _restoreApache
   _restoreApache
+  _restoreNginx
   _clearupdns
   _clearupdns
   if [ -z "$DEBUG" ]; then
   if [ -z "$DEBUG" ]; then
     rm -f "$TLS_CONF"
     rm -f "$TLS_CONF"
@@ -2822,6 +3000,7 @@ issue() {
 
 
   _info "Getting domain auth token for each domain"
   _info "Getting domain auth token for each domain"
   sep='#'
   sep='#'
+  dvsep=','
   if [ -z "$vlist" ]; then
   if [ -z "$vlist" ]; then
     alldomains=$(echo "$Le_Domain,$Le_Alt" | tr ',' ' ')
     alldomains=$(echo "$Le_Domain,$Le_Alt" | tr ',' ' ')
     _index=1
     _index=1
@@ -2829,7 +3008,7 @@ issue() {
     for d in $alldomains; do
     for d in $alldomains; do
       _info "Getting webroot for domain" "$d"
       _info "Getting webroot for domain" "$d"
       _w="$(echo $Le_Webroot | cut -d , -f $_index)"
       _w="$(echo $Le_Webroot | cut -d , -f $_index)"
-      _info _w "$_w"
+      _debug _w "$_w"
       if [ "$_w" ]; then
       if [ "$_w" ]; then
         _currentRoot="$_w"
         _currentRoot="$_w"
       fi
       fi
@@ -2881,13 +3060,13 @@ issue() {
       dvlist="$d$sep$keyauthorization$sep$uri$sep$vtype$sep$_currentRoot"
       dvlist="$d$sep$keyauthorization$sep$uri$sep$vtype$sep$_currentRoot"
       _debug dvlist "$dvlist"
       _debug dvlist "$dvlist"
 
 
-      vlist="$vlist$dvlist,"
+      vlist="$vlist$dvlist$dvsep"
 
 
     done
     done
-
+    _debug vlist "$vlist"
     #add entry
     #add entry
     dnsadded=""
     dnsadded=""
-    ventries=$(echo "$vlist" | tr ',' ' ')
+    ventries=$(echo "$vlist" | tr "$dvsep" ' ')
     for ventry in $ventries; do
     for ventry in $ventries; do
       d=$(echo "$ventry" | cut -d "$sep" -f 1)
       d=$(echo "$ventry" | cut -d "$sep" -f 1)
       keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
       keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
@@ -2970,10 +3149,11 @@ issue() {
     _sleep "$Le_DNSSleep"
     _sleep "$Le_DNSSleep"
   fi
   fi
 
 
+  NGINX_VLIST=""
   _debug "ok, let's start to verify"
   _debug "ok, let's start to verify"
 
 
   _ncIndex=1
   _ncIndex=1
-  ventries=$(echo "$vlist" | tr ',' ' ')
+  ventries=$(echo "$vlist" | tr "$dvsep" ' ')
   for ventry in $ventries; do
   for ventry in $ventries; do
     d=$(echo "$ventry" | cut -d "$sep" -f 1)
     d=$(echo "$ventry" | cut -d "$sep" -f 1)
     keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
     keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
@@ -3012,6 +3192,22 @@ issue() {
       elif [ "$_currentRoot" = "$MODE_STATELESS" ]; then
       elif [ "$_currentRoot" = "$MODE_STATELESS" ]; then
         _info "Stateless mode for domain:$d"
         _info "Stateless mode for domain:$d"
         _sleep 1
         _sleep 1
+      elif _startswith "$_currentRoot" "$NGINX"; then
+        _info "Nginx mode for domain:$d"
+        #set up nginx server
+        FOUND_REAL_NGINX_CONF=""
+        BACKUP_NGINX_CONF=""
+        if ! _setNginx "$d" "$_currentRoot" "$thumbprint"; then
+          _clearup
+          _on_issue_err
+          return 1
+        else
+          _realConf="$FOUND_REAL_NGINX_CONF"
+          _backup="$BACKUP_NGINX_CONF"
+          _debug _realConf "$_realConf"
+          NGINX_VLIST="$NGINX_VLIST$d$sep$_realConf$sep$_backup$dvsep"
+        fi
+        _sleep 1
       else
       else
         if [ "$_currentRoot" = "apache" ]; then
         if [ "$_currentRoot" = "apache" ]; then
           wellknown_path="$ACME_DIR"
           wellknown_path="$ACME_DIR"
@@ -4629,6 +4825,14 @@ _process() {
           _webroot="$_webroot,$wvalue"
           _webroot="$_webroot,$wvalue"
         fi
         fi
         ;;
         ;;
+      --nginx)
+        wvalue="$NGINX"
+        if [ -z "$_webroot" ]; then
+          _webroot="$wvalue"
+        else
+          _webroot="$_webroot,$wvalue"
+        fi
+        ;;
       --tls)
       --tls)
         wvalue="$W_TLS"
         wvalue="$W_TLS"
         if [ -z "$_webroot" ]; then
         if [ -z "$_webroot" ]; then