mirror of
				https://github.com/hiskang/acme.sh
				synced 2025-10-31 10:27:22 +00:00 
			
		
		
		
	
						commit
						09fed60dec
					
				| @ -317,7 +317,7 @@ You don't have to do anything manually! | ||||
| 1. DirectAdmin API | ||||
| 1. KingHost (https://www.kinghost.com.br/) | ||||
| 1. Zilore (https://zilore.com) | ||||
| 
 | ||||
| 1. Loopia.se API | ||||
| 
 | ||||
| And:  | ||||
| 
 | ||||
|  | ||||
| @ -814,6 +814,28 @@ acme.sh --issue --dns dns_zilore -d example.com -d *.example.com | ||||
| 
 | ||||
| The `Zilore_Key` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| ## 44. Use Loopia.se API | ||||
| User must provide login credentials to the Loopia API. | ||||
| The user needs the following permissions: | ||||
| 
 | ||||
| - addSubdomain | ||||
| - updateZoneRecord | ||||
| - getDomains | ||||
| - removeSubdomain | ||||
| 
 | ||||
| Set the login credentials: | ||||
| ``` | ||||
| export LOOPIA_User="user@loopiaapi" | ||||
| export LOOPIA_Password="password" | ||||
| ``` | ||||
| 
 | ||||
| And to issue a cert: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_loopia -d example.com -d *.example.com | ||||
| ``` | ||||
| 
 | ||||
| The username and password will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| # Use custom API | ||||
| 
 | ||||
| If your API is not supported yet, you can write your own DNS API. | ||||
|  | ||||
| @ -76,10 +76,10 @@ dns_azure_add() { | ||||
|   values="{\"value\":[\"$txtvalue\"]}" | ||||
|   timestamp="$(_time)" | ||||
|   if [ "$_code" = "200" ]; then | ||||
|     vlist="$(echo "$response" | _egrep_o "\"value\"\s*:\s*\[\s*\"[^\"]*\"\s*]" | cut -d : -f 2 | tr -d "[]\"")" | ||||
|     vlist="$(echo "$response" | _egrep_o "\"value\"\\s*:\\s*\\[\\s*\"[^\"]*\"\\s*]" | cut -d : -f 2 | tr -d "[]\"")" | ||||
|     _debug "existing TXT found" | ||||
|     _debug "$vlist" | ||||
|     existingts="$(echo "$response" | _egrep_o "\"acmetscheck\"\s*:\s*\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d "\"")" | ||||
|     existingts="$(echo "$response" | _egrep_o "\"acmetscheck\"\\s*:\\s*\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d "\"")" | ||||
|     if [ -z "$existingts" ]; then | ||||
|       # the record was not created by acme.sh. Copy the exisiting entires | ||||
|       existingts=$timestamp | ||||
| @ -172,7 +172,7 @@ dns_azure_rm() { | ||||
|   _azure_rest GET "$acmeRecordURI" "" "$accesstoken" | ||||
|   timestamp="$(_time)" | ||||
|   if [ "$_code" = "200" ]; then | ||||
|     vlist="$(echo "$response" | _egrep_o "\"value\"\s*:\s*\[\s*\"[^\"]*\"\s*]" | cut -d : -f 2 | tr -d "[]\"" | grep -v "$txtvalue")" | ||||
|     vlist="$(echo "$response" | _egrep_o "\"value\"\\s*:\\s*\\[\\s*\"[^\"]*\"\\s*]" | cut -d : -f 2 | tr -d "[]\"" | grep -v "$txtvalue")" | ||||
|     values="" | ||||
|     comma="" | ||||
|     for v in $vlist; do | ||||
| @ -230,7 +230,7 @@ _azure_rest() { | ||||
|     fi | ||||
|     _ret="$?" | ||||
|     _secure_debug2 "response $response" | ||||
|     _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")" | ||||
|     _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")" | ||||
|     _debug "http response code $_code" | ||||
|     if [ "$_code" = "401" ]; then | ||||
|       # we have an invalid access token set to expired | ||||
| @ -308,7 +308,7 @@ _get_root() { | ||||
|   domain=$1 | ||||
|   subscriptionId=$2 | ||||
|   accesstoken=$3 | ||||
|   i=2 | ||||
|   i=1 | ||||
|   p=1 | ||||
| 
 | ||||
|   ## Ref: https://docs.microsoft.com/en-us/rest/api/dns/zones/list | ||||
| @ -328,9 +328,14 @@ _get_root() { | ||||
|     fi | ||||
| 
 | ||||
|     if _contains "$response" "\"name\":\"$h\"" >/dev/null; then | ||||
|       _domain_id=$(echo "$response" | _egrep_o "\{\"id\":\"[^\"]*$h\"" | head -n 1 | cut -d : -f 2 | tr -d \") | ||||
|       _domain_id=$(echo "$response" | _egrep_o "\\{\"id\":\"[^\"]*$h\"" | head -n 1 | cut -d : -f 2 | tr -d \") | ||||
|       if [ "$_domain_id" ]; then | ||||
|         _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) | ||||
|         if [ "$i" = 1 ]; then | ||||
|           #create the record at the domain apex (@) if only the domain name was provided as --domain-alias | ||||
|           _sub_domain="@" | ||||
|         else | ||||
|           _sub_domain=$(echo "$domain" | cut -d . -f 1-$p) | ||||
|         fi | ||||
|         _domain=$h | ||||
|         return 0 | ||||
|       fi | ||||
|  | ||||
| @ -33,8 +33,9 @@ dns_he_add() { | ||||
|   # Fills in the $_zone_id | ||||
|   _find_zone "$_full_domain" || return 1 | ||||
|   _debug "Zone id \"$_zone_id\" will be used." | ||||
| 
 | ||||
|   body="email=${HE_Username}&pass=${HE_Password}" | ||||
|   username_encoded="$(printf "%s" "${HE_Username}" | _url_encode)" | ||||
|   password_encoded="$(printf "%s" "${HE_Password}" | _url_encode)" | ||||
|   body="email=${username_encoded}&pass=${password_encoded}" | ||||
|   body="$body&account=" | ||||
|   body="$body&menu=edit_zone" | ||||
|   body="$body&Type=TXT" | ||||
| @ -71,7 +72,9 @@ dns_he_rm() { | ||||
|   _debug "Zone id \"$_zone_id\" will be used." | ||||
| 
 | ||||
|   # Find the record id to clean | ||||
|   body="email=${HE_Username}&pass=${HE_Password}" | ||||
|   username_encoded="$(printf "%s" "${HE_Username}" | _url_encode)" | ||||
|   password_encoded="$(printf "%s" "${HE_Password}" | _url_encode)" | ||||
|   body="email=${username_encoded}&pass=${password_encoded}" | ||||
|   body="$body&hosted_dns_zoneid=$_zone_id" | ||||
|   body="$body&menu=edit_zone" | ||||
|   body="$body&hosted_dns_editzone=" | ||||
| @ -112,9 +115,15 @@ dns_he_rm() { | ||||
| 
 | ||||
| _find_zone() { | ||||
|   _domain="$1" | ||||
|   body="email=${HE_Username}&pass=${HE_Password}" | ||||
|   username_encoded="$(printf "%s" "${HE_Username}" | _url_encode)" | ||||
|   password_encoded="$(printf "%s" "${HE_Password}" | _url_encode)" | ||||
|   body="email=${username_encoded}&pass=${password_encoded}" | ||||
|   response="$(_post "$body" "https://dns.he.net/")" | ||||
|   _debug2 response "$response" | ||||
|   if _contains "$response" '>Incorrect<'; then | ||||
|     _err "Unable to login to dns.he.net please check username and password" | ||||
|     return 1 | ||||
|   fi | ||||
|   _table="$(echo "$response" | tr -d "#" | sed "s/<table/#<table/g" | tr -d "\n" | tr "#" "\n" | grep 'id="domains_table"')" | ||||
|   _debug2 _table "$_table" | ||||
|   _matches="$(echo "$_table" | sed "s/<tr/#<tr/g" | tr "#" "\n" | grep 'alt="edit"' | tr -d " " | sed "s/<td/#<td/g" | tr "#" "\n" | grep 'hosted_dns_zoneid')" | ||||
|  | ||||
							
								
								
									
										227
									
								
								dnsapi/dns_loopia.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								dnsapi/dns_loopia.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,227 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| # | ||||
| #LOOPIA_User="username" | ||||
| # | ||||
| #LOOPIA_Password="password" | ||||
| 
 | ||||
| LOOPIA_Api="https://api.loopia.se/RPCSERV" | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_loopia_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   LOOPIA_User="${LOOPIA_User:-$(_readaccountconf_mutable LOOPIA_User)}" | ||||
|   LOOPIA_Password="${LOOPIA_Password:-$(_readaccountconf_mutable LOOPIA_Password)}" | ||||
|   if [ -z "$LOOPIA_User" ] || [ -z "$LOOPIA_Password" ]; then | ||||
|     LOOPIA_User="" | ||||
|     LOOPIA_Password="" | ||||
|     _err "You don't specify loopia user and password yet." | ||||
|     _err "Please create you key and try again." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   #save the api key and email to the account conf file. | ||||
|   _saveaccountconf_mutable LOOPIA_User "$LOOPIA_User" | ||||
|   _saveaccountconf_mutable LOOPIA_Password "$LOOPIA_Password" | ||||
| 
 | ||||
|   _debug "First detect the root zone" | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "invalid domain" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug _sub_domain "$_sub_domain" | ||||
|   _debug _domain "$_domain" | ||||
| 
 | ||||
|   _info "Adding record" | ||||
| 
 | ||||
|   _loopia_add_record "$_domain" "$_sub_domain" | ||||
|   _loopia_update_record "$_domain" "$_sub_domain" "$txtvalue" | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| dns_loopia_rm() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   LOOPIA_User="${LOOPIA_User:-$(_readaccountconf_mutable LOOPIA_User)}" | ||||
|   LOOPIA_Password="${LOOPIA_Password:-$(_readaccountconf_mutable LOOPIA_Password)}" | ||||
|   if [ -z "$LOOPIA_User" ] || [ -z "$LOOPIA_Password" ]; then | ||||
|     LOOPIA_User="" | ||||
|     LOOPIA_Password="" | ||||
|     _err "You don't specify LOOPIA user and password yet." | ||||
|     _err "Please create you key and try again." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   #save the api key and email to the account conf file. | ||||
|   _saveaccountconf_mutable LOOPIA_User "$LOOPIA_User" | ||||
|   _saveaccountconf_mutable LOOPIA_Password "$LOOPIA_Password" | ||||
| 
 | ||||
|   _debug "First detect the root zone" | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "invalid domain" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?> | ||||
|   <methodCall> | ||||
|     <methodName>removeSubdomain</methodName> | ||||
|     <params> | ||||
|       <param> | ||||
|         <value><string>%s</string></value> | ||||
|       </param> | ||||
|       <param> | ||||
|         <value><string>%s</string></value> | ||||
|       </param> | ||||
|       <param> | ||||
|         <value><string>%s</string></value> | ||||
|       </param> | ||||
|       <param> | ||||
|         <value><string>%s</string></value> | ||||
|       </param> | ||||
|     </params> | ||||
|   </methodCall>' $LOOPIA_User $LOOPIA_Password "$_domain" "$_sub_domain") | ||||
| 
 | ||||
|   response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")" | ||||
| 
 | ||||
|   if ! _contains "$response" "OK"; then | ||||
|     _err "Error could not get txt records" | ||||
|     return 1 | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions below ################################## | ||||
| 
 | ||||
| _get_root() { | ||||
|   domain=$1 | ||||
|   _debug "get root" | ||||
| 
 | ||||
|   domain=$1 | ||||
|   i=2 | ||||
|   p=1 | ||||
| 
 | ||||
|   xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?> | ||||
|   <methodCall> | ||||
|   <methodName>getDomains</methodName> | ||||
|   <params> | ||||
|    <param> | ||||
|     <value><string>%s</string></value> | ||||
|    </param> | ||||
|    <param> | ||||
|     <value><string>%s</string></value> | ||||
|    </param> | ||||
|   </params> | ||||
|   </methodCall>' $LOOPIA_User $LOOPIA_Password) | ||||
| 
 | ||||
|   response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")" | ||||
|   while true; do | ||||
|     h=$(echo "$domain" | cut -d . -f $i-100) | ||||
|     if [ -z "$h" ]; then | ||||
|       #not valid | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     if _contains "$response" "$h"; then | ||||
|       _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) | ||||
|       _domain="$h" | ||||
|       return 0 | ||||
|     fi | ||||
|     p=$i | ||||
|     i=$(_math "$i" + 1) | ||||
|   done | ||||
|   return 1 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| _loopia_update_record() { | ||||
|   domain=$1 | ||||
|   sub_domain=$2 | ||||
|   txtval=$3 | ||||
| 
 | ||||
|   xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?> | ||||
|   <methodCall> | ||||
|     <methodName>updateZoneRecord</methodName> | ||||
|     <params> | ||||
|       <param> | ||||
|         <value><string>%s</string></value> | ||||
|       </param> | ||||
|       <param> | ||||
|         <value><string>%s</string></value> | ||||
|       </param> | ||||
|       <param> | ||||
|         <value><string>%s</string></value> | ||||
|       </param> | ||||
|       <param> | ||||
|         <value><string>%s</string></value> | ||||
|       </param> | ||||
|       <param> | ||||
|         <struct> | ||||
|           <member> | ||||
|             <name>type</name> | ||||
|             <value><string>TXT</string></value> | ||||
|           </member> | ||||
|           <member> | ||||
|             <name>priority</name> | ||||
|             <value><int>0</int></value> | ||||
|           </member> | ||||
|           <member> | ||||
|             <name>ttl</name> | ||||
|             <value><int>60</int></value> | ||||
|           </member> | ||||
|           <member> | ||||
|             <name>rdata</name> | ||||
|             <value><string>%s</string></value> | ||||
|           </member> | ||||
|           <member> | ||||
|             <name>record_id</name> | ||||
|             <value><int>0</int></value> | ||||
|           </member> | ||||
|         </struct> | ||||
|       </param> | ||||
|     </params> | ||||
|   </methodCall>' $LOOPIA_User $LOOPIA_Password "$domain" "$sub_domain" "$txtval") | ||||
| 
 | ||||
|   response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")" | ||||
| 
 | ||||
|   if ! _contains "$response" "OK"; then | ||||
|     _err "Error" | ||||
|     return 1 | ||||
|   fi | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| _loopia_add_record() { | ||||
|   domain=$1 | ||||
|   sub_domain=$2 | ||||
| 
 | ||||
|   xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?> | ||||
|   <methodCall> | ||||
|     <methodName>addSubdomain</methodName> | ||||
|     <params> | ||||
|       <param> | ||||
|         <value><string>%s</string></value> | ||||
|       </param> | ||||
|       <param> | ||||
|         <value><string>%s</string></value> | ||||
|       </param> | ||||
|       <param> | ||||
|         <value><string>%s</string></value> | ||||
|       </param> | ||||
|       <param> | ||||
|         <value><string>%s</string></value> | ||||
|       </param> | ||||
|     </params> | ||||
|   </methodCall>' $LOOPIA_User $LOOPIA_Password "$domain" "$sub_domain") | ||||
| 
 | ||||
|   response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")" | ||||
| 
 | ||||
|   if ! _contains "$response" "OK"; then | ||||
|     _err "Error" | ||||
|     return 1 | ||||
|   fi | ||||
|   return 0 | ||||
| } | ||||
| @ -69,15 +69,21 @@ dns_pdns_add() { | ||||
| #fulldomain | ||||
| dns_pdns_rm() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   if [ -z "$PDNS_Ttl" ]; then | ||||
|     PDNS_Ttl="$DEFAULT_PDNS_TTL" | ||||
|   fi | ||||
| 
 | ||||
|   _debug "Detect root zone" | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "invalid domain" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _debug _domain "$_domain" | ||||
| 
 | ||||
|   if ! rm_record "$_domain" "$fulldomain"; then | ||||
|   if ! rm_record "$_domain" "$fulldomain" "$txtvalue"; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
| @ -88,9 +94,16 @@ set_record() { | ||||
|   _info "Adding record" | ||||
|   root=$1 | ||||
|   full=$2 | ||||
|   txtvalue=$3 | ||||
|   new_challenge=$3 | ||||
| 
 | ||||
|   if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root" "{\"rrsets\": [{\"changetype\": \"REPLACE\", \"name\": \"$full.\", \"type\": \"TXT\", \"ttl\": $PDNS_Ttl, \"records\": [{\"name\": \"$full.\", \"type\": \"TXT\", \"content\": \"\\\"$txtvalue\\\"\", \"disabled\": false, \"ttl\": $PDNS_Ttl}]}]}"; then | ||||
|   _record_string="" | ||||
|   _build_record_string "$new_challenge" | ||||
|   _list_existingchallenges | ||||
|   for oldchallenge in $_existing_challenges; do | ||||
|     _build_record_string "$oldchallenge" | ||||
|   done | ||||
| 
 | ||||
|   if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root" "{\"rrsets\": [{\"changetype\": \"REPLACE\", \"name\": \"$full.\", \"type\": \"TXT\", \"ttl\": $PDNS_Ttl, \"records\": [$_record_string]}]}"; then | ||||
|     _err "Set txt record error." | ||||
|     return 1 | ||||
|   fi | ||||
| @ -106,15 +119,38 @@ rm_record() { | ||||
|   _info "Remove record" | ||||
|   root=$1 | ||||
|   full=$2 | ||||
|   txtvalue=$3 | ||||
| 
 | ||||
|   #Enumerate existing acme challenges | ||||
|   _list_existingchallenges | ||||
| 
 | ||||
|   if _contains "$_existing_challenges" "$txtvalue"; then | ||||
|     #Delete all challenges (PowerDNS API does not allow to delete content) | ||||
|     if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root" "{\"rrsets\": [{\"changetype\": \"DELETE\", \"name\": \"$full.\", \"type\": \"TXT\"}]}"; then | ||||
|       _err "Delete txt record error." | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     _record_string="" | ||||
|     #If the only existing challenge was the challenge to delete: nothing to do | ||||
|     if ! [ "$_existing_challenges" = "$txtvalue" ]; then | ||||
|       for oldchallenge in $_existing_challenges; do | ||||
|         #Build up the challenges to re-add, ommitting the one what should be deleted | ||||
|         if ! [ "$oldchallenge" = "$txtvalue" ]; then | ||||
|           _build_record_string "$oldchallenge" | ||||
|         fi | ||||
|       done | ||||
|       #Recreate the existing challenges | ||||
|       if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root" "{\"rrsets\": [{\"changetype\": \"REPLACE\", \"name\": \"$full.\", \"type\": \"TXT\", \"ttl\": $PDNS_Ttl, \"records\": [$_record_string]}]}"; then | ||||
|         _err "Set txt record error." | ||||
|         return 1 | ||||
|       fi | ||||
|     fi | ||||
|     if ! notify_slaves "$root"; then | ||||
|       return 1 | ||||
|     fi | ||||
|   else | ||||
|     _info "Record not found, nothing to remove" | ||||
|   fi | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| @ -185,3 +221,12 @@ _pdns_rest() { | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| _build_record_string() { | ||||
|   _record_string="${_record_string:+${_record_string}, }{\"content\": \"\\\"${1}\\\"\", \"disabled\": false}" | ||||
| } | ||||
| 
 | ||||
| _list_existingchallenges() { | ||||
|   _pdns_rest "GET" "/api/v1/servers/$PDNS_ServerId/zones/$root" | ||||
|   _existing_challenges=$(echo "$response" | _normalizeJson | _egrep_o "\"name\":\"${fulldomain}[^]]*}" | _egrep_o 'content\":\"\\"[^\\]*' | sed -n 's/^content":"\\"//p') | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user