Return instead of exit.
Clear OTP secret if environment variable is set to empty. This is for when the 2FA is disabled. Rename `_is_idn` function to `_is_idn_cyon`. Remove usage of curl (except for URL encoding of data). Instead of cleaning up the cookie jar, get rid of it completely and logout of cyon instead.pull/414/head
							parent
							
								
									46b2ee3bae
								
							
						
					
					
						commit
						2698ef6c5f
					
				|  | @ -33,29 +33,23 @@ | |||
| ######## | ||||
| 
 | ||||
| dns_cyon_add() { | ||||
|   _load_credentials | ||||
|   _load_parameters "$@" | ||||
| 
 | ||||
|   _info_header "add" | ||||
|   _login | ||||
|   _domain_env | ||||
|   _add_txt | ||||
|   _cleanup | ||||
| 
 | ||||
|   return 0 | ||||
|   _load_credentials \ | ||||
|   && _load_parameters "$@" \ | ||||
|   && _info_header "add" \ | ||||
|   && _login \ | ||||
|   && _domain_env \ | ||||
|   && _add_txt \ | ||||
|   && _logout | ||||
| } | ||||
| 
 | ||||
| dns_cyon_rm() { | ||||
|   _load_credentials | ||||
|   _load_parameters "$@" | ||||
| 
 | ||||
|   _info_header "delete" | ||||
|   _login | ||||
|   _domain_env | ||||
|   _delete_txt | ||||
|   _cleanup | ||||
| 
 | ||||
|   return 0 | ||||
|   _load_credentials \ | ||||
|   && _load_parameters "$@" \ | ||||
|   && _info_header "delete" \ | ||||
|   && _login \ | ||||
|   && _domain_env \ | ||||
|   && _delete_txt \ | ||||
|   && _logout | ||||
| } | ||||
| 
 | ||||
| ######################### | ||||
|  | @ -65,20 +59,22 @@ dns_cyon_rm() { | |||
| _load_credentials() { | ||||
|   # Convert loaded password to/from base64 as needed. | ||||
|   if [ "${cyon_password_b64}" ]; then | ||||
|     cyon_password="$(printf "%s" "${cyon_password_b64}" | _dbase64)" | ||||
|     cyon_password="$(printf "%s" "${cyon_password_b64}" | _dbase64 "multiline")" | ||||
|   elif [ "${cyon_password}" ]; then | ||||
|     cyon_password_b64="$(printf "%s" "${cyon_password}" | _base64)" | ||||
|   fi | ||||
| 
 | ||||
|   if [ -z "${cyon_username}" ] || [ -z "${cyon_password}" ]; then | ||||
|     # Dummy entries to satify script checker. | ||||
|     cyon_username="" | ||||
|     cyon_password="" | ||||
|     cyon_otp_secret="" | ||||
| 
 | ||||
|     _err "" | ||||
|     _err "You haven't set your cyon.ch login credentials yet." | ||||
|     _err "Please set the required cyon environment variables." | ||||
|     _err "" | ||||
|     exit 1 | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   # Save the login credentials to the account.conf file. | ||||
|  | @ -87,44 +83,52 @@ _load_credentials() { | |||
|   _saveaccountconf cyon_password_b64 "$cyon_password_b64" | ||||
|   if [ ! -z "${cyon_otp_secret}" ]; then | ||||
|     _saveaccountconf cyon_otp_secret "$cyon_otp_secret" | ||||
|   else | ||||
|     _clearaccountconf cyon_otp_secret | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| _is_idn() { | ||||
|   _idn_temp=$(printf "%s" "$1" | tr -d "[0-9a-zA-Z.,-]") | ||||
|   _idn_temp2="$(printf "%s" "$1" | grep -o "xn--")" | ||||
| _is_idn_cyon() { | ||||
|   _idn_temp="$(printf "%s" "${1}" | tr -d "[0-9a-zA-Z.,-_]")" | ||||
|   _idn_temp2="$(printf "%s" "${1}" | grep -o "xn--")" | ||||
|   [ "$_idn_temp" ] || [ "$_idn_temp2" ] | ||||
| } | ||||
| 
 | ||||
| # comment on https://stackoverflow.com/a/10797966 | ||||
| _urlencode_cyon() { | ||||
|   curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | cut -c 3- | ||||
| } | ||||
| 
 | ||||
| _load_parameters() { | ||||
|   # Read the required parameters to add the TXT entry. | ||||
|   fulldomain="$(printf "%s" "$1" | tr '[:upper:]' '[:lower:]')" | ||||
|   fulldomain="$(printf "%s" "${1}" | tr '[:upper:]' '[:lower:]')" | ||||
|   fulldomain_idn="${fulldomain}" | ||||
| 
 | ||||
|   # Special case for IDNs, as cyon needs a domain environment change, | ||||
|   # which uses the "pretty" instead of the punycode version. | ||||
|   if _is_idn "$1"; then | ||||
|   if _is_idn_cyon "${fulldomain}"; then | ||||
|     if ! _exists idn; then | ||||
|       _fail "Please install idn to process IDN names." | ||||
|       _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" | ||||
|   _debug fulldomain "${fulldomain}" | ||||
|   _debug fulldomain_idn "${fulldomain_idn}" | ||||
| 
 | ||||
|   txtvalue="$2" | ||||
|   _debug txtvalue "$txtvalue" | ||||
|   txtvalue="${2}" | ||||
|   _debug txtvalue "${txtvalue}" | ||||
| 
 | ||||
|   # Cookiejar required for login session, as cyon.ch has no official API (yet). | ||||
|   cookiejar=$(tempfile) | ||||
|   _debug cookiejar "$cookiejar" | ||||
|   # This header is required for curl calls. | ||||
|   _H1="X-Requested-With: XMLHttpRequest" | ||||
| } | ||||
| 
 | ||||
| _info_header() { | ||||
|   if [ "$1" = "add" ]; then | ||||
|   if [ "${1}" = "add" ]; then | ||||
|     _info "" | ||||
|     _info "+---------------------------------------------+" | ||||
|     _info "| Adding DNS TXT entry to your cyon.ch domain |" | ||||
|  | @ -132,42 +136,46 @@ _info_header() { | |||
|     _info "" | ||||
|     _info "  * Full Domain: ${fulldomain}" | ||||
|     _info "  * TXT Value:   ${txtvalue}" | ||||
|     _info "  * Cookie Jar:  ${cookiejar}" | ||||
|     _info "" | ||||
|   elif [ "$1" = "delete" ]; then | ||||
|   elif [ "${1}" = "delete" ]; then | ||||
|     _info "" | ||||
|     _info "+-------------------------------------------------+" | ||||
|     _info "| Deleting DNS TXT entry from your cyon.ch domain |" | ||||
|     _info "+-------------------------------------------------+" | ||||
|     _info "" | ||||
|     _info "  * Full Domain: ${fulldomain}" | ||||
|     _info "  * Cookie Jar:  ${cookiejar}" | ||||
|     _info "" | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| _get_cookie_header() { | ||||
|   printf "%s" "$(sed -n 's/Set-\(Cookie:.*cyon=[^;]*\).*/\1/p' "$HTTP_HEADER" | _tail_n 1)" | ||||
| } | ||||
| 
 | ||||
| _login() { | ||||
|   _info "  - Logging in..." | ||||
|   login_response=$(curl \ | ||||
|     "https://my.cyon.ch/auth/index/dologin-async" \ | ||||
|     -s \ | ||||
|     -c "${cookiejar}" \ | ||||
|     -H "X-Requested-With: XMLHttpRequest" \ | ||||
|     --data-urlencode "username=${cyon_username}" \ | ||||
|     --data-urlencode "password=${cyon_password}" \ | ||||
|     --data-urlencode "pathname=/") | ||||
| 
 | ||||
|   username_encoded="$(printf "%s" "${cyon_username}" | _urlencode_cyon)" | ||||
|   password_encoded="$(printf "%s" "${cyon_password}" | _urlencode_cyon)" | ||||
| 
 | ||||
|   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}" | _get_response_success)" != "success" ]; then | ||||
|     _fail "    $(printf "%s" "${login_response}" | _get_response_message)" | ||||
|     _err "    $(printf "%s" "${login_response}" | _get_response_message)" | ||||
|     _err "" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _info "    success" | ||||
| 
 | ||||
|   # NECESSARY!! Load the main page after login, before the OTP check. | ||||
|   curl "https://my.cyon.ch/" -s --compressed -b "${cookiejar}" >/dev/null | ||||
|   # NECESSARY!! Load the main page after login, to get the new cookie. | ||||
|   _H2="$(_get_cookie_header)" | ||||
|   _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. | ||||
| 
 | ||||
|  | @ -176,26 +184,25 @@ _login() { | |||
|     _info "  - Authorising with OTP code..." | ||||
| 
 | ||||
|     if ! _exists oathtool; then | ||||
|       _fail "Please install oathtool to use 2 Factor Authentication." | ||||
|       _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 "${cyon_otp_secret}" 2>/dev/null) | ||||
|     otp_code="$(oathtool --base32 --totp "${cyon_otp_secret}" 2>/dev/null)" | ||||
| 
 | ||||
|     otp_response=$(curl \ | ||||
|       "https://my.cyon.ch/auth/multi-factor/domultifactorauth-async" \ | ||||
|       -s \ | ||||
|       --compressed \ | ||||
|       -b "${cookiejar}" \ | ||||
|       -c "${cookiejar}" \ | ||||
|       -H "X-Requested-With: XMLHttpRequest" \ | ||||
|       -d "totpcode=${otp_code}&pathname=%2F&rememberme=0") | ||||
|     login_otp_url="https://my.cyon.ch/auth/multi-factor/domultifactorauth-async" | ||||
|     login_otp_data="totpcode=${otp_code}&pathname=%2F&rememberme=0" | ||||
| 
 | ||||
|     _debug otp_response "${otp_response}" | ||||
|     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" "${otp_response}" | _get_response_success)" != "success" ]; then | ||||
|       _fail "    $(printf "%s" "${otp_response}" | _get_response_message)" | ||||
|     if [ "$(printf "%s" "${login_otp_response}" | _get_response_success)" != "success" ]; then | ||||
|       _err "    $(printf "%s" "${login_otp_response}" | _get_response_message)" | ||||
|       _err "" | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     _info "    success" | ||||
|  | @ -204,29 +211,36 @@ _login() { | |||
|   _info "" | ||||
| } | ||||
| 
 | ||||
| _logout() { | ||||
|   _info "  - Logging out..." | ||||
| 
 | ||||
|   _get "https://my.cyon.ch/auth/index/dologout" > /dev/null | ||||
| 
 | ||||
|   _info "    success" | ||||
|   _info "" | ||||
| } | ||||
| 
 | ||||
| _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/') | ||||
|   domain_env="$(printf "%s" "${fulldomain}" | sed -E -e 's/.*\.(.*\..*)$/\1/')" | ||||
|   _debug "Changing domain environment to ${domain_env}" | ||||
| 
 | ||||
|   domain_env_response=$(curl \ | ||||
|     "https://my.cyon.ch/user/environment/setdomain/d/${domain_env}/gik/domain%3A${domain_env}" \ | ||||
|     -s \ | ||||
|     --compressed \ | ||||
|     -b "${cookiejar}" \ | ||||
|     -H "X-Requested-With: XMLHttpRequest") | ||||
|   domain_env_url="https://my.cyon.ch/user/environment/setdomain/d/${domain_env}/gik/domain%3A${domain_env}" | ||||
| 
 | ||||
|   domain_env_response="$(_get "${domain_env_url}")" | ||||
|   _debug domain_env_response "${domain_env_response}" | ||||
| 
 | ||||
|   _check_2fa_miss "${domain_env_response}" | ||||
|   if ! _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) | ||||
|   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 | ||||
|     _fail "    $(printf "%s" "${domain_env_response}" | _get_response_message)" | ||||
|     _err "    $(printf "%s" "${domain_env_response}" | _get_response_message)" | ||||
|     _err "" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _info "    success" | ||||
|  | @ -235,47 +249,41 @@ _domain_env() { | |||
| 
 | ||||
| _add_txt() { | ||||
|   _info "  - Adding DNS TXT entry..." | ||||
|   addtxt_response=$(curl \ | ||||
|     "https://my.cyon.ch/domain/dnseditor/add-record-async" \ | ||||
|     -s \ | ||||
|     --compressed \ | ||||
|     -b "${cookiejar}" \ | ||||
|     -H "X-Requested-With: XMLHttpRequest" \ | ||||
|     -d "zone=${fulldomain_idn}.&ttl=900&type=TXT&value=${txtvalue}") | ||||
| 
 | ||||
|   _debug addtxt_response "${addtxt_response}" | ||||
|   add_txt_url="https://my.cyon.ch/domain/dnseditor/add-record-async" | ||||
|   add_txt_data="zone=${fulldomain_idn}.&ttl=900&type=TXT&value=${txtvalue}" | ||||
| 
 | ||||
|   _check_2fa_miss "${addtxt_response}" | ||||
|   add_txt_response="$(_post "$add_txt_data" "$add_txt_url")" | ||||
|   _debug add_txt_response "${add_txt_response}" | ||||
| 
 | ||||
|   addtxt_message=$(printf "%s" "${addtxt_response}" | _get_response_message) | ||||
|   addtxt_status=$(printf "%s" "${addtxt_response}" | _get_response_status) | ||||
|   if ! _check_if_2fa_missed "${add_txt_response}"; then return 1; fi | ||||
| 
 | ||||
|   add_txt_message="$(printf "%s" "${add_txt_response}" | _get_response_message)" | ||||
|   add_txt_status="$(printf "%s" "${add_txt_response}" | _get_response_status)" | ||||
| 
 | ||||
|   # Bail if adding TXT entry fails. | ||||
|   if [ "${addtxt_status}" != "true" ]; then | ||||
|     _fail "    ${addtxt_message}" | ||||
|   if [ "${add_txt_status}" != "true" ]; then | ||||
|     _err "    ${add_txt_message}" | ||||
|     _err "" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _info "    success" | ||||
|   _info "    success (TXT|${fulldomain_idn}.|${txtvalue})" | ||||
|   _info "" | ||||
| } | ||||
| 
 | ||||
| _delete_txt() { | ||||
|   _info "  - Deleting DNS TXT entry..." | ||||
| 
 | ||||
|   list_txt_response=$(curl \ | ||||
|     "https://my.cyon.ch/domain/dnseditor/list-async" \ | ||||
|     -s \ | ||||
|     -b "${cookiejar}" \ | ||||
|     --compressed \ | ||||
|     -H "X-Requested-With: XMLHttpRequest" \ | ||||
|     | sed -e 's/data-hash/\\ndata-hash/g') | ||||
|   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}" | ||||
| 
 | ||||
|   _check_2fa_miss "${list_txt_response}" | ||||
|   if ! _check_if_2fa_missed "${list_txt_response}"; then return 1; fi | ||||
| 
 | ||||
|   # Find and delete all acme challenge entries for the $fulldomain. | ||||
|   _dns_entries=$(printf "%s" "$list_txt_response" | sed -n 's/data-hash=\\"\([^"]*\)\\" data-identifier=\\"\([^"]*\)\\".*/\1 \2/p') | ||||
|   _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)" | ||||
|  | @ -285,21 +293,19 @@ _delete_txt() { | |||
|       continue | ||||
|     fi | ||||
| 
 | ||||
|     delete_txt_response=$(curl \ | ||||
|       "https://my.cyon.ch/domain/dnseditor/delete-record-async" \ | ||||
|       -s \ | ||||
|       --compressed \ | ||||
|       -b "${cookiejar}" \ | ||||
|       -H "X-Requested-With: XMLHttpRequest" \ | ||||
|       --data-urlencode "hash=${_hash}" \ | ||||
|       --data-urlencode "identifier=${_identifier}") | ||||
|     hash_encoded="$(printf "%s" "${_hash}" | _urlencode_cyon)" | ||||
|     identifier_encoded="$(printf "%s" "${_identifier}" | _urlencode_cyon)" | ||||
| 
 | ||||
|     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}" | ||||
| 
 | ||||
|     _check_2fa_miss "${delete_txt_response}" | ||||
|     if ! _check_if_2fa_missed "${delete_txt_response}"; then return 1; fi | ||||
| 
 | ||||
|     delete_txt_message=$(printf "%s" "${delete_txt_response}" | _get_response_message) | ||||
|     delete_txt_status=$(printf "%s" "${delete_txt_response}" | _get_response_status) | ||||
|     delete_txt_message="$(printf "%s" "${delete_txt_response}" | _get_response_message)" | ||||
|     delete_txt_status="$(printf "%s" "${delete_txt_response}" | _get_response_status)" | ||||
| 
 | ||||
|     # Skip if deleting TXT entry fails. | ||||
|     if [ "${delete_txt_status}" != "true" ]; then | ||||
|  | @ -325,23 +331,11 @@ _get_response_success() { | |||
|   _egrep_o '"onSuccess":"[^"]*"' | cut -d : -f 2 | tr -d '"' | ||||
| } | ||||
| 
 | ||||
| _check_2fa_miss() { | ||||
| _check_if_2fa_missed() { | ||||
|   # Did we miss the 2FA? | ||||
|   if test "${1#*multi_factor_form}" != "$1"; then | ||||
|     _fail "    Missed OTP authentication!" | ||||
|   if test "${1#*multi_factor_form}" != "${1}"; then | ||||
|     _err "    Missed OTP authentication!" | ||||
|   _err "" | ||||
|   return 1 | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| _fail() { | ||||
|   _err "$1" | ||||
|   _err "" | ||||
|   _cleanup | ||||
|   exit 1 | ||||
| } | ||||
| 
 | ||||
| _cleanup() { | ||||
|   _info "  - Cleanup." | ||||
|   _debug "Remove cookie jar: ${cookiejar}" | ||||
|   rm "${cookiejar}" 2>/dev/null | ||||
|   _info "" | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Armando Lüscher
						Armando Lüscher