mirror of
				https://github.com/hiskang/acme.sh
				synced 2025-10-31 10:27:22 +00:00 
			
		
		
		
	
						commit
						e1c42eb6cc
					
				| @ -292,6 +292,7 @@ You don't have to do anything manually! | |||||||
| 1. Alwaysdata.com API | 1. Alwaysdata.com API | ||||||
| 1. Linode.com API | 1. Linode.com API | ||||||
| 1. FreeDNS (https://freedns.afraid.org/) | 1. FreeDNS (https://freedns.afraid.org/) | ||||||
|  | 1. cyon.ch | ||||||
| 
 | 
 | ||||||
| **More APIs coming soon...** | **More APIs coming soon...** | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -305,6 +305,24 @@ Note that you cannot use acme.sh automatic DNS validation for FreeDNS public dom | |||||||
| you create under a FreeDNS public domain.  You must own the top level domain in order to automaitcally | you create under a FreeDNS public domain.  You must own the top level domain in order to automaitcally | ||||||
| validate with acme.sh at FreeDNS. | validate with acme.sh at FreeDNS. | ||||||
| 
 | 
 | ||||||
|  | ## 16. Use cyon.ch | ||||||
|  | 
 | ||||||
|  | You only need to set your cyon.ch login credentials. | ||||||
|  | If you also have 2 Factor Authentication (OTP) enabled, you need to set your secret token too and have `oathtool` installed. | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | export CY_Username="your_cyon_username" | ||||||
|  | export CY_Password="your_cyon_password" | ||||||
|  | export CY_OTP_Secret="your_otp_secret" # Only required if using 2FA | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | To issue a cert: | ||||||
|  | ``` | ||||||
|  | acme.sh --issue --dns dns_cyon -d example.com -d www.example.com | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | The `CY_Username`, `CY_Password` and `CY_OTP_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||||
|  | 
 | ||||||
| # Use custom API | # Use custom API | ||||||
| 
 | 
 | ||||||
| If your API is not supported yet, you can write your own DNS API. | If your API is not supported yet, you can write your own DNS API. | ||||||
|  | |||||||
							
								
								
									
										328
									
								
								dnsapi/dns_cyon.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										328
									
								
								dnsapi/dns_cyon.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,328 @@ | |||||||
|  | #!/usr/bin/env sh | ||||||
|  | 
 | ||||||
|  | ######## | ||||||
|  | # Custom cyon.ch DNS API for use with [acme.sh](https://github.com/Neilpang/acme.sh) | ||||||
|  | # | ||||||
|  | # Usage: acme.sh --issue --dns dns_cyon -d www.domain.com | ||||||
|  | # | ||||||
|  | # Dependencies: | ||||||
|  | # ------------- | ||||||
|  | # - oathtool (When using 2 Factor Authentication) | ||||||
|  | # | ||||||
|  | # Issues: | ||||||
|  | # ------- | ||||||
|  | # Any issues / questions / suggestions can be posted here: | ||||||
|  | # https://github.com/noplanman/cyon-api/issues | ||||||
|  | # | ||||||
|  | # Author: Armando Lüscher <armando@noplanman.ch> | ||||||
|  | ######## | ||||||
|  | 
 | ||||||
|  | dns_cyon_add() { | ||||||
|  |   _cyon_load_credentials \ | ||||||
|  |     && _cyon_load_parameters "$@" \ | ||||||
|  |     && _cyon_print_header "add" \ | ||||||
|  |     && _cyon_login \ | ||||||
|  |     && _cyon_change_domain_env \ | ||||||
|  |     && _cyon_add_txt \ | ||||||
|  |     && _cyon_logout | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | dns_cyon_rm() { | ||||||
|  |   _cyon_load_credentials \ | ||||||
|  |     && _cyon_load_parameters "$@" \ | ||||||
|  |     && _cyon_print_header "delete" \ | ||||||
|  |     && _cyon_login \ | ||||||
|  |     && _cyon_change_domain_env \ | ||||||
|  |     && _cyon_delete_txt \ | ||||||
|  |     && _cyon_logout | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ######################### | ||||||
|  | ### PRIVATE FUNCTIONS ### | ||||||
|  | ######################### | ||||||
|  | 
 | ||||||
|  | _cyon_load_credentials() { | ||||||
|  |   # Convert loaded password to/from base64 as needed. | ||||||
|  |   if [ "${CY_Password_B64}" ]; then | ||||||
|  |     CY_Password="$(printf "%s" "${CY_Password_B64}" | _dbase64 "multiline")" | ||||||
|  |   elif [ "${CY_Password}" ]; then | ||||||
|  |     CY_Password_B64="$(printf "%s" "${CY_Password}" | _base64)" | ||||||
|  |   fi | ||||||
|  | 
 | ||||||
|  |   if [ -z "${CY_Username}" ] || [ -z "${CY_Password}" ]; then | ||||||
|  |     # Dummy entries to satify script checker. | ||||||
|  |     CY_Username="" | ||||||
|  |     CY_Password="" | ||||||
|  |     CY_OTP_Secret="" | ||||||
|  | 
 | ||||||
|  |     _err "" | ||||||
|  |     _err "You haven't set your cyon.ch login credentials yet." | ||||||
|  |     _err "Please set the required cyon environment variables." | ||||||
|  |     _err "" | ||||||
|  |     return 1 | ||||||
|  |   fi | ||||||
|  | 
 | ||||||
|  |   # Save the login credentials to the account.conf file. | ||||||
|  |   _debug "Save credentials to account.conf" | ||||||
|  |   _saveaccountconf CY_Username "${CY_Username}" | ||||||
|  |   _saveaccountconf CY_Password_B64 "$CY_Password_B64" | ||||||
|  |   if [ ! -z "${CY_OTP_Secret}" ]; then | ||||||
|  |     _saveaccountconf CY_OTP_Secret "$CY_OTP_Secret" | ||||||
|  |   else | ||||||
|  |     _clearaccountconf CY_OTP_Secret | ||||||
|  |   fi | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | _cyon_is_idn() { | ||||||
|  |   _idn_temp="$(printf "%s" "${1}" | tr -d "0-9a-zA-Z.,-_")" | ||||||
|  |   _idn_temp2="$(printf "%s" "${1}" | grep -o "xn--")" | ||||||
|  |   [ "$_idn_temp" ] || [ "$_idn_temp2" ] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | _cyon_load_parameters() { | ||||||
|  |   # Read the required parameters to add the TXT entry. | ||||||
|  |   # shellcheck disable=SC2018,SC2019 | ||||||
|  |   fulldomain="$(printf "%s" "${1}" | tr "A-Z" "a-z")" | ||||||
|  |   fulldomain_idn="${fulldomain}" | ||||||
|  | 
 | ||||||
|  |   # Special case for IDNs, as cyon needs a domain environment change, | ||||||
|  |   # which uses the "pretty" instead of the punycode version. | ||||||
|  |   if _cyon_is_idn "${fulldomain}"; then | ||||||
|  |     if ! _exists idn; then | ||||||
|  |       _err "Please install idn to process IDN names." | ||||||
|  |       _err "" | ||||||
|  |       return 1 | ||||||
|  |     fi | ||||||
|  | 
 | ||||||
|  |     fulldomain="$(idn -u "${fulldomain}")" | ||||||
|  |     fulldomain_idn="$(idn -a "${fulldomain}")" | ||||||
|  |   fi | ||||||
|  | 
 | ||||||
|  |   _debug fulldomain "${fulldomain}" | ||||||
|  |   _debug fulldomain_idn "${fulldomain_idn}" | ||||||
|  | 
 | ||||||
|  |   txtvalue="${2}" | ||||||
|  |   _debug txtvalue "${txtvalue}" | ||||||
|  | 
 | ||||||
|  |   # This header is required for curl calls. | ||||||
|  |   _H1="X-Requested-With: XMLHttpRequest" | ||||||
|  |   export _H1 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | _cyon_print_header() { | ||||||
|  |   if [ "${1}" = "add" ]; then | ||||||
|  |     _info "" | ||||||
|  |     _info "+---------------------------------------------+" | ||||||
|  |     _info "| Adding DNS TXT entry to your cyon.ch domain |" | ||||||
|  |     _info "+---------------------------------------------+" | ||||||
|  |     _info "" | ||||||
|  |     _info "  * Full Domain: ${fulldomain}" | ||||||
|  |     _info "  * TXT Value:   ${txtvalue}" | ||||||
|  |     _info "" | ||||||
|  |   elif [ "${1}" = "delete" ]; then | ||||||
|  |     _info "" | ||||||
|  |     _info "+-------------------------------------------------+" | ||||||
|  |     _info "| Deleting DNS TXT entry from your cyon.ch domain |" | ||||||
|  |     _info "+-------------------------------------------------+" | ||||||
|  |     _info "" | ||||||
|  |     _info "  * Full Domain: ${fulldomain}" | ||||||
|  |     _info "" | ||||||
|  |   fi | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | _cyon_get_cookie_header() { | ||||||
|  |   printf "Cookie: %s" "$(grep "cyon=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'cyon=[^;]*;' | tr -d ';')" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | _cyon_login() { | ||||||
|  |   _info "  - Logging in..." | ||||||
|  | 
 | ||||||
|  |   username_encoded="$(printf "%s" "${CY_Username}" | _url_encode)" | ||||||
|  |   password_encoded="$(printf "%s" "${CY_Password}" | _url_encode)" | ||||||
|  | 
 | ||||||
|  |   login_url="https://my.cyon.ch/auth/index/dologin-async" | ||||||
|  |   login_data="$(printf "%s" "username=${username_encoded}&password=${password_encoded}&pathname=%2F")" | ||||||
|  | 
 | ||||||
|  |   login_response="$(_post "$login_data" "$login_url")" | ||||||
|  |   _debug login_response "${login_response}" | ||||||
|  | 
 | ||||||
|  |   # Bail if login fails. | ||||||
|  |   if [ "$(printf "%s" "${login_response}" | _cyon_get_response_success)" != "success" ]; then | ||||||
|  |     _err "    $(printf "%s" "${login_response}" | _cyon_get_response_message)" | ||||||
|  |     _err "" | ||||||
|  |     return 1 | ||||||
|  |   fi | ||||||
|  | 
 | ||||||
|  |   _info "    success" | ||||||
|  | 
 | ||||||
|  |   # NECESSARY!! Load the main page after login, to get the new cookie. | ||||||
|  |   _H2="$(_cyon_get_cookie_header)" | ||||||
|  |   export _H2 | ||||||
|  | 
 | ||||||
|  |   _get "https://my.cyon.ch/" >/dev/null | ||||||
|  | 
 | ||||||
|  |   # todo: instead of just checking if the env variable is defined, check if we actually need to do a 2FA auth request. | ||||||
|  | 
 | ||||||
|  |   # 2FA authentication with OTP? | ||||||
|  |   if [ ! -z "${CY_OTP_Secret}" ]; then | ||||||
|  |     _info "  - Authorising with OTP code..." | ||||||
|  | 
 | ||||||
|  |     if ! _exists oathtool; then | ||||||
|  |       _err "Please install oathtool to use 2 Factor Authentication." | ||||||
|  |       _err "" | ||||||
|  |       return 1 | ||||||
|  |     fi | ||||||
|  | 
 | ||||||
|  |     # Get OTP code with the defined secret. | ||||||
|  |     otp_code="$(oathtool --base32 --totp "${CY_OTP_Secret}" 2>/dev/null)" | ||||||
|  | 
 | ||||||
|  |     login_otp_url="https://my.cyon.ch/auth/multi-factor/domultifactorauth-async" | ||||||
|  |     login_otp_data="totpcode=${otp_code}&pathname=%2F&rememberme=0" | ||||||
|  | 
 | ||||||
|  |     login_otp_response="$(_post "$login_otp_data" "$login_otp_url")" | ||||||
|  |     _debug login_otp_response "${login_otp_response}" | ||||||
|  | 
 | ||||||
|  |     # Bail if OTP authentication fails. | ||||||
|  |     if [ "$(printf "%s" "${login_otp_response}" | _cyon_get_response_success)" != "success" ]; then | ||||||
|  |       _err "    $(printf "%s" "${login_otp_response}" | _cyon_get_response_message)" | ||||||
|  |       _err "" | ||||||
|  |       return 1 | ||||||
|  |     fi | ||||||
|  | 
 | ||||||
|  |     _info "    success" | ||||||
|  |   fi | ||||||
|  | 
 | ||||||
|  |   _info "" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | _cyon_logout() { | ||||||
|  |   _info "  - Logging out..." | ||||||
|  | 
 | ||||||
|  |   _get "https://my.cyon.ch/auth/index/dologout" >/dev/null | ||||||
|  | 
 | ||||||
|  |   _info "    success" | ||||||
|  |   _info "" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | _cyon_change_domain_env() { | ||||||
|  |   _info "  - Changing domain environment..." | ||||||
|  | 
 | ||||||
|  |   # Get the "example.com" part of the full domain name. | ||||||
|  |   domain_env="$(printf "%s" "${fulldomain}" | sed -E -e 's/.*\.(.*\..*)$/\1/')" | ||||||
|  |   _debug "Changing domain environment to ${domain_env}" | ||||||
|  | 
 | ||||||
|  |   gloo_item_key="$(_get "https://my.cyon.ch/domain/" | tr '\n' ' ' | sed -E -e "s/.*data-domain=\"${domain_env}\"[^<]*data-itemkey=\"([^\"]*).*/\1/")" | ||||||
|  |   _debug gloo_item_key "${gloo_item_key}" | ||||||
|  | 
 | ||||||
|  |   domain_env_url="https://my.cyon.ch/user/environment/setdomain/d/${domain_env}/gik/${gloo_item_key}" | ||||||
|  | 
 | ||||||
|  |   domain_env_response="$(_get "${domain_env_url}")" | ||||||
|  |   _debug domain_env_response "${domain_env_response}" | ||||||
|  | 
 | ||||||
|  |   if ! _cyon_check_if_2fa_missed "${domain_env_response}"; then return 1; fi | ||||||
|  | 
 | ||||||
|  |   domain_env_success="$(printf "%s" "${domain_env_response}" | _egrep_o '"authenticated":\w*' | cut -d : -f 2)" | ||||||
|  | 
 | ||||||
|  |   # Bail if domain environment change fails. | ||||||
|  |   if [ "${domain_env_success}" != "true" ]; then | ||||||
|  |     _err "    $(printf "%s" "${domain_env_response}" | _cyon_get_response_message)" | ||||||
|  |     _err "" | ||||||
|  |     return 1 | ||||||
|  |   fi | ||||||
|  | 
 | ||||||
|  |   _info "    success" | ||||||
|  |   _info "" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | _cyon_add_txt() { | ||||||
|  |   _info "  - Adding DNS TXT entry..." | ||||||
|  | 
 | ||||||
|  |   add_txt_url="https://my.cyon.ch/domain/dnseditor/add-record-async" | ||||||
|  |   add_txt_data="zone=${fulldomain_idn}.&ttl=900&type=TXT&value=${txtvalue}" | ||||||
|  | 
 | ||||||
|  |   add_txt_response="$(_post "$add_txt_data" "$add_txt_url")" | ||||||
|  |   _debug add_txt_response "${add_txt_response}" | ||||||
|  | 
 | ||||||
|  |   if ! _cyon_check_if_2fa_missed "${add_txt_response}"; then return 1; fi | ||||||
|  | 
 | ||||||
|  |   add_txt_message="$(printf "%s" "${add_txt_response}" | _cyon_get_response_message)" | ||||||
|  |   add_txt_status="$(printf "%s" "${add_txt_response}" | _cyon_get_response_status)" | ||||||
|  | 
 | ||||||
|  |   # Bail if adding TXT entry fails. | ||||||
|  |   if [ "${add_txt_status}" != "true" ]; then | ||||||
|  |     _err "    ${add_txt_message}" | ||||||
|  |     _err "" | ||||||
|  |     return 1 | ||||||
|  |   fi | ||||||
|  | 
 | ||||||
|  |   _info "    success (TXT|${fulldomain_idn}.|${txtvalue})" | ||||||
|  |   _info "" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | _cyon_delete_txt() { | ||||||
|  |   _info "  - Deleting DNS TXT entry..." | ||||||
|  | 
 | ||||||
|  |   list_txt_url="https://my.cyon.ch/domain/dnseditor/list-async" | ||||||
|  | 
 | ||||||
|  |   list_txt_response="$(_get "${list_txt_url}" | sed -e 's/data-hash/\\ndata-hash/g')" | ||||||
|  |   _debug list_txt_response "${list_txt_response}" | ||||||
|  | 
 | ||||||
|  |   if ! _cyon_check_if_2fa_missed "${list_txt_response}"; then return 1; fi | ||||||
|  | 
 | ||||||
|  |   # Find and delete all acme challenge entries for the $fulldomain. | ||||||
|  |   _dns_entries="$(printf "%b\n" "${list_txt_response}" | sed -n 's/data-hash=\\"\([^"]*\)\\" data-identifier=\\"\([^"]*\)\\".*/\1 \2/p')" | ||||||
|  | 
 | ||||||
|  |   printf "%s" "${_dns_entries}" | while read -r _hash _identifier; do | ||||||
|  |     dns_type="$(printf "%s" "$_identifier" | cut -d'|' -f1)" | ||||||
|  |     dns_domain="$(printf "%s" "$_identifier" | cut -d'|' -f2)" | ||||||
|  | 
 | ||||||
|  |     if [ "${dns_type}" != "TXT" ] || [ "${dns_domain}" != "${fulldomain_idn}." ]; then | ||||||
|  |       continue | ||||||
|  |     fi | ||||||
|  | 
 | ||||||
|  |     hash_encoded="$(printf "%s" "${_hash}" | _url_encode)" | ||||||
|  |     identifier_encoded="$(printf "%s" "${_identifier}" | _url_encode)" | ||||||
|  | 
 | ||||||
|  |     delete_txt_url="https://my.cyon.ch/domain/dnseditor/delete-record-async" | ||||||
|  |     delete_txt_data="$(printf "%s" "hash=${hash_encoded}&identifier=${identifier_encoded}")" | ||||||
|  | 
 | ||||||
|  |     delete_txt_response="$(_post "$delete_txt_data" "$delete_txt_url")" | ||||||
|  |     _debug delete_txt_response "${delete_txt_response}" | ||||||
|  | 
 | ||||||
|  |     if ! _cyon_check_if_2fa_missed "${delete_txt_response}"; then return 1; fi | ||||||
|  | 
 | ||||||
|  |     delete_txt_message="$(printf "%s" "${delete_txt_response}" | _cyon_get_response_message)" | ||||||
|  |     delete_txt_status="$(printf "%s" "${delete_txt_response}" | _cyon_get_response_status)" | ||||||
|  | 
 | ||||||
|  |     # Skip if deleting TXT entry fails. | ||||||
|  |     if [ "${delete_txt_status}" != "true" ]; then | ||||||
|  |       _err "    ${delete_txt_message} (${_identifier})" | ||||||
|  |     else | ||||||
|  |       _info "    success (${_identifier})" | ||||||
|  |     fi | ||||||
|  |   done | ||||||
|  | 
 | ||||||
|  |   _info "    done" | ||||||
|  |   _info "" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | _cyon_get_response_message() { | ||||||
|  |   _egrep_o '"message":"[^"]*"' | cut -d : -f 2 | tr -d '"' | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | _cyon_get_response_status() { | ||||||
|  |   _egrep_o '"status":\w*' | cut -d : -f 2 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | _cyon_get_response_success() { | ||||||
|  |   _egrep_o '"onSuccess":"[^"]*"' | cut -d : -f 2 | tr -d '"' | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | _cyon_check_if_2fa_missed() { | ||||||
|  |   # Did we miss the 2FA? | ||||||
|  |   if test "${1#*multi_factor_form}" != "${1}"; then | ||||||
|  |     _err "    Missed OTP authentication!" | ||||||
|  |     _err "" | ||||||
|  |     return 1 | ||||||
|  |   fi | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user