commit
						c459b5eb9d
					
				|  | @ -15,11 +15,11 @@ RUN apk --no-cache add -f \ | |||
|   jq \ | ||||
|   cronie | ||||
| 
 | ||||
| ENV LE_CONFIG_HOME /acme.sh | ||||
| ENV LE_CONFIG_HOME=/acme.sh | ||||
| 
 | ||||
| ARG AUTO_UPGRADE=1 | ||||
| 
 | ||||
| ENV AUTO_UPGRADE $AUTO_UPGRADE | ||||
| ENV AUTO_UPGRADE=$AUTO_UPGRADE | ||||
| 
 | ||||
| #Install | ||||
| COPY ./ /install_acme.sh/ | ||||
|  |  | |||
							
								
								
									
										6
									
								
								acme.sh
								
								
								
								
							
							
						
						
									
										6
									
								
								acme.sh
								
								
								
								
							|  | @ -5818,7 +5818,7 @@ _deploy() { | |||
|         return 1 | ||||
|       fi | ||||
| 
 | ||||
|       if ! $d_command "$_d" "$CERT_KEY_PATH" "$CERT_PATH" "$CA_CERT_PATH" "$CERT_FULLCHAIN_PATH"; then | ||||
|       if ! $d_command "$_d" "$CERT_KEY_PATH" "$CERT_PATH" "$CA_CERT_PATH" "$CERT_FULLCHAIN_PATH" "$CERT_PFX_PATH"; then | ||||
|         _err "Error deploying for domain: $_d" | ||||
|         return 1 | ||||
|       fi | ||||
|  | @ -6061,7 +6061,7 @@ installcronjob() { | |||
|     _script="$(_readlink "$_SCRIPT_")" | ||||
|     _debug _script "$_script" | ||||
|     if [ -f "$_script" ]; then | ||||
|       _info "Usinging the current script from: $_script" | ||||
|       _info "Using the current script from: $_script" | ||||
|       lesh="$_script" | ||||
|     else | ||||
|       _err "Cannot install cronjob, $PROJECT_ENTRY not found." | ||||
|  | @ -6813,7 +6813,7 @@ _send_notify() { | |||
| 
 | ||||
|   _nsource="$NOTIFY_SOURCE" | ||||
|   if [ -z "$_nsource" ]; then | ||||
|     _nsource="$(hostname)" | ||||
|     _nsource="$(uname -n)" | ||||
|   fi | ||||
| 
 | ||||
|   _nsubject="$_nsubject by $_nsource" | ||||
|  |  | |||
|  | @ -18,6 +18,7 @@ docker_deploy() { | |||
|   _ccert="$3" | ||||
|   _cca="$4" | ||||
|   _cfullchain="$5" | ||||
|   _cpfx="$6" | ||||
|   _debug _cdomain "$_cdomain" | ||||
|   _getdeployconf DEPLOY_DOCKER_CONTAINER_LABEL | ||||
|   _debug2 DEPLOY_DOCKER_CONTAINER_LABEL "$DEPLOY_DOCKER_CONTAINER_LABEL" | ||||
|  | @ -88,6 +89,12 @@ docker_deploy() { | |||
|     _savedeployconf DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE" | ||||
|   fi | ||||
| 
 | ||||
|   _getdeployconf DEPLOY_DOCKER_CONTAINER_PFX_FILE | ||||
|   _debug2 DEPLOY_DOCKER_CONTAINER_PFX_FILE "$DEPLOY_DOCKER_CONTAINER_PFX_FILE" | ||||
|   if [ "$DEPLOY_DOCKER_CONTAINER_PFX_FILE" ]; then | ||||
|     _savedeployconf DEPLOY_DOCKER_CONTAINER_PFX_FILE "$DEPLOY_DOCKER_CONTAINER_PFX_FILE" | ||||
|   fi | ||||
| 
 | ||||
|   _getdeployconf DEPLOY_DOCKER_CONTAINER_RELOAD_CMD | ||||
|   _debug2 DEPLOY_DOCKER_CONTAINER_RELOAD_CMD "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" | ||||
|   if [ "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" ]; then | ||||
|  | @ -125,6 +132,12 @@ docker_deploy() { | |||
|     fi | ||||
|   fi | ||||
| 
 | ||||
|   if [ "$DEPLOY_DOCKER_CONTAINER_PFX_FILE" ]; then | ||||
|     if ! _docker_cp "$_cid" "$_cpfx" "$DEPLOY_DOCKER_CONTAINER_PFX_FILE"; then | ||||
|       return 1 | ||||
|     fi | ||||
|   fi | ||||
| 
 | ||||
|   if [ "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" ]; then | ||||
|     _info "Reloading: $DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" | ||||
|     if ! _docker_exec "$_cid" "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD"; then | ||||
|  |  | |||
|  | @ -0,0 +1,120 @@ | |||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| # Deploy certificates to a proxmox backup server using the API. | ||||
| # | ||||
| # Environment variables that can be set are: | ||||
| # `DEPLOY_PROXMOXBS_SERVER`: The hostname of the proxmox backup server. Defaults to | ||||
| #                            _cdomain. | ||||
| # `DEPLOY_PROXMOXBS_SERVER_PORT`: The port number the management interface is on. | ||||
| #                                 Defaults to 8007. | ||||
| # `DEPLOY_PROXMOXBS_USER`: The user we'll connect as. Defaults to root. | ||||
| # `DEPLOY_PROXMOXBS_USER_REALM`: The authentication realm the user authenticates | ||||
| #                                with. Defaults to pam. | ||||
| # `DEPLOY_PROXMOXBS_API_TOKEN_NAME`: The name of the API token created for the | ||||
| #                                    user account. Defaults to acme. | ||||
| # `DEPLOY_PROXMOXBS_API_TOKEN_KEY`: The API token. Required. | ||||
| 
 | ||||
| proxmoxbs_deploy() { | ||||
|   _cdomain="$1" | ||||
|   _ckey="$2" | ||||
|   _ccert="$3" | ||||
|   _cca="$4" | ||||
|   _cfullchain="$5" | ||||
| 
 | ||||
|   _debug _cdomain "$_cdomain" | ||||
|   _debug2 _ckey "$_ckey" | ||||
|   _debug _ccert "$_ccert" | ||||
|   _debug _cca "$_cca" | ||||
|   _debug _cfullchain "$_cfullchain" | ||||
| 
 | ||||
|   # "Sane" defaults. | ||||
|   _getdeployconf DEPLOY_PROXMOXBS_SERVER | ||||
|   if [ -z "$DEPLOY_PROXMOXBS_SERVER" ]; then | ||||
|     _target_hostname="$_cdomain" | ||||
|   else | ||||
|     _target_hostname="$DEPLOY_PROXMOXBS_SERVER" | ||||
|     _savedeployconf DEPLOY_PROXMOXBS_SERVER "$DEPLOY_PROXMOXBS_SERVER" | ||||
|   fi | ||||
|   _debug2 DEPLOY_PROXMOXBS_SERVER "$_target_hostname" | ||||
| 
 | ||||
|   _getdeployconf DEPLOY_PROXMOXBS_SERVER_PORT | ||||
|   if [ -z "$DEPLOY_PROXMOXBS_SERVER_PORT" ]; then | ||||
|     _target_port="8007" | ||||
|   else | ||||
|     _target_port="$DEPLOY_PROXMOXBS_SERVER_PORT" | ||||
|     _savedeployconf DEPLOY_PROXMOXBS_SERVER_PORT "$DEPLOY_PROXMOXBS_SERVER_PORT" | ||||
|   fi | ||||
|   _debug2 DEPLOY_PROXMOXBS_SERVER_PORT "$_target_port" | ||||
| 
 | ||||
|   # Complete URL. | ||||
|   _target_url="https://${_target_hostname}:${_target_port}/api2/json/nodes/localhost/certificates/custom" | ||||
|   _debug TARGET_URL "$_target_url" | ||||
| 
 | ||||
|   # More "sane" defaults. | ||||
|   _getdeployconf DEPLOY_PROXMOXBS_USER | ||||
|   if [ -z "$DEPLOY_PROXMOXBS_USER" ]; then | ||||
|     _proxmoxbs_user="root" | ||||
|   else | ||||
|     _proxmoxbs_user="$DEPLOY_PROXMOXBS_USER" | ||||
|     _savedeployconf DEPLOY_PROXMOXBS_USER "$DEPLOY_PROXMOXBS_USER" | ||||
|   fi | ||||
|   _debug2 DEPLOY_PROXMOXBS_USER "$_proxmoxbs_user" | ||||
| 
 | ||||
|   _getdeployconf DEPLOY_PROXMOXBS_USER_REALM | ||||
|   if [ -z "$DEPLOY_PROXMOXBS_USER_REALM" ]; then | ||||
|     _proxmoxbs_user_realm="pam" | ||||
|   else | ||||
|     _proxmoxbs_user_realm="$DEPLOY_PROXMOXBS_USER_REALM" | ||||
|     _savedeployconf DEPLOY_PROXMOXBS_USER_REALM "$DEPLOY_PROXMOXBS_USER_REALM" | ||||
|   fi | ||||
|   _debug2 DEPLOY_PROXMOXBS_USER_REALM "$_proxmoxbs_user_realm" | ||||
| 
 | ||||
|   _getdeployconf DEPLOY_PROXMOXBS_API_TOKEN_NAME | ||||
|   if [ -z "$DEPLOY_PROXMOXBS_API_TOKEN_NAME" ]; then | ||||
|     _proxmoxbs_api_token_name="acme" | ||||
|   else | ||||
|     _proxmoxbs_api_token_name="$DEPLOY_PROXMOXBS_API_TOKEN_NAME" | ||||
|     _savedeployconf DEPLOY_PROXMOXBS_API_TOKEN_NAME "$DEPLOY_PROXMOXBS_API_TOKEN_NAME" | ||||
|   fi | ||||
|   _debug2 DEPLOY_PROXMOXBS_API_TOKEN_NAME "$_proxmoxbs_api_token_name" | ||||
| 
 | ||||
|   # This is required. | ||||
|   _getdeployconf DEPLOY_PROXMOXBS_API_TOKEN_KEY | ||||
|   if [ -z "$DEPLOY_PROXMOXBS_API_TOKEN_KEY" ]; then | ||||
|     _err "API key not provided." | ||||
|     return 1 | ||||
|   else | ||||
|     _proxmoxbs_api_token_key="$DEPLOY_PROXMOXBS_API_TOKEN_KEY" | ||||
|     _savedeployconf DEPLOY_PROXMOXBS_API_TOKEN_KEY "$DEPLOY_PROXMOXBS_API_TOKEN_KEY" | ||||
|   fi | ||||
|   _debug2 DEPLOY_PROXMOXBS_API_TOKEN_KEY "$_proxmoxbs_api_token_key" | ||||
| 
 | ||||
|   # PBS API Token header value. Used in "Authorization: PBSAPIToken". | ||||
|   _proxmoxbs_header_api_token="${_proxmoxbs_user}@${_proxmoxbs_user_realm}!${_proxmoxbs_api_token_name}:${_proxmoxbs_api_token_key}" | ||||
|   _debug2 "Auth Header" "$_proxmoxbs_header_api_token" | ||||
| 
 | ||||
|   # Ugly. I hate putting heredocs inside functions because heredocs don't | ||||
|   # account for whitespace correctly but it _does_ work and is several times | ||||
|   # cleaner than anything else I had here. | ||||
|   # | ||||
|   # This dumps the json payload to a variable that should be passable to the | ||||
|   # _psot function. | ||||
|   _json_payload=$( | ||||
|     cat <<HEREDOC | ||||
| { | ||||
|   "certificates": "$(tr '\n' ':' <"$_cfullchain" | sed 's/:/\\n/g')", | ||||
|   "key": "$(tr '\n' ':' <"$_ckey" | sed 's/:/\\n/g')", | ||||
|   "node":"localhost", | ||||
|   "restart":true, | ||||
|   "force":true | ||||
| } | ||||
| HEREDOC | ||||
|   ) | ||||
|   _debug2 Payload "$_json_payload" | ||||
| 
 | ||||
|   _info "Push certificates to server" | ||||
|   export HTTPS_INSECURE=1 | ||||
|   export _H1="Authorization: PBSAPIToken=${_proxmoxbs_header_api_token}" | ||||
|   _post "$_json_payload" "$_target_url" "" POST "application/json" | ||||
| 
 | ||||
| } | ||||
|  | @ -186,8 +186,8 @@ synology_dsm_deploy() { | |||
|       if [ -n "$SYNO_USE_TEMP_ADMIN" ]; then | ||||
|         _getdeployconf SYNO_LOCAL_HOSTNAME | ||||
|         _debug SYNO_LOCAL_HOSTNAME "${SYNO_LOCAL_HOSTNAME:-}" | ||||
|         if [ "$SYNO_LOCAL_HOSTNAME" != "1" ] && [ "$SYNO_LOCAL_HOSTNAME" == "$SYNO_HOSTNAME" ]; then | ||||
|           if [ "$SYNO_HOSTNAME" != "localhost" ] && [ "$SYNO_HOSTNAME" != "127.0.0.1" ]; then | ||||
|         if [ "$SYNO_HOSTNAME" != "localhost" ] && [ "$SYNO_HOSTNAME" != "127.0.0.1" ]; then | ||||
|           if [ "$SYNO_LOCAL_HOSTNAME" != "1" ]; then | ||||
|             _err "SYNO_USE_TEMP_ADMIN=1 only support local deployment, though if you are sure that the hostname $SYNO_HOSTNAME is targeting to your **current local machine**, execute 'export SYNO_LOCAL_HOSTNAME=1' then rerun." | ||||
|             return 1 | ||||
|           fi | ||||
|  | @ -320,7 +320,7 @@ synology_dsm_deploy() { | |||
|     _cleardeployconf SYNO_DEVICE_ID | ||||
|     _cleardeployconf SYNO_DEVICE_NAME | ||||
|     _savedeployconf SYNO_USE_TEMP_ADMIN "$SYNO_USE_TEMP_ADMIN" | ||||
|     _savedeployconf SYNO_LOCAL_HOSTNAME "$SYNO_HOSTNAME" | ||||
|     _savedeployconf SYNO_LOCAL_HOSTNAME "$SYNO_LOCAL_HOSTNAME" | ||||
|   else | ||||
|     _savedeployconf SYNO_USERNAME "$SYNO_USERNAME" | ||||
|     _savedeployconf SYNO_PASSWORD "$SYNO_PASSWORD" | ||||
|  | @ -411,7 +411,7 @@ _temp_admin_create() { | |||
|   _username="$1" | ||||
|   _password="$2" | ||||
|   synouser --del "$_username" >/dev/null 2>/dev/null | ||||
|   synouser --add "$_username" "$_password" "" 0 "scruelt@hotmail.com" 0 >/dev/null | ||||
|   synouser --add "$_username" "$_password" "" 0 "" 0 >/dev/null | ||||
| } | ||||
| 
 | ||||
| _temp_admin_cleanup() { | ||||
|  |  | |||
|  | @ -217,7 +217,7 @@ truenas_deploy() { | |||
|           _app_id=$(echo "$_app_id_list" | sed -n "${i}p") | ||||
|           _app_config="$(_post "\"$_app_id\"" "$_api_url/app/config" "" "POST" "application/json")" | ||||
|           # Check if the app use the same certificate TrueNAS web UI | ||||
|           _app_active_cert_config=$(echo "$_app_config" | _json_decode | jq -r ".ix_certificates[\"$_active_cert_id\"]") | ||||
|           _app_active_cert_config=$(echo "$_app_config" | tr -d '\000-\037' | _json_decode | jq -r ".ix_certificates[\"$_active_cert_id\"]") | ||||
|           if [ "$_app_active_cert_config" != "null" ]; then | ||||
|             _info "Updating certificate from $_active_cert_id to $_cert_id for app: $_app_id" | ||||
|             #Replace the old certificate id with the new one in path | ||||
|  |  | |||
|  | @ -0,0 +1,294 @@ | |||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| # TrueNAS deploy script for SCALE/CORE using websocket | ||||
| # It is recommend to use a wildcard certificate | ||||
| # | ||||
| # Websocket Documentation: https://www.truenas.com/docs/api/scale_websocket_api.html | ||||
| # | ||||
| # Tested with TrueNAS Scale - Electric Eel 24.10 | ||||
| # Changes certificate in the following services: | ||||
| #  - Web UI | ||||
| #  - FTP | ||||
| #  - iX Apps | ||||
| # | ||||
| # The following environment variables must be set: | ||||
| # ------------------------------------------------ | ||||
| # | ||||
| # # API KEY | ||||
| # # Use the folowing URL to create a new API token: <TRUENAS_HOSTNAME OR IP>/ui/apikeys | ||||
| # export DEPLOY_TRUENAS_APIKEY="<API_KEY_GENERATED_IN_THE_WEB_UI" | ||||
| # | ||||
| 
 | ||||
| ### Private functions | ||||
| 
 | ||||
| # Call websocket method | ||||
| # Usage: | ||||
| #   _ws_response=$(_ws_call "math.dummycalc" "'{"x": 4, "y": 5}'") | ||||
| #   _info "$_ws_response" | ||||
| # | ||||
| # Output: | ||||
| #   {"z": 9} | ||||
| # | ||||
| # Arguments: | ||||
| #   $@ - midclt arguments for call | ||||
| # | ||||
| # Returns: | ||||
| #   JSON/JOBID | ||||
| _ws_call() { | ||||
|   _debug "_ws_call arg1" "$1" | ||||
|   _debug "_ws_call arg2" "$2" | ||||
|   _debug "_ws_call arg3" "$3" | ||||
|   if [ $# -eq 3 ]; then | ||||
|     _ws_response=$(midclt -K "$DEPLOY_TRUENAS_APIKEY" call "$1" "$2" "$3") | ||||
|   fi | ||||
|   if [ $# -eq 2 ]; then | ||||
|     _ws_response=$(midclt -K "$DEPLOY_TRUENAS_APIKEY" call "$1" "$2") | ||||
|   fi | ||||
|   if [ $# -eq 1 ]; then | ||||
|     _ws_response=$(midclt -K "$DEPLOY_TRUENAS_APIKEY" call "$1") | ||||
|   fi | ||||
|   _debug "_ws_response" "$_ws_response" | ||||
|   printf "%s" "$_ws_response" | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| # Check argument is a number | ||||
| # Usage: | ||||
| # | ||||
| # Output: | ||||
| #   n/a | ||||
| # | ||||
| # Arguments: | ||||
| #   $1 - Anything | ||||
| # | ||||
| # Returns: | ||||
| #   0: true | ||||
| #   1: false | ||||
| _ws_check_jobid() { | ||||
|   case "$1" in | ||||
|   [0-9]*) | ||||
|     return 0 | ||||
|     ;; | ||||
|   esac | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| # Wait for job to finish and return result as JSON | ||||
| # Usage: | ||||
| #   _ws_result=$(_ws_get_job_result "$_ws_jobid") | ||||
| #   _new_certid=$(printf "%s" "$_ws_result" | jq -r '."id"') | ||||
| # | ||||
| # Output: | ||||
| #   JSON result of the job | ||||
| # | ||||
| # Arguments: | ||||
| #   $1 - JobID | ||||
| # | ||||
| # Returns: | ||||
| #   n/a | ||||
| _ws_get_job_result() { | ||||
|   while true; do | ||||
|     sleep 2 | ||||
|     _ws_response=$(_ws_call "core.get_jobs" "[[\"id\", \"=\", $1]]") | ||||
|     if [ "$(printf "%s" "$_ws_response" | jq -r '.[]."state"')" != "RUNNING" ]; then | ||||
|       _ws_result="$(printf "%s" "$_ws_response" | jq '.[]."result"')" | ||||
|       _debug "_ws_result" "$_ws_result" | ||||
|       printf "%s" "$_ws_result" | ||||
|       _ws_error="$(printf "%s" "$_ws_response" | jq '.[]."error"')" | ||||
|       if [ "$_ws_error" != "null" ]; then | ||||
|         _err "Job $1 failed:" | ||||
|         _err "$_ws_error" | ||||
|         return 7 | ||||
|       fi | ||||
|       break | ||||
|     fi | ||||
|   done | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| ######################## | ||||
| ### Public functions ### | ||||
| ######################## | ||||
| 
 | ||||
| # truenas_ws_deploy | ||||
| # | ||||
| # Deploy new certificate to TrueNAS services | ||||
| # | ||||
| # Arguments | ||||
| #  1: Domain | ||||
| #  2: Key-File | ||||
| #  3: Certificate-File | ||||
| #  4: CA-File | ||||
| #  5: FullChain-File | ||||
| # Returns: | ||||
| #  0: Success | ||||
| #  1: Missing API Key | ||||
| #  2: TrueNAS not ready | ||||
| #  3: Not a JobID | ||||
| #  4: FTP cert error | ||||
| #  5: WebUI cert error | ||||
| #  6: Job error | ||||
| #  7: WS call error | ||||
| # 10: No CORE or SCALE detected | ||||
| # | ||||
| truenas_ws_deploy() { | ||||
|   _domain="$1" | ||||
|   _file_key="$2" | ||||
|   _file_cert="$3" | ||||
|   _file_ca="$4" | ||||
|   _file_fullchain="$5" | ||||
|   _debug _domain "$_domain" | ||||
|   _debug _file_key "$_file_key" | ||||
|   _debug _file_cert "$_file_cert" | ||||
|   _debug _file_ca "$_file_ca" | ||||
|   _debug _file_fullchain "$_file_fullchain" | ||||
| 
 | ||||
|   ########## Environment check | ||||
| 
 | ||||
|   _info "Checking environment variables..." | ||||
|   _getdeployconf DEPLOY_TRUENAS_APIKEY | ||||
|   # Check API Key | ||||
|   if [ -z "$DEPLOY_TRUENAS_APIKEY" ]; then | ||||
|     _err "TrueNAS API key not found, please set the DEPLOY_TRUENAS_APIKEY environment variable." | ||||
|     return 1 | ||||
|   fi | ||||
|   _secure_debug2 DEPLOY_TRUENAS_APIKEY "$DEPLOY_TRUENAS_APIKEY" | ||||
|   _info "Environment variables: OK" | ||||
| 
 | ||||
|   ########## Health check | ||||
| 
 | ||||
|   _info "Checking TrueNAS health..." | ||||
|   _ws_response=$(_ws_call "system.ready" | tr '[:lower:]' '[:upper:]') | ||||
|   _ws_ret=$? | ||||
|   if [ $_ws_ret -gt 0 ]; then | ||||
|     _err "Error calling system.ready:" | ||||
|     _err "$_ws_response" | ||||
|     return $_ws_ret | ||||
|   fi | ||||
| 
 | ||||
|   if [ "$_ws_response" != "TRUE" ]; then | ||||
|     _err "TrueNAS is not ready." | ||||
|     _err "Please check environment variables DEPLOY_TRUENAS_APIKEY, DEPLOY_TRUENAS_HOSTNAME and DEPLOY_TRUENAS_PROTOCOL." | ||||
|     _err "Verify API key." | ||||
|     return 2 | ||||
|   fi | ||||
|   _savedeployconf DEPLOY_TRUENAS_APIKEY "$DEPLOY_TRUENAS_APIKEY" | ||||
|   _info "TrueNAS health: OK" | ||||
| 
 | ||||
|   ########## System info | ||||
| 
 | ||||
|   _info "Gather system info..." | ||||
|   _ws_response=$(_ws_call "system.info") | ||||
|   _truenas_system=$(printf "%s" "$_ws_response" | jq -r '."version"' | cut -d '-' -f 2 | tr '[:lower:]' '[:upper:]') | ||||
|   _truenas_version=$(printf "%s" "$_ws_response" | jq -r '."version"' | cut -d '-' -f 3) | ||||
|   _info "TrueNAS system: $_truenas_system" | ||||
|   _info "TrueNAS version: $_truenas_version" | ||||
|   if [ "$_truenas_system" != "SCALE" ] && [ "$_truenas_system" != "CORE" ]; then | ||||
|     _err "Cannot gather TrueNAS system. Nor CORE oder SCALE detected." | ||||
|     return 10 | ||||
|   fi | ||||
| 
 | ||||
|   ########## Gather current certificate | ||||
| 
 | ||||
|   _info "Gather current WebUI certificate..." | ||||
|   _ws_response="$(_ws_call "system.general.config")" | ||||
|   _ui_certificate_id=$(printf "%s" "$_ws_response" | jq -r '."ui_certificate"."id"') | ||||
|   _ui_certificate_name=$(printf "%s" "$_ws_response" | jq -r '."ui_certificate"."name"') | ||||
|   _info "Current WebUI certificate ID: $_ui_certificate_id" | ||||
|   _info "Current WebUI certificate name: $_ui_certificate_name" | ||||
| 
 | ||||
|   ########## Upload new certificate | ||||
| 
 | ||||
|   _info "Upload new certificate..." | ||||
|   _certname="acme_$(_utc_date | tr -d '\-\:' | tr ' ' '_')" | ||||
|   _info "New WebUI certificate name: $_certname" | ||||
|   _debug _certname "$_certname" | ||||
|   _ws_jobid=$(_ws_call "certificate.create" "{\"name\": \"${_certname}\", \"create_type\": \"CERTIFICATE_CREATE_IMPORTED\", \"certificate\": \"$(_json_encode <"$_file_fullchain")\", \"privatekey\": \"$(_json_encode <"$_file_key")\", \"passphrase\": \"\"}") | ||||
|   _debug "_ws_jobid" "$_ws_jobid" | ||||
|   if ! _ws_check_jobid "$_ws_jobid"; then | ||||
|     _err "No JobID returned from websocket method." | ||||
|     return 3 | ||||
|   fi | ||||
|   _ws_result=$(_ws_get_job_result "$_ws_jobid") | ||||
|   _ws_ret=$? | ||||
|   if [ $_ws_ret -gt 0 ]; then | ||||
|     return $_ws_ret | ||||
|   fi | ||||
|   _debug "_ws_result" "$_ws_result" | ||||
|   _new_certid=$(printf "%s" "$_ws_result" | jq -r '."id"') | ||||
|   _info "New certificate ID: $_new_certid" | ||||
| 
 | ||||
|   ########## FTP | ||||
| 
 | ||||
|   _info "Replace FTP certificate..." | ||||
|   _ws_response=$(_ws_call "ftp.update" "{\"ssltls_certificate\": $_new_certid}") | ||||
|   _ftp_certid=$(printf "%s" "$_ws_response" | jq -r '."ssltls_certificate"') | ||||
|   if [ "$_ftp_certid" != "$_new_certid" ]; then | ||||
|     _err "Cannot set FTP certificate." | ||||
|     _debug "_ws_response" "$_ws_response" | ||||
|     return 4 | ||||
|   fi | ||||
| 
 | ||||
|   ########## ix Apps (SCALE only) | ||||
| 
 | ||||
|   if [ "$_truenas_system" = "SCALE" ]; then | ||||
|     _info "Replace app certificates..." | ||||
|     _ws_response=$(_ws_call "app.query") | ||||
|     for _app_name in $(printf "%s" "$_ws_response" | jq -r '.[]."name"'); do | ||||
|       _info "Checking app $_app_name..." | ||||
|       _ws_response=$(_ws_call "app.config" "$_app_name") | ||||
|       if [ "$(printf "%s" "$_ws_response" | jq -r '."network" | has("certificate_id")')" = "true" ]; then | ||||
|         _info "App has certificate option, setup new certificate..." | ||||
|         _info "App will be redeployed after updating the certificate." | ||||
|         _ws_jobid=$(_ws_call "app.update" "$_app_name" "{\"values\": {\"network\": {\"certificate_id\": $_new_certid}}}") | ||||
|         _debug "_ws_jobid" "$_ws_jobid" | ||||
|         if ! _ws_check_jobid "$_ws_jobid"; then | ||||
|           _err "No JobID returned from websocket method." | ||||
|           return 3 | ||||
|         fi | ||||
|         _ws_result=$(_ws_get_job_result "$_ws_jobid") | ||||
|         _ws_ret=$? | ||||
|         if [ $_ws_ret -gt 0 ]; then | ||||
|           return $_ws_ret | ||||
|         fi | ||||
|         _debug "_ws_result" "$_ws_result" | ||||
|         _info "App certificate replaced." | ||||
|       else | ||||
|         _info "App has no certificate option, skipping..." | ||||
|       fi | ||||
|     done | ||||
|   fi | ||||
| 
 | ||||
|   ########## WebUI | ||||
| 
 | ||||
|   _info "Replace WebUI certificate..." | ||||
|   _ws_response=$(_ws_call "system.general.update" "{\"ui_certificate\": $_new_certid}") | ||||
|   _changed_certid=$(printf "%s" "$_ws_response" | jq -r '."ui_certificate"."id"') | ||||
|   if [ "$_changed_certid" != "$_new_certid" ]; then | ||||
|     _err "WebUI certificate change error.." | ||||
|     return 5 | ||||
|   else | ||||
|     _info "WebUI certificate replaced." | ||||
|   fi | ||||
|   _info "Restarting WebUI..." | ||||
|   _ws_response=$(_ws_call "system.general.ui_restart") | ||||
|   _info "Waiting for UI restart..." | ||||
|   sleep 6 | ||||
| 
 | ||||
|   ########## Certificates | ||||
| 
 | ||||
|   _info "Deleting old certificate..." | ||||
|   _ws_jobid=$(_ws_call "certificate.delete" "$_ui_certificate_id") | ||||
|   if ! _ws_check_jobid "$_ws_jobid"; then | ||||
|     _err "No JobID returned from websocket method." | ||||
|     return 3 | ||||
|   fi | ||||
|   _ws_result=$(_ws_get_job_result "$_ws_jobid") | ||||
|   _ws_ret=$? | ||||
|   if [ $_ws_ret -gt 0 ]; then | ||||
|     return $_ws_ret | ||||
|   fi | ||||
| 
 | ||||
|   _info "Have a nice day...bye!" | ||||
| 
 | ||||
| } | ||||
|  | @ -30,7 +30,9 @@ | |||
| # Keystore password (built into Unifi Controller, not a user-set password): | ||||
| #DEPLOY_UNIFI_KEYPASS="aircontrolenterprise" | ||||
| # Command to restart Unifi Controller: | ||||
| #DEPLOY_UNIFI_RELOAD="service unifi restart" | ||||
| # DEPLOY_UNIFI_RELOAD="systemctl restart unifi" | ||||
| # System Properties file location for controller | ||||
| #DEPLOY_UNIFI_SYSTEM_PROPERTIES="/usr/lib/unifi/data/system.properties" | ||||
| # | ||||
| # Settings for Unifi Cloud Key Gen1 (nginx admin pages): | ||||
| # Directory where cloudkey.crt and cloudkey.key live: | ||||
|  | @ -43,7 +45,7 @@ | |||
| # Directory where unifi-core.crt and unifi-core.key live: | ||||
| #DEPLOY_UNIFI_CORE_CONFIG="/data/unifi-core/config/" | ||||
| # Command to restart unifi-core: | ||||
| #DEPLOY_UNIFI_RELOAD="systemctl restart unifi-core" | ||||
| # DEPLOY_UNIFI_OS_RELOAD="systemctl restart unifi-core" | ||||
| # | ||||
| # At least one of DEPLOY_UNIFI_KEYSTORE, DEPLOY_UNIFI_CLOUDKEY_CERTDIR, | ||||
| # or DEPLOY_UNIFI_CORE_CONFIG must exist to receive the deployed certs. | ||||
|  | @ -69,12 +71,16 @@ unifi_deploy() { | |||
|   _getdeployconf DEPLOY_UNIFI_CLOUDKEY_CERTDIR | ||||
|   _getdeployconf DEPLOY_UNIFI_CORE_CONFIG | ||||
|   _getdeployconf DEPLOY_UNIFI_RELOAD | ||||
|   _getdeployconf DEPLOY_UNIFI_SYSTEM_PROPERTIES | ||||
|   _getdeployconf DEPLOY_UNIFI_OS_RELOAD | ||||
| 
 | ||||
|   _debug2 DEPLOY_UNIFI_KEYSTORE "$DEPLOY_UNIFI_KEYSTORE" | ||||
|   _debug2 DEPLOY_UNIFI_KEYPASS "$DEPLOY_UNIFI_KEYPASS" | ||||
|   _debug2 DEPLOY_UNIFI_CLOUDKEY_CERTDIR "$DEPLOY_UNIFI_CLOUDKEY_CERTDIR" | ||||
|   _debug2 DEPLOY_UNIFI_CORE_CONFIG "$DEPLOY_UNIFI_CORE_CONFIG" | ||||
|   _debug2 DEPLOY_UNIFI_RELOAD "$DEPLOY_UNIFI_RELOAD" | ||||
|   _debug2 DEPLOY_UNIFI_OS_RELOAD "$DEPLOY_UNIFI_OS_RELOAD" | ||||
|   _debug2 DEPLOY_UNIFI_SYSTEM_PROPERTIES "$DEPLOY_UNIFI_SYSTEM_PROPERTIES" | ||||
| 
 | ||||
|   # Space-separated list of environments detected and installed: | ||||
|   _services_updated="" | ||||
|  | @ -135,33 +141,53 @@ unifi_deploy() { | |||
|       cp -f "$_import_pkcs12" "$_unifi_keystore" | ||||
|     fi | ||||
| 
 | ||||
|     # correct file ownership according to the directory, the keystore is placed in | ||||
|     _unifi_keystore_dir=$(dirname "${_unifi_keystore}") | ||||
|     _unifi_keystore_dir_owner=$(find "${_unifi_keystore_dir}" -maxdepth 0 -printf '%u\n') | ||||
|     _unifi_keystore_owner=$(find "${_unifi_keystore}" -maxdepth 0 -printf '%u\n') | ||||
|     if ! [ "${_unifi_keystore_owner}" = "${_unifi_keystore_dir_owner}" ]; then | ||||
|       _debug "Changing keystore owner to ${_unifi_keystore_dir_owner}" | ||||
|       chown "$_unifi_keystore_dir_owner" "${_unifi_keystore}" >/dev/null 2>&1 # fail quietly if we're not running as root | ||||
|     fi | ||||
| 
 | ||||
|     # Update unifi service for certificate cipher compatibility | ||||
|     _unifi_system_properties="${DEPLOY_UNIFI_SYSTEM_PROPERTIES:-/usr/lib/unifi/data/system.properties}" | ||||
|     if ${ACME_OPENSSL_BIN:-openssl} pkcs12 \ | ||||
|       -in "$_import_pkcs12" \ | ||||
|       -password pass:aircontrolenterprise \ | ||||
|       -nokeys | ${ACME_OPENSSL_BIN:-openssl} x509 -text \ | ||||
|       -noout | grep -i "signature" | grep -iq ecdsa >/dev/null 2>&1; then | ||||
|       cp -f /usr/lib/unifi/data/system.properties /usr/lib/unifi/data/system.properties_original | ||||
|       _info "Updating system configuration for cipher compatibility." | ||||
|       _info "Saved original system config to /usr/lib/unifi/data/system.properties_original" | ||||
|       sed -i '/unifi\.https\.ciphers/d' /usr/lib/unifi/data/system.properties | ||||
|       echo "unifi.https.ciphers=ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES128-GCM-SHA256" >>/usr/lib/unifi/data/system.properties | ||||
|       sed -i '/unifi\.https\.sslEnabledProtocols/d' /usr/lib/unifi/data/system.properties | ||||
|       echo "unifi.https.sslEnabledProtocols=TLSv1.3,TLSv1.2" >>/usr/lib/unifi/data/system.properties | ||||
|       _info "System configuration updated." | ||||
|       if [ -f "$(dirname "${DEPLOY_UNIFI_KEYSTORE}")/system.properties" ]; then | ||||
|         _unifi_system_properties="$(dirname "${DEPLOY_UNIFI_KEYSTORE}")/system.properties" | ||||
|       else | ||||
|         _unifi_system_properties="/usr/lib/unifi/data/system.properties" | ||||
|       fi | ||||
|       if [ -f "${_unifi_system_properties}" ]; then | ||||
|         cp -f "${_unifi_system_properties}" "${_unifi_system_properties}"_original | ||||
|         _info "Updating system configuration for cipher compatibility." | ||||
|         _info "Saved original system config to ${_unifi_system_properties}_original" | ||||
|         sed -i '/unifi\.https\.ciphers/d' "${_unifi_system_properties}" | ||||
|         echo "unifi.https.ciphers=ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES128-GCM-SHA256" >>"${_unifi_system_properties}" | ||||
|         sed -i '/unifi\.https\.sslEnabledProtocols/d' "${_unifi_system_properties}" | ||||
|         echo "unifi.https.sslEnabledProtocols=TLSv1.3,TLSv1.2" >>"${_unifi_system_properties}" | ||||
|         _info "System configuration updated." | ||||
|       fi | ||||
|     fi | ||||
| 
 | ||||
|     rm "$_import_pkcs12" | ||||
| 
 | ||||
|     # Restarting unifi-core will bring up unifi, doing it out of order results in | ||||
|     # a certificate error, and breaks wifiman. | ||||
|     # Restart if we aren't doing unifi-core, otherwise stop for later restart. | ||||
|     if systemctl -q is-active unifi; then | ||||
|       if [ ! -f "${DEPLOY_UNIFI_CORE_CONFIG:-/data/unifi-core/config}/unifi-core.key" ]; then | ||||
|         _reload_cmd="${_reload_cmd:+$_reload_cmd && }systemctl restart unifi" | ||||
|       else | ||||
|         _reload_cmd="${_reload_cmd:+$_reload_cmd && }systemctl stop unifi" | ||||
|       fi | ||||
|     # Restart if we aren't doing Unifi OS (e.g. unifi-core service), otherwise stop for later restart. | ||||
|     _unifi_reload="${DEPLOY_UNIFI_RELOAD:-systemctl restart unifi}" | ||||
|     if [ ! -f "${DEPLOY_UNIFI_CORE_CONFIG:-/data/unifi-core/config}/unifi-core.key" ]; then | ||||
|       _reload_cmd="${_reload_cmd:+$_reload_cmd && }$_unifi_reload" | ||||
|     else | ||||
|       _info "Stopping Unifi Controller for later restart." | ||||
|       _unifi_stop=$(echo "${_unifi_reload}" | sed -e 's/restart/stop/') | ||||
|       $_unifi_stop | ||||
|       _reload_cmd="${_reload_cmd:+$_reload_cmd && }$_unifi_reload" | ||||
|       _info "Unifi Controller stopped." | ||||
|     fi | ||||
|     _services_updated="${_services_updated} unifi" | ||||
|     _info "Install Unifi Controller certificate success!" | ||||
|  | @ -181,13 +207,24 @@ unifi_deploy() { | |||
|       return 1 | ||||
|     fi | ||||
|     # Cloud Key expects to load the keystore from /etc/ssl/private/unifi.keystore.jks. | ||||
|     # Normally /usr/lib/unifi/data/keystore is a symlink there (so the keystore was | ||||
|     # updated above), but if not, we don't know how to handle this installation: | ||||
|     if ! cmp -s "$_unifi_keystore" "${_cloudkey_certdir}/unifi.keystore.jks"; then | ||||
|       _err "Unsupported Cloud Key configuration: keystore not found at '${_cloudkey_certdir}/unifi.keystore.jks'" | ||||
|       return 1 | ||||
|     # It appears that unifi won't start if this is a symlink, so we'll copy it instead. | ||||
| 
 | ||||
|     # if ! cmp -s "$_unifi_keystore" "${_cloudkey_certdir}/unifi.keystore.jks"; then | ||||
|     #   _err "Unsupported Cloud Key configuration: keystore not found at '${_cloudkey_certdir}/unifi.keystore.jks'" | ||||
|     #   return 1 | ||||
|     # fi | ||||
| 
 | ||||
|     _info "Updating ${_cloudkey_certdir}/unifi.keystore.jks" | ||||
|     if [ -e "${_cloudkey_certdir}/unifi.keystore.jks" ]; then | ||||
|       if [ -L "${_cloudkey_certdir}/unifi.keystore.jks" ]; then | ||||
|         rm -f "${_cloudkey_certdir}/unifi.keystore.jks" | ||||
|       else | ||||
|         mv "${_cloudkey_certdir}/unifi.keystore.jks" "${_cloudkey_certdir}/unifi.keystore.jks_original" | ||||
|       fi | ||||
|     fi | ||||
| 
 | ||||
|     cp "${_unifi_keystore}" "${_cloudkey_certdir}/unifi.keystore.jks" | ||||
| 
 | ||||
|     cat "$_cfullchain" >"${_cloudkey_certdir}/cloudkey.crt" | ||||
|     cat "$_ckey" >"${_cloudkey_certdir}/cloudkey.key" | ||||
|     (cd "$_cloudkey_certdir" && tar -cf cert.tar cloudkey.crt cloudkey.key unifi.keystore.jks) | ||||
|  | @ -215,14 +252,14 @@ unifi_deploy() { | |||
|     # Save the existing certs in case something goes wrong. | ||||
|     cp -f "${_unifi_core_config}"/unifi-core.crt "${_unifi_core_config}"/unifi-core_original.crt | ||||
|     cp -f "${_unifi_core_config}"/unifi-core.key "${_unifi_core_config}"/unifi-core_original.key | ||||
|     _info "Previous certificate and key saved to ${_unifi_core_config}/unifi-core_original.crt/key." | ||||
|     _info "Previous certificate and key saved to ${_unifi_core_config}/unifi-core_original.crt.key." | ||||
| 
 | ||||
|     cat "$_cfullchain" >"${_unifi_core_config}/unifi-core.crt" | ||||
|     cat "$_ckey" >"${_unifi_core_config}/unifi-core.key" | ||||
| 
 | ||||
|     if systemctl -q is-active unifi-core; then | ||||
|       _reload_cmd="${_reload_cmd:+$_reload_cmd && }systemctl restart unifi-core" | ||||
|     fi | ||||
|     _unifi_os_reload="${DEPLOY_UNIFI_OS_RELOAD:-systemctl restart unifi-core}" | ||||
|     _reload_cmd="${_reload_cmd:+$_reload_cmd && }$_unifi_os_reload" | ||||
| 
 | ||||
|     _info "Install UnifiOS certificate success!" | ||||
|     _services_updated="${_services_updated} unifi-core" | ||||
|   elif [ "$DEPLOY_UNIFI_CORE_CONFIG" ]; then | ||||
|  | @ -261,6 +298,8 @@ unifi_deploy() { | |||
|   _savedeployconf DEPLOY_UNIFI_CLOUDKEY_CERTDIR "$DEPLOY_UNIFI_CLOUDKEY_CERTDIR" | ||||
|   _savedeployconf DEPLOY_UNIFI_CORE_CONFIG "$DEPLOY_UNIFI_CORE_CONFIG" | ||||
|   _savedeployconf DEPLOY_UNIFI_RELOAD "$DEPLOY_UNIFI_RELOAD" | ||||
|   _savedeployconf DEPLOY_UNIFI_OS_RELOAD "$DEPLOY_UNIFI_OS_RELOAD" | ||||
|   _savedeployconf DEPLOY_UNIFI_SYSTEM_PROPERTIES "$DEPLOY_UNIFI_SYSTEM_PROPERTIES" | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ Options: | |||
|  AZUREDNS_APPID App ID. App ID of the service principal | ||||
|  AZUREDNS_CLIENTSECRET Client Secret. Secret from creating the service principal | ||||
|  AZUREDNS_MANAGEDIDENTITY Use Managed Identity. Use Managed Identity assigned to a resource instead of a service principal. "true"/"false" | ||||
|  AZUREDNS_BEARERTOKEN Optional Bearer Token. Used instead of service principal credentials or managed identity | ||||
|  AZUREDNS_BEARERTOKEN Bearer Token. Used instead of service principal credentials or managed identity. Optional. | ||||
| ' | ||||
| 
 | ||||
| wiki=https://github.com/acmesh-official/acme.sh/wiki/How-to-use-Azure-DNS | ||||
|  |  | |||
|  | @ -0,0 +1,281 @@ | |||
| #!/usr/bin/env sh | ||||
| # shellcheck disable=SC2034 | ||||
| dns_beget_info='Beget.com | ||||
| Site: Beget.com | ||||
| Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_beget | ||||
| Options: | ||||
|  BEGET_User API user | ||||
|  BEGET_Password API password | ||||
| Issues: github.com/acmesh-official/acme.sh/issues/6200 | ||||
| Author: ARNik arnik@arnik.ru | ||||
| ' | ||||
| 
 | ||||
| Beget_Api="https://api.beget.com/api" | ||||
| 
 | ||||
| ####################  Public functions #################### | ||||
| 
 | ||||
| # Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| # Used to add txt record | ||||
| dns_beget_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
|   _debug "dns_beget_add() $fulldomain $txtvalue" | ||||
|   fulldomain=$(echo "$fulldomain" | _lower_case) | ||||
| 
 | ||||
|   Beget_Username="${Beget_Username:-$(_readaccountconf_mutable Beget_Username)}" | ||||
|   Beget_Password="${Beget_Password:-$(_readaccountconf_mutable Beget_Password)}" | ||||
| 
 | ||||
|   if [ -z "$Beget_Username" ] || [ -z "$Beget_Password" ]; then | ||||
|     Beget_Username="" | ||||
|     Beget_Password="" | ||||
|     _err "You must export variables: Beget_Username, and Beget_Password" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   #save the credentials to the account conf file. | ||||
|   _saveaccountconf_mutable Beget_Username "$Beget_Username" | ||||
|   _saveaccountconf_mutable Beget_Password "$Beget_Password" | ||||
| 
 | ||||
|   _info "Prepare subdomain." | ||||
|   if ! _prepare_subdomain "$fulldomain"; then | ||||
|     _err "Can't prepare subdomain." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _info "Get domain records" | ||||
|   data="{\"fqdn\":\"$fulldomain\"}" | ||||
|   res=$(_api_call "$Beget_Api/dns/getData" "$data") | ||||
|   if ! _is_api_reply_ok "$res"; then | ||||
|     _err "Can't get domain records." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _info "Add new TXT record" | ||||
|   data="{\"fqdn\":\"$fulldomain\",\"records\":{" | ||||
|   data=${data}$(_parce_records "$res" "A") | ||||
|   data=${data}$(_parce_records "$res" "AAAA") | ||||
|   data=${data}$(_parce_records "$res" "CAA") | ||||
|   data=${data}$(_parce_records "$res" "MX") | ||||
|   data=${data}$(_parce_records "$res" "SRV") | ||||
|   data=${data}$(_parce_records "$res" "TXT") | ||||
|   data=$(echo "$data" | sed 's/,$//') | ||||
|   data=${data}'}}' | ||||
| 
 | ||||
|   str=$(_txt_to_dns_json "$txtvalue") | ||||
|   data=$(_add_record "$data" "TXT" "$str") | ||||
| 
 | ||||
|   res=$(_api_call "$Beget_Api/dns/changeRecords" "$data") | ||||
|   if ! _is_api_reply_ok "$res"; then | ||||
|     _err "Can't change domain records." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| # Usage: fulldomain txtvalue | ||||
| # Used to remove the txt record after validation | ||||
| dns_beget_rm() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
|   _debug "dns_beget_rm() $fulldomain $txtvalue" | ||||
|   fulldomain=$(echo "$fulldomain" | _lower_case) | ||||
| 
 | ||||
|   Beget_Username="${Beget_Username:-$(_readaccountconf_mutable Beget_Username)}" | ||||
|   Beget_Password="${Beget_Password:-$(_readaccountconf_mutable Beget_Password)}" | ||||
| 
 | ||||
|   _info "Get current domain records" | ||||
|   data="{\"fqdn\":\"$fulldomain\"}" | ||||
|   res=$(_api_call "$Beget_Api/dns/getData" "$data") | ||||
|   if ! _is_api_reply_ok "$res"; then | ||||
|     _err "Can't get domain records." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _info "Remove TXT record" | ||||
|   data="{\"fqdn\":\"$fulldomain\",\"records\":{" | ||||
|   data=${data}$(_parce_records "$res" "A") | ||||
|   data=${data}$(_parce_records "$res" "AAAA") | ||||
|   data=${data}$(_parce_records "$res" "CAA") | ||||
|   data=${data}$(_parce_records "$res" "MX") | ||||
|   data=${data}$(_parce_records "$res" "SRV") | ||||
|   data=${data}$(_parce_records "$res" "TXT") | ||||
|   data=$(echo "$data" | sed 's/,$//') | ||||
|   data=${data}'}}' | ||||
| 
 | ||||
|   str=$(_txt_to_dns_json "$txtvalue") | ||||
|   data=$(_rm_record "$data" "$str") | ||||
| 
 | ||||
|   res=$(_api_call "$Beget_Api/dns/changeRecords" "$data") | ||||
|   if ! _is_api_reply_ok "$res"; then | ||||
|     _err "Can't change domain records." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions below #################### | ||||
| 
 | ||||
| # Create subdomain if needed | ||||
| # Usage: _prepare_subdomain [fulldomain] | ||||
| _prepare_subdomain() { | ||||
|   fulldomain=$1 | ||||
| 
 | ||||
|   _info "Detect the root zone" | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "invalid domain" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug _domain_id "$_domain_id" | ||||
|   _debug _sub_domain "$_sub_domain" | ||||
|   _debug _domain "$_domain" | ||||
| 
 | ||||
|   if [ -z "$_sub_domain" ]; then | ||||
|     _debug "$fulldomain is a root domain." | ||||
|     return 0 | ||||
|   fi | ||||
| 
 | ||||
|   _info "Get subdomain list" | ||||
|   res=$(_api_call "$Beget_Api/domain/getSubdomainList") | ||||
|   if ! _is_api_reply_ok "$res"; then | ||||
|     _err "Can't get subdomain list." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if _contains "$res" "\"fqdn\":\"$fulldomain\""; then | ||||
|     _debug "Subdomain $fulldomain already exist." | ||||
|     return 0 | ||||
|   fi | ||||
| 
 | ||||
|   _info "Subdomain $fulldomain does not exist. Let's create one." | ||||
|   data="{\"subdomain\":\"$_sub_domain\",\"domain_id\":$_domain_id}" | ||||
|   res=$(_api_call "$Beget_Api/domain/addSubdomainVirtual" "$data") | ||||
|   if ! _is_api_reply_ok "$res"; then | ||||
|     _err "Can't create subdomain." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _debug "Cleanup subdomen records" | ||||
|   data="{\"fqdn\":\"$fulldomain\",\"records\":{}}" | ||||
|   res=$(_api_call "$Beget_Api/dns/changeRecords" "$data") | ||||
|   if ! _is_api_reply_ok "$res"; then | ||||
|     _debug "Can't cleanup $fulldomain records." | ||||
|   fi | ||||
| 
 | ||||
|   data="{\"fqdn\":\"www.$fulldomain\",\"records\":{}}" | ||||
|   res=$(_api_call "$Beget_Api/dns/changeRecords" "$data") | ||||
|   if ! _is_api_reply_ok "$res"; then | ||||
|     _debug "Can't cleanup www.$fulldomain records." | ||||
|   fi | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| # Usage: _get_root _acme-challenge.www.domain.com | ||||
| #returns | ||||
| # _sub_domain=_acme-challenge.www | ||||
| # _domain=domain.com | ||||
| # _domain_id=32436365 | ||||
| _get_root() { | ||||
|   fulldomain=$1 | ||||
|   i=1 | ||||
|   p=1 | ||||
| 
 | ||||
|   _debug "Get domain list" | ||||
|   res=$(_api_call "$Beget_Api/domain/getList") | ||||
|   if ! _is_api_reply_ok "$res"; then | ||||
|     _err "Can't get domain list." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   while true; do | ||||
|     h=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-100) | ||||
|     _debug h "$h" | ||||
| 
 | ||||
|     if [ -z "$h" ]; then | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     if _contains "$res" "$h"; then | ||||
|       _domain_id=$(echo "$res" | _egrep_o "\"id\":[0-9]*,\"fqdn\":\"$h\"" | cut -d , -f1 | cut -d : -f2) | ||||
|       if [ "$_domain_id" ]; then | ||||
|         if [ "$h" != "$fulldomain" ]; then | ||||
|           _sub_domain=$(echo "$fulldomain" | cut -d . -f 1-"$p") | ||||
|         else | ||||
|           _sub_domain="" | ||||
|         fi | ||||
|         _domain=$h | ||||
|         return 0 | ||||
|       fi | ||||
|       return 1 | ||||
|     fi | ||||
|     p="$i" | ||||
|     i=$(_math "$i" + 1) | ||||
|   done | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| # Parce DNS records from json string | ||||
| # Usage: _parce_records [j_str] [record_name] | ||||
| _parce_records() { | ||||
|   j_str=$1 | ||||
|   record_name=$2 | ||||
|   res="\"$record_name\":[" | ||||
|   res=${res}$(echo "$j_str" | _egrep_o "\"$record_name\":\[.*" | cut -d '[' -f2 | cut -d ']' -f1) | ||||
|   res=${res}"]," | ||||
|   echo "$res" | ||||
| } | ||||
| 
 | ||||
| # Usage: _add_record [data] [record_name] [record_data] | ||||
| _add_record() { | ||||
|   data=$1 | ||||
|   record_name=$2 | ||||
|   record_data=$3 | ||||
|   echo "$data" | sed "s/\"$record_name\":\[/\"$record_name\":\[$record_data,/" | sed "s/,\]/\]/" | ||||
| } | ||||
| 
 | ||||
| # Usage: _rm_record [data] [record_data] | ||||
| _rm_record() { | ||||
|   data=$1 | ||||
|   record_data=$2 | ||||
|   echo "$data" | sed "s/$record_data//g" | sed "s/,\+/,/g" | | ||||
|     sed "s/{,/{/g" | sed "s/,}/}/g" | | ||||
|     sed "s/\[,/\[/g" | sed "s/,\]/\]/g" | ||||
| } | ||||
| 
 | ||||
| _txt_to_dns_json() { | ||||
|   echo "{\"ttl\":600,\"txtdata\":\"$1\"}" | ||||
| } | ||||
| 
 | ||||
| # Usage: _api_call [api_url] [input_data] | ||||
| _api_call() { | ||||
|   api_url="$1" | ||||
|   input_data="$2" | ||||
| 
 | ||||
|   _debug "_api_call $api_url" | ||||
|   _debug "Request: $input_data" | ||||
| 
 | ||||
|   # res=$(curl -s -L -D ./http.header \ | ||||
|   # "$api_url" \ | ||||
|   # --data-urlencode login=$Beget_Username \ | ||||
|   # --data-urlencode passwd=$Beget_Password \ | ||||
|   # --data-urlencode input_format=json \ | ||||
|   # --data-urlencode output_format=json \ | ||||
|   # --data-urlencode "input_data=$input_data") | ||||
| 
 | ||||
|   url="$api_url?login=$Beget_Username&passwd=$Beget_Password&input_format=json&output_format=json" | ||||
|   if [ -n "$input_data" ]; then | ||||
|     url=${url}"&input_data=" | ||||
|     url=${url}$(echo "$input_data" | _url_encode) | ||||
|   fi | ||||
|   res=$(_get "$url") | ||||
| 
 | ||||
|   _debug "Reply: $res" | ||||
|   echo "$res" | ||||
| } | ||||
| 
 | ||||
| # Usage: _is_api_reply_ok [api_reply] | ||||
| _is_api_reply_ok() { | ||||
|   _contains "$1" '^{"status":"success","answer":{"status":"success","result":.*}}$' | ||||
| } | ||||
|  | @ -215,10 +215,8 @@ _cyon_change_domain_env() { | |||
| 
 | ||||
|   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 | ||||
|   if [ "$(printf "%s" "${domain_env_response}" | _cyon_get_environment_change_status)" != "true" ]; then | ||||
|     _err "    $(printf "%s" "${domain_env_response}" | _cyon_get_response_message)" | ||||
|     _err "" | ||||
|     return 1 | ||||
|  | @ -232,7 +230,7 @@ _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_data="name=${fulldomain_idn}.&ttl=900&type=TXT&dnscontent=${txtvalue}" | ||||
| 
 | ||||
|   add_txt_response="$(_post "$add_txt_data" "$add_txt_url")" | ||||
|   _debug add_txt_response "${add_txt_response}" | ||||
|  | @ -241,9 +239,10 @@ _cyon_add_txt() { | |||
| 
 | ||||
|   add_txt_message="$(printf "%s" "${add_txt_response}" | _cyon_get_response_message)" | ||||
|   add_txt_status="$(printf "%s" "${add_txt_response}" | _cyon_get_response_status)" | ||||
|   add_txt_validation="$(printf "%s" "${add_txt_response}" | _cyon_get_validation_status)" | ||||
| 
 | ||||
|   # Bail if adding TXT entry fails. | ||||
|   if [ "${add_txt_status}" != "true" ]; then | ||||
|   if [ "${add_txt_status}" != "true" ] || [ "${add_txt_validation}" != "true" ]; then | ||||
|     _err "    ${add_txt_message}" | ||||
|     _err "" | ||||
|     return 1 | ||||
|  | @ -305,13 +304,21 @@ _cyon_get_response_message() { | |||
| } | ||||
| 
 | ||||
| _cyon_get_response_status() { | ||||
|   _egrep_o '"status":\w*' | cut -d : -f 2 | ||||
|   _egrep_o '"status":[a-zA-z0-9]*' | cut -d : -f 2 | ||||
| } | ||||
| 
 | ||||
| _cyon_get_validation_status() { | ||||
|   _egrep_o '"valid":[a-zA-z0-9]*' | cut -d : -f 2 | ||||
| } | ||||
| 
 | ||||
| _cyon_get_response_success() { | ||||
|   _egrep_o '"onSuccess":"[^"]*"' | cut -d : -f 2 | tr -d '"' | ||||
| } | ||||
| 
 | ||||
| _cyon_get_environment_change_status() { | ||||
|   _egrep_o '"authenticated":[a-zA-z0-9]*' | cut -d : -f 2 | ||||
| } | ||||
| 
 | ||||
| _cyon_check_if_2fa_missed() { | ||||
|   # Did we miss the 2FA? | ||||
|   if test "${1#*multi_factor_form}" != "${1}"; then | ||||
|  |  | |||
|  | @ -0,0 +1,38 @@ | |||
| #!/usr/bin/env sh | ||||
| # shellcheck disable=SC2034 | ||||
| dns_he_ddns_info='Hurricane Electric HE.net DDNS | ||||
| Site: dns.he.net | ||||
| Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_he_ddns | ||||
| Options: | ||||
|  HE_DDNS_KEY The DDNS key | ||||
| Author: Markku Leiniö | ||||
| ' | ||||
| 
 | ||||
| HE_DDNS_URL="https://dyn.dns.he.net/nic/update" | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #Usage: dns_he_ddns_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_he_ddns_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
|   HE_DDNS_KEY="${HE_DDNS_KEY:-$(_readaccountconf_mutable HE_DDNS_KEY)}" | ||||
|   if [ -z "$HE_DDNS_KEY" ]; then | ||||
|     HE_DDNS_KEY="" | ||||
|     _err "You didn't specify a DDNS key for accessing the TXT record in HE API." | ||||
|     return 1 | ||||
|   fi | ||||
|   #Save the DDNS key to the account conf file. | ||||
|   _saveaccountconf_mutable HE_DDNS_KEY "$HE_DDNS_KEY" | ||||
| 
 | ||||
|   _info "Using Hurricane Electric DDNS API" | ||||
|   _debug fulldomain "$fulldomain" | ||||
|   _debug txtvalue "$txtvalue" | ||||
| 
 | ||||
|   response="$(_post "hostname=$fulldomain&password=$HE_DDNS_KEY&txt=$txtvalue" "$HE_DDNS_URL")" | ||||
|   _info "Response: $response" | ||||
|   _contains "$response" "good" && return 0 || return 1 | ||||
| } | ||||
| 
 | ||||
| # dns_he_ddns_rm() is not implemented because the API call always updates the | ||||
| # contents of the existing record (that the API key gives access to). | ||||
|  | @ -1,13 +1,13 @@ | |||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| # Created by Laraveluser | ||||
| # | ||||
| # Pass credentials before "acme.sh --issue --dns dns_limacity ..." | ||||
| # -- | ||||
| # export LIMACITY_APIKEY="<API-KEY>" | ||||
| # -- | ||||
| # | ||||
| # Pleas note: APIKEY must have following roles: dns.admin, domains.reader | ||||
| # shellcheck disable=SC2034 | ||||
| dns_limacity_info='lima-city.de | ||||
| Site: www.lima-city.de | ||||
| Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_limacity | ||||
| Options: | ||||
|  LIMACITY_APIKEY API Key. Note: The API Key must have following roles: dns.admin, domains.reader | ||||
| Issues: github.com/acmesh-official/acme.sh/issues/4758 | ||||
| Author: @Laraveluser | ||||
| ' | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,215 @@ | |||
| #!/usr/bin/env sh | ||||
| # shellcheck disable=SC2034 | ||||
| dns_mijnhost_info='mijn.host | ||||
| Domains: mijn.host | ||||
| Site: mijn.host | ||||
| Docs: https://mijn.host/api/doc/ | ||||
| Issues: https://github.com/acmesh-official/acme.sh/issues/6177 | ||||
| Author: peterv99 | ||||
| Options: | ||||
|  MIJNHOST_API_KEY API Key | ||||
| ' | ||||
| 
 | ||||
| ########  Public functions ###################### Constants for your mijn-host API | ||||
| MIJNHOST_API="https://mijn.host/api/v2" | ||||
| 
 | ||||
| # Add TXT record for domain verification | ||||
| dns_mijnhost_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   MIJNHOST_API_KEY="${MIJNHOST_API_KEY:-$(_readaccountconf_mutable MIJNHOST_API_KEY)}" | ||||
|   if [ -z "$MIJNHOST_API_KEY" ]; then | ||||
|     MIJNHOST_API_KEY="" | ||||
|     _err "You haven't specified your mijn-host API key yet." | ||||
|     _err "Please add MIJNHOST_API_KEY to the env." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   # Save the API key for future use | ||||
|   _saveaccountconf_mutable MIJNHOST_API_KEY "$MIJNHOST_API_KEY" | ||||
| 
 | ||||
|   _debug "First detect the root zone" | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "Invalid domain" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _debug2 _sub_domain "$_sub_domain" | ||||
|   _debug2 _domain "$_domain" | ||||
|   _debug "Adding DNS record" "${fulldomain}." | ||||
| 
 | ||||
|   # Construct the API URL | ||||
|   api_url="$MIJNHOST_API/domains/$_domain/dns" | ||||
| 
 | ||||
|   # Getting previous records | ||||
|   _mijnhost_rest GET "$api_url" "" | ||||
| 
 | ||||
|   if [ "$_code" != "200" ]; then | ||||
|     _err "Error getting current DNS enties ($_code)" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   records=$(echo "$response" | _egrep_o '"records":\[.*\]' | sed 's/"records"://') | ||||
| 
 | ||||
|   _debug2 "Current records" "$records" | ||||
| 
 | ||||
|   # Build the payload for the API | ||||
|   data="{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"value\":\"$txtvalue\",\"ttl\":300}" | ||||
| 
 | ||||
|   _debug2 "Record to add" "$data" | ||||
| 
 | ||||
|   # Updating the records | ||||
|   updated_records=$(echo "$records" | sed -E "s/\]( *$)/,$data\]/") | ||||
| 
 | ||||
|   _debug2 "Updated records" "$updated_records" | ||||
| 
 | ||||
|   # data | ||||
|   data="{\"records\": $updated_records}" | ||||
| 
 | ||||
|   _mijnhost_rest PUT "$api_url" "$data" | ||||
| 
 | ||||
|   if [ "$_code" = "200" ]; then | ||||
|     _info "DNS record succesfully added." | ||||
|     return 0 | ||||
|   else | ||||
|     _err "Error adding DNS record ($_code)." | ||||
|     return 1 | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| # Remove TXT record after verification | ||||
| dns_mijnhost_rm() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   MIJNHOST_API_KEY="${MIJNHOST_API_KEY:-$(_readaccountconf_mutable MIJNHOST_API_KEY)}" | ||||
|   if [ -z "$MIJNHOST_API_KEY" ]; then | ||||
|     MIJNHOST_API_KEY="" | ||||
|     _err "You haven't specified your mijn-host API key yet." | ||||
|     _err "Please add MIJNHOST_API_KEY to the env." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _debug "Detecting root zone for" "${fulldomain}." | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "Invalid domain" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _debug "Removing DNS record for TXT value" "${txtvalue}." | ||||
| 
 | ||||
|   # Construct the API URL | ||||
|   api_url="$MIJNHOST_API/domains/$_domain/dns" | ||||
| 
 | ||||
|   # Get current records | ||||
|   _mijnhost_rest GET "$api_url" "" | ||||
| 
 | ||||
|   if [ "$_code" != "200" ]; then | ||||
|     _err "Error getting current DNS enties ($_code)" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _debug2 "Get current records response:" "$response" | ||||
| 
 | ||||
|   records=$(echo "$response" | _egrep_o '"records":\[.*\]' | sed 's/"records"://') | ||||
| 
 | ||||
|   _debug2 "Current records:" "$records" | ||||
| 
 | ||||
|   updated_records=$(echo "$records" | sed -E "s/\{[^}]*\"value\":\"$txtvalue\"[^}]*\},?//g" | sed 's/,]/]/g') | ||||
| 
 | ||||
|   _debug2 "Updated records:" "$updated_records" | ||||
| 
 | ||||
|   # Build the new payload | ||||
|   data="{\"records\": $updated_records}" | ||||
| 
 | ||||
|   # Use the _put method to update the records | ||||
|   _mijnhost_rest PUT "$api_url" "$data" | ||||
| 
 | ||||
|   if [ "$_code" = "200" ]; then | ||||
|     _info "DNS record removed successfully." | ||||
|     return 0 | ||||
|   else | ||||
|     _err "Error removing DNS record ($_code)." | ||||
|     return 1 | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| # Helper function to detect the root zone | ||||
| _get_root() { | ||||
|   domain=$1 | ||||
| 
 | ||||
|   # Get current records | ||||
|   _debug "Getting current domains" | ||||
|   _mijnhost_rest GET "$MIJNHOST_API/domains" "" | ||||
| 
 | ||||
|   if [ "$_code" != "200" ]; then | ||||
|     _err "error getting current domains ($_code)" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   # Extract root domains from response | ||||
|   rootDomains=$(echo "$response" | _egrep_o '"domain":"[^"]*"' | sed -E 's/"domain":"([^"]*)"/\1/') | ||||
|   _debug "Root domains:" "$rootDomains" | ||||
| 
 | ||||
|   for rootDomain in $rootDomains; do | ||||
|     if _contains "$domain" "$rootDomain"; then | ||||
|       _domain="$rootDomain" | ||||
|       _sub_domain=$(echo "$domain" | sed "s/.$rootDomain//g") | ||||
|       _debug "Found root domain" "$_domain" "and subdomain" "$_sub_domain" "for" "$domain" | ||||
|       return 0 | ||||
|     fi | ||||
|   done | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| # Helper function for rest calls | ||||
| _mijnhost_rest() { | ||||
|   m=$1 | ||||
|   ep="$2" | ||||
|   data="$3" | ||||
| 
 | ||||
|   MAX_REQUEST_RETRY_TIMES=15 | ||||
|   _request_retry_times=0 | ||||
|   _retry_sleep=5 #Initial sleep time in seconds. | ||||
| 
 | ||||
|   while [ "${_request_retry_times}" -lt "$MAX_REQUEST_RETRY_TIMES" ]; do | ||||
|     _debug2 _request_retry_times "$_request_retry_times" | ||||
|     export _H1="API-Key: $MIJNHOST_API_KEY" | ||||
|     export _H2="Content-Type: application/json" | ||||
|     # clear headers from previous request to avoid getting wrong http code on timeouts | ||||
|     : >"$HTTP_HEADER" | ||||
|     _debug "$ep" | ||||
|     if [ "$m" != "GET" ]; then | ||||
|       _debug2 "data $data" | ||||
|       response="$(_post "$data" "$ep" "" "$m")" | ||||
|     else | ||||
|       response="$(_get "$ep")" | ||||
|     fi | ||||
|     _ret="$?" | ||||
|     _debug2 "response $response" | ||||
|     _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 API token, maybe it is expired? | ||||
|       _err "Access denied. Invalid API token." | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     if [ "$_ret" != "0" ] || [ -z "$_code" ] || [ "$_code" = "400" ] || _contains "$response" "DNS records not managed by mijn.host"; then #Sometimes API errors out | ||||
|       _request_retry_times="$(_math "$_request_retry_times" + 1)" | ||||
|       _info "REST call error $_code retrying $ep in ${_retry_sleep}s" | ||||
|       _sleep "$_retry_sleep" | ||||
|       _retry_sleep="$(_math "$_retry_sleep" \* 2)" | ||||
|       continue | ||||
|     fi | ||||
|     break | ||||
|   done | ||||
|   if [ "$_request_retry_times" = "$MAX_REQUEST_RETRY_TIMES" ]; then | ||||
|     _err "Error mijn.host API call was retried $MAX_REQUEST_RETRY_TIMES times." | ||||
|     _err "Calling $ep failed." | ||||
|     return 1 | ||||
|   fi | ||||
|   response="$(echo "$response" | _normalizeJson)" | ||||
|   return 0 | ||||
| } | ||||
|  | @ -1,12 +1,14 @@ | |||
| #!/usr/bin/env sh | ||||
| # shellcheck disable=SC2034 | ||||
| dns_myapi_info='Custom API Example | ||||
|  A sample custom DNS API script. | ||||
| Domains: example.com | ||||
|  A sample custom DNS API script description. | ||||
| Domains: example.com example.net | ||||
| Site: github.com/acmesh-official/acme.sh/wiki/DNS-API-Dev-Guide | ||||
| Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_duckdns | ||||
| Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_myapi | ||||
| Options: | ||||
|  MYAPI_Token API Token. Get API Token from https://example.com/api/. Optional. | ||||
|  MYAPI_Token API Token. Get API Token from https://example.com/api/ | ||||
|  MYAPI_Variable2 Option 2. Default "default value". | ||||
|  MYAPI_Variable2 Option 3. Optional. | ||||
| Issues: github.com/acmesh-official/acme.sh | ||||
| Author: Neil Pang <neilgit@neilpang.com> | ||||
| ' | ||||
|  |  | |||
|  | @ -19,7 +19,7 @@ client="" | |||
| 
 | ||||
| dns_netcup_add() { | ||||
|   _debug NC_Apikey "$NC_Apikey" | ||||
|   login | ||||
|   _login | ||||
|   if [ "$NC_Apikey" = "" ] || [ "$NC_Apipw" = "" ] || [ "$NC_CID" = "" ]; then | ||||
|     _err "No Credentials given" | ||||
|     return 1 | ||||
|  | @ -61,7 +61,7 @@ dns_netcup_add() { | |||
| } | ||||
| 
 | ||||
| dns_netcup_rm() { | ||||
|   login | ||||
|   _login | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|  | @ -125,7 +125,7 @@ dns_netcup_rm() { | |||
|   logout | ||||
| } | ||||
| 
 | ||||
| login() { | ||||
| _login() { | ||||
|   tmp=$(_post "{\"action\": \"login\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apipassword\": \"$NC_Apipw\", \"customernumber\": \"$NC_CID\"}}" "$end" "" "POST") | ||||
|   sid=$(echo "$tmp" | tr '{}' '\n' | grep apisessionid | cut -d '"' -f 4) | ||||
|   _debug "$tmp" | ||||
|  |  | |||
|  | @ -4,8 +4,8 @@ dns_omglol_info='omg.lol | |||
| Site: omg.lol | ||||
| Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_omglol | ||||
| Options: | ||||
|  OMG_ApiKey API Key from omg.lol. This is accessible from the bottom of the account page at https://home.omg.lol/account | ||||
|  OMG_Address This is your omg.lol address, without the preceding @ - you can see your list on your dashboard at https://home.omg.lol/dashboard | ||||
|  OMG_ApiKey API Key. This is accessible from the bottom of the account page at https://home.omg.lol/account | ||||
|  OMG_Address Address. This is your omg.lol address, without the preceding @ - you can see your list on your dashboard at https://home.omg.lol/dashboard | ||||
| Issues: github.com/acmesh-official/acme.sh/issues/5299 | ||||
| Author: @Kholin <kholin+acme.omglolapi@omg.lol> | ||||
| ' | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
| # shellcheck disable=SC2034 | ||||
| dns_openprovider_info='OpenProvider.eu | ||||
| Site: OpenProvider.eu | ||||
| Domains: OpenProvider.com | ||||
| Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_openprovider | ||||
| Options: | ||||
|  OPENPROVIDER_USER Username | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ Options: | |||
|  PDNS_Url API URL. E.g. "http://ns.example.com:8081" | ||||
|  PDNS_ServerId Server ID. E.g. "localhost" | ||||
|  PDNS_Token API Token | ||||
|  PDNS_Ttl=60 Domain TTL. Default: "60". | ||||
|  PDNS_Ttl Domain TTL. Default: "60". | ||||
| ' | ||||
| 
 | ||||
| DEFAULT_PDNS_TTL=60 | ||||
|  |  | |||
|  | @ -1,14 +1,31 @@ | |||
| #!/usr/bin/env sh | ||||
| # shellcheck disable=SC2034 | ||||
| dns_selectel_info='Selectel.com | ||||
| Domains: Selectel.ru | ||||
| Site: Selectel.com | ||||
| Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_selectel | ||||
| Options: | ||||
|  SL_Key API Key | ||||
| ' | ||||
| 
 | ||||
| SL_Api="https://api.selectel.ru/domains/v1" | ||||
| # dns_selectel_info='Selectel.com | ||||
| # Domains: Selectel.ru | ||||
| # Site: Selectel.com | ||||
| # Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_selectel | ||||
| # Options: | ||||
| # Variables that must be defined before running | ||||
| #   SL_Ver can take one of the values 'v1' or 'v2', default is 'v1' | ||||
| #   SL_Ver='v1', when using version API legacy (v1) | ||||
| #   SL_Ver='v2', when using version API actual (v2) | ||||
| # when using API version v1, i.e. SL_Ver is 'v1' or not defined: | ||||
| #   SL_Key - API Key, required | ||||
| # when using API version v2: | ||||
| #   SL_Ver          - required as 'v2' | ||||
| #   SL_Login_ID     - account ID, required | ||||
| #   SL_Project_Name - name project, required | ||||
| #   SL_Login_Name   - service user name, required | ||||
| #   SL_Pswd         - service user password, required | ||||
| #   SL_Expire       - token lifetime in minutes (0-1440), default 1400 minutes | ||||
| # | ||||
| # Issues: github.com/acmesh-official/acme.sh/issues/5126 | ||||
| # | ||||
| 
 | ||||
| SL_Api="https://api.selectel.ru/domains" | ||||
| auth_uri="https://cloud.api.selcloud.ru/identity/v3/auth/tokens" | ||||
| _sl_sep='#' | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
|  | @ -17,17 +34,14 @@ dns_selectel_add() { | |||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   SL_Key="${SL_Key:-$(_readaccountconf_mutable SL_Key)}" | ||||
| 
 | ||||
|   if [ -z "$SL_Key" ]; then | ||||
|     SL_Key="" | ||||
|     _err "You don't specify selectel.ru api key yet." | ||||
|     _err "Please create you key and try again." | ||||
|   if ! _sl_init_vars; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   #save the api key to the account conf file. | ||||
|   _saveaccountconf_mutable SL_Key "$SL_Key" | ||||
|   _debug2 SL_Ver "$SL_Ver" | ||||
|   _debug2 SL_Expire "$SL_Expire" | ||||
|   _debug2 SL_Login_Name "$SL_Login_Name" | ||||
|   _debug2 SL_Login_ID "$SL_Login_ID" | ||||
|   _debug2 SL_Project_Name "$SL_Project_Name" | ||||
| 
 | ||||
|   _debug "First detect the root zone" | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|  | @ -39,11 +53,63 @@ dns_selectel_add() { | |||
|   _debug _domain "$_domain" | ||||
| 
 | ||||
|   _info "Adding record" | ||||
|   if _sl_rest POST "/$_domain_id/records/" "{\"type\": \"TXT\", \"ttl\": 60, \"name\": \"$fulldomain\", \"content\": \"$txtvalue\"}"; then | ||||
|     if _contains "$response" "$txtvalue" || _contains "$response" "record_already_exists"; then | ||||
|   if [ "$SL_Ver" = "v2" ]; then | ||||
|     _ext_srv1="/zones/" | ||||
|     _ext_srv2="/rrset/" | ||||
|     _text_tmp=$(echo "$txtvalue" | sed -En "s/[\"]*([^\"]*)/\1/p") | ||||
|     _text_tmp='\"'$_text_tmp'\"' | ||||
|     _data="{\"type\": \"TXT\", \"ttl\": 60, \"name\": \"${fulldomain}.\", \"records\": [{\"content\":\"$_text_tmp\"}]}" | ||||
|   elif [ "$SL_Ver" = "v1" ]; then | ||||
|     _ext_srv1="/" | ||||
|     _ext_srv2="/records/" | ||||
|     _data="{\"type\":\"TXT\",\"ttl\":60,\"name\":\"$fulldomain\",\"content\":\"$txtvalue\"}" | ||||
|   else | ||||
|     _err "Error. Unsupported version API $SL_Ver" | ||||
|     return 1 | ||||
|   fi | ||||
|   _ext_uri="${_ext_srv1}$_domain_id${_ext_srv2}" | ||||
|   _debug _ext_uri "$_ext_uri" | ||||
|   _debug _data "$_data" | ||||
| 
 | ||||
|   if _sl_rest POST "$_ext_uri" "$_data"; then | ||||
|     if _contains "$response" "$txtvalue"; then | ||||
|       _info "Added, OK" | ||||
|       return 0 | ||||
|     fi | ||||
|     if _contains "$response" "already_exists"; then | ||||
|       # record TXT with $fulldomain already exists | ||||
|       if [ "$SL_Ver" = "v2" ]; then | ||||
|         # It is necessary to add one more content to the comments | ||||
|         # read all records rrset | ||||
|         _debug "Getting txt records" | ||||
|         _sl_rest GET "${_ext_uri}" | ||||
|         # There is already a $txtvalue value, no need to add it | ||||
|         if _contains "$response" "$txtvalue"; then | ||||
|           _info "Added, OK" | ||||
|           _info "Txt record ${fulldomain} with value ${txtvalue} already exists" | ||||
|           return 0 | ||||
|         fi | ||||
|         # group \1 - full record rrset; group \2 - records attribute value, exactly {"content":"\"value1\""},{"content":"\"value2\""}",... | ||||
|         _record_seg="$(echo "$response" | sed -En "s/.*(\{\"id\"[^}]*${fulldomain}[^}]*records[^}]*\[(\{[^]]*\})\][^}]*}).*/\1/p")" | ||||
|         _record_array="$(echo "$response" | sed -En "s/.*(\{\"id\"[^}]*${fulldomain}[^}]*records[^}]*\[(\{[^]]*\})\][^}]*}).*/\2/p")" | ||||
|         # record id | ||||
|         _record_id="$(echo "$_record_seg" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\"" | cut -d : -f 2 | tr -d "\"")" | ||||
|         # preparing _data | ||||
|         _tmp_str="${_record_array},{\"content\":\"${_text_tmp}\"}" | ||||
|         _data="{\"ttl\": 60, \"records\": [${_tmp_str}]}" | ||||
|         _debug2 _record_seg "$_record_seg" | ||||
|         _debug2 _record_array "$_record_array" | ||||
|         _debug2 _record_array "$_record_id" | ||||
|         _debug "New data for record" "$_data" | ||||
|         if _sl_rest PATCH "${_ext_uri}${_record_id}" "$_data"; then | ||||
|           _info "Added, OK" | ||||
|           return 0 | ||||
|         fi | ||||
|       elif [ "$SL_Ver" = "v1" ]; then | ||||
|         _info "Added, OK" | ||||
|         return 0 | ||||
|       fi | ||||
|     fi | ||||
|   fi | ||||
|   _err "Add txt record error." | ||||
|   return 1 | ||||
|  | @ -54,15 +120,15 @@ dns_selectel_rm() { | |||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   SL_Key="${SL_Key:-$(_readaccountconf_mutable SL_Key)}" | ||||
| 
 | ||||
|   if [ -z "$SL_Key" ]; then | ||||
|     SL_Key="" | ||||
|     _err "You don't specify slectel api key yet." | ||||
|     _err "Please create you key and try again." | ||||
|   if ! _sl_init_vars "nosave"; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _debug2 SL_Ver "$SL_Ver" | ||||
|   _debug2 SL_Expire "$SL_Expire" | ||||
|   _debug2 SL_Login_Name "$SL_Login_Name" | ||||
|   _debug2 SL_Login_ID "$SL_Login_ID" | ||||
|   _debug2 SL_Project_Name "$SL_Project_Name" | ||||
|   # | ||||
|   _debug "First detect the root zone" | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "invalid domain" | ||||
|  | @ -71,91 +137,195 @@ dns_selectel_rm() { | |||
|   _debug _domain_id "$_domain_id" | ||||
|   _debug _sub_domain "$_sub_domain" | ||||
|   _debug _domain "$_domain" | ||||
| 
 | ||||
|   # | ||||
|   if [ "$SL_Ver" = "v2" ]; then | ||||
|     _ext_srv1="/zones/" | ||||
|     _ext_srv2="/rrset/" | ||||
|   elif [ "$SL_Ver" = "v1" ]; then | ||||
|     _ext_srv1="/" | ||||
|     _ext_srv2="/records/" | ||||
|   else | ||||
|     _err "Error. Unsupported version API $SL_Ver" | ||||
|     return 1 | ||||
|   fi | ||||
|   # | ||||
|   _debug "Getting txt records" | ||||
|   _sl_rest GET "/${_domain_id}/records/" | ||||
| 
 | ||||
|   _ext_uri="${_ext_srv1}$_domain_id${_ext_srv2}" | ||||
|   _debug _ext_uri "$_ext_uri" | ||||
|   _sl_rest GET "${_ext_uri}" | ||||
|   # | ||||
|   if ! _contains "$response" "$txtvalue"; then | ||||
|     _err "Txt record not found" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _record_seg="$(echo "$response" | _egrep_o "[^{]*\"content\" *: *\"$txtvalue\"[^}]*}")" | ||||
|   # | ||||
|   if [ "$SL_Ver" = "v2" ]; then | ||||
|     _record_seg="$(echo "$response" | sed -En "s/.*(\{\"id\"[^}]*records[^[]*(\[(\{[^]]*${txtvalue}[^]]*)\])[^}]*}).*/\1/gp")" | ||||
|     _record_arr="$(echo "$response" | sed -En "s/.*(\{\"id\"[^}]*records[^[]*(\[(\{[^]]*${txtvalue}[^]]*)\])[^}]*}).*/\3/p")" | ||||
|   elif [ "$SL_Ver" = "v1" ]; then | ||||
|     _record_seg="$(echo "$response" | _egrep_o "[^{]*\"content\" *: *\"$txtvalue\"[^}]*}")" | ||||
|   else | ||||
|     _err "Error. Unsupported version API $SL_Ver" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug2 "_record_seg" "$_record_seg" | ||||
|   if [ -z "$_record_seg" ]; then | ||||
|     _err "can not find _record_seg" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _record_id="$(echo "$_record_seg" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\"" | cut -d : -f 2)" | ||||
|   _debug2 "_record_id" "$_record_id" | ||||
|   # record id | ||||
|   # the following lines change the algorithm for deleting records with the value $txtvalue | ||||
|   # if you use the 1st line, then all such records are deleted at once | ||||
|   # if you use the 2nd line, then only the first entry from them is deleted | ||||
|   #_record_id="$(echo "$_record_seg" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\"" | cut -d : -f 2 | tr -d "\"")" | ||||
|   _record_id="$(echo "$_record_seg" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\"" | cut -d : -f 2 | tr -d "\"" | sed '1!d')" | ||||
|   if [ -z "$_record_id" ]; then | ||||
|     _err "can not find _record_id" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _sl_rest DELETE "/$_domain_id/records/$_record_id"; then | ||||
|     _err "Delete record error." | ||||
|     return 1 | ||||
|   _debug2 "_record_id" "$_record_id" | ||||
|   # delete all record type TXT with text $txtvalue | ||||
|   if [ "$SL_Ver" = "v2" ]; then | ||||
|     # actual | ||||
|     _new_arr="$(echo "$_record_seg" | sed -En "s/.*(\{\"id\"[^}]*records[^[]*(\[(\{[^]]*${txtvalue}[^]]*)\])[^}]*}).*/\3/gp" | sed -En "s/(\},\{)/}\n{/gp" | sed "/${txtvalue}/d" | sed ":a;N;s/\n/,/;ta")" | ||||
|     # uri record for DEL or PATCH | ||||
|     _del_uri="${_ext_uri}${_record_id}" | ||||
|     _debug _del_uri "$_del_uri" | ||||
|     if [ -z "$_new_arr" ]; then | ||||
|       # remove record | ||||
|       if ! _sl_rest DELETE "${_del_uri}"; then | ||||
|         _err "Delete record error: ${_del_uri}." | ||||
|       else | ||||
|         info "Delete record success: ${_del_uri}." | ||||
|       fi | ||||
|     else | ||||
|       # update a record by removing one element in content | ||||
|       _data="{\"ttl\": 60, \"records\": [${_new_arr}]}" | ||||
|       _debug2 _data "$_data" | ||||
|       # REST API PATCH call | ||||
|       if _sl_rest PATCH "${_del_uri}" "$_data"; then | ||||
|         _info "Patched, OK: ${_del_uri}" | ||||
|       else | ||||
|         _err "Patched record error: ${_del_uri}." | ||||
|       fi | ||||
|     fi | ||||
|   else | ||||
|     # legacy | ||||
|     for _one_id in $_record_id; do | ||||
|       _del_uri="${_ext_uri}${_one_id}" | ||||
|       _debug _del_uri "$_del_uri" | ||||
|       if ! _sl_rest DELETE "${_del_uri}"; then | ||||
|         _err "Delete record error: ${_del_uri}." | ||||
|       else | ||||
|         info "Delete record success: ${_del_uri}." | ||||
|       fi | ||||
|     done | ||||
|   fi | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions below ################################## | ||||
| #_acme-challenge.www.domain.com | ||||
| #returns | ||||
| # _sub_domain=_acme-challenge.www | ||||
| # _domain=domain.com | ||||
| # _domain_id=sdjkglgdfewsdfg | ||||
| 
 | ||||
| _get_root() { | ||||
|   domain=$1 | ||||
| 
 | ||||
|   if ! _sl_rest GET "/"; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   i=2 | ||||
|   p=1 | ||||
|   while true; do | ||||
|     h=$(printf "%s" "$domain" | cut -d . -f "$i"-100) | ||||
|     _debug h "$h" | ||||
|     if [ -z "$h" ]; then | ||||
|       #not valid | ||||
|   if [ "$SL_Ver" = 'v1' ]; then | ||||
|     # version API 1 | ||||
|     if ! _sl_rest GET "/"; then | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     if _contains "$response" "\"name\" *: *\"$h\","; then | ||||
|       _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p") | ||||
|       _domain=$h | ||||
|       _debug "Getting domain id for $h" | ||||
|       if ! _sl_rest GET "/$h"; then | ||||
|     i=2 | ||||
|     p=1 | ||||
|     while true; do | ||||
|       h=$(printf "%s" "$domain" | cut -d . -f "$i"-100) | ||||
|       _debug h "$h" | ||||
|       if [ -z "$h" ]; then | ||||
|         return 1 | ||||
|       fi | ||||
|       _domain_id="$(echo "$response" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\":" | cut -d : -f 2)" | ||||
|       return 0 | ||||
|       if _contains "$response" "\"name\" *: *\"$h\","; then | ||||
|         _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p") | ||||
|         _domain=$h | ||||
|         _debug "Getting domain id for $h" | ||||
|         if ! _sl_rest GET "/$h"; then | ||||
|           _err "Error read records of all domains $SL_Ver" | ||||
|           return 1 | ||||
|         fi | ||||
|         _domain_id="$(echo "$response" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\":" | cut -d : -f 2)" | ||||
|         return 0 | ||||
|       fi | ||||
|       p=$i | ||||
|       i=$(_math "$i" + 1) | ||||
|     done | ||||
|     _err "Error read records of all domains $SL_Ver" | ||||
|     return 1 | ||||
|   elif [ "$SL_Ver" = "v2" ]; then | ||||
|     # version API 2 | ||||
|     _ext_uri='/zones/' | ||||
|     domain="${domain}." | ||||
|     _debug "domain:: " "$domain" | ||||
|     # read records of all domains | ||||
|     if ! _sl_rest GET "$_ext_uri"; then | ||||
|       _err "Error read records of all domains $SL_Ver" | ||||
|       return 1 | ||||
|     fi | ||||
|     p=$i | ||||
|     i=$(_math "$i" + 1) | ||||
|   done | ||||
|   return 1 | ||||
|     i=1 | ||||
|     p=1 | ||||
|     while true; do | ||||
|       h=$(printf "%s" "$domain" | cut -d . -f "$i"-100) | ||||
|       _debug h "$h" | ||||
|       if [ -z "$h" ]; then | ||||
|         _err "The domain was not found among the registered ones" | ||||
|         return 1 | ||||
|       fi | ||||
|       _domain_record=$(echo "$response" | sed -En "s/.*(\{[^}]*id[^}]*\"name\" *: *\"$h\"[^}]*}).*/\1/p") | ||||
|       _debug "_domain_record:: " "$_domain_record" | ||||
|       if [ -n "$_domain_record" ]; then | ||||
|         _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p") | ||||
|         _domain=$h | ||||
|         _debug "Getting domain id for $h" | ||||
|         _domain_id=$(echo "$_domain_record" | sed -En "s/\{[^}]*\"id\" *: *\"([^\"]*)\"[^}]*\}/\1/p") | ||||
|         return 0 | ||||
|       fi | ||||
|       p=$i | ||||
|       i=$(_math "$i" + 1) | ||||
|     done | ||||
|     _err "Error read records of all domains $SL_Ver" | ||||
|     return 1 | ||||
|   else | ||||
|     _err "Error. Unsupported version API $SL_Ver" | ||||
|     return 1 | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| ################################################################# | ||||
| # use: method add_url body | ||||
| _sl_rest() { | ||||
|   m=$1 | ||||
|   ep="$2" | ||||
|   data="$3" | ||||
|   _debug "$ep" | ||||
| 
 | ||||
|   export _H1="X-Token: $SL_Key" | ||||
|   _token=$(_get_auth_token) | ||||
|   if [ -z "$_token" ]; then | ||||
|     _err "BAD key or token $ep" | ||||
|     return 1 | ||||
|   fi | ||||
|   if [ "$SL_Ver" = v2 ]; then | ||||
|     _h1_name="X-Auth-Token" | ||||
|   else | ||||
|     _h1_name='X-Token' | ||||
|   fi | ||||
|   export _H1="${_h1_name}: ${_token}" | ||||
|   export _H2="Content-Type: application/json" | ||||
| 
 | ||||
|   _debug2 "Full URI: " "$SL_Api/${SL_Ver}${ep}" | ||||
|   _debug2 "_H1:" "$_H1" | ||||
|   _debug2 "_H2:" "$_H2" | ||||
|   if [ "$m" != "GET" ]; then | ||||
|     _debug data "$data" | ||||
|     response="$(_post "$data" "$SL_Api/$ep" "" "$m")" | ||||
|     response="$(_post "$data" "$SL_Api/${SL_Ver}${ep}" "" "$m")" | ||||
|   else | ||||
|     response="$(_get "$SL_Api/$ep")" | ||||
|     response="$(_get "$SL_Api/${SL_Ver}${ep}")" | ||||
|   fi | ||||
| 
 | ||||
|   # shellcheck disable=SC2181 | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "error $ep" | ||||
|     return 1 | ||||
|  | @ -163,3 +333,152 @@ _sl_rest() { | |||
|   _debug2 response "$response" | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| _get_auth_token() { | ||||
|   if [ "$SL_Ver" = 'v1' ]; then | ||||
|     # token for v1 | ||||
|     _debug "Token v1" | ||||
|     _token_keystone=$SL_Key | ||||
|   elif [ "$SL_Ver" = 'v2' ]; then | ||||
|     # token for v2. Get a token for calling the API | ||||
|     _debug "Keystone Token v2" | ||||
|     token_v2=$(_readaccountconf_mutable SL_Token_V2) | ||||
|     if [ -n "$token_v2" ]; then | ||||
|       # The structure with the token was considered. Let's check its validity | ||||
|       # field 1 - SL_Login_Name | ||||
|       # field 2 - token keystone | ||||
|       # field 3 - SL_Login_ID | ||||
|       # field 4 - SL_Project_Name | ||||
|       # field 5 - Receipt time | ||||
|       # separator - '$_sl_sep' | ||||
|       _login_name=$(_getfield "$token_v2" 1 "$_sl_sep") | ||||
|       _token_keystone=$(_getfield "$token_v2" 2 "$_sl_sep") | ||||
|       _project_name=$(_getfield "$token_v2" 4 "$_sl_sep") | ||||
|       _receipt_time=$(_getfield "$token_v2" 5 "$_sl_sep") | ||||
|       _login_id=$(_getfield "$token_v2" 3 "$_sl_sep") | ||||
|       _debug2 _login_name "$_login_name" | ||||
|       _debug2 _login_id "$_login_id" | ||||
|       _debug2 _project_name "$_project_name" | ||||
|       # check the validity of the token for the user and the project and its lifetime | ||||
|       _dt_diff_minute=$((($(date +%s) - _receipt_time) / 60)) | ||||
|       _debug2 _dt_diff_minute "$_dt_diff_minute" | ||||
|       [ "$_dt_diff_minute" -gt "$SL_Expire" ] && unset _token_keystone | ||||
|       if [ "$_project_name" != "$SL_Project_Name" ] || [ "$_login_name" != "$SL_Login_Name" ] || [ "$_login_id" != "$SL_Login_ID" ]; then | ||||
|         unset _token_keystone | ||||
|       fi | ||||
|       _debug "Get exists token" | ||||
|     fi | ||||
|     if [ -z "$_token_keystone" ]; then | ||||
|       # the previous token is incorrect or was not received, get a new one | ||||
|       _debug "Update (get new) token" | ||||
|       _data_auth="{\"auth\":{\"identity\":{\"methods\":[\"password\"],\"password\":{\"user\":{\"name\":\"${SL_Login_Name}\",\"domain\":{\"name\":\"${SL_Login_ID}\"},\"password\":\"${SL_Pswd}\"}}},\"scope\":{\"project\":{\"name\":\"${SL_Project_Name}\",\"domain\":{\"name\":\"${SL_Login_ID}\"}}}}}" | ||||
|       export _H1="Content-Type: application/json" | ||||
|       _result=$(_post "$_data_auth" "$auth_uri") | ||||
|       _token_keystone=$(grep 'x-subject-token' "$HTTP_HEADER" | sed -nE "s/[[:space:]]*x-subject-token:[[:space:]]*([[:print:]]*)(\r*)/\1/p") | ||||
|       _dt_curr=$(date +%s) | ||||
|       SL_Token_V2="${SL_Login_Name}${_sl_sep}${_token_keystone}${_sl_sep}${SL_Login_ID}${_sl_sep}${SL_Project_Name}${_sl_sep}${_dt_curr}" | ||||
|       _saveaccountconf_mutable SL_Token_V2 "$SL_Token_V2" | ||||
|     fi | ||||
|   else | ||||
|     # token set empty for unsupported version API | ||||
|     _token_keystone="" | ||||
|   fi | ||||
|   printf -- "%s" "$_token_keystone" | ||||
| } | ||||
| 
 | ||||
| ################################################################# | ||||
| # use: [non_save] | ||||
| _sl_init_vars() { | ||||
|   _non_save="${1}" | ||||
|   _debug2 _non_save "$_non_save" | ||||
| 
 | ||||
|   _debug "First init variables" | ||||
|   # version API | ||||
|   SL_Ver="${SL_Ver:-$(_readaccountconf_mutable SL_Ver)}" | ||||
|   if [ -z "$SL_Ver" ]; then | ||||
|     SL_Ver="v1" | ||||
|   fi | ||||
|   if ! [ "$SL_Ver" = "v1" ] && ! [ "$SL_Ver" = "v2" ]; then | ||||
|     _err "You don't specify selectel.ru API version." | ||||
|     _err "Please define specify API version." | ||||
|   fi | ||||
|   _debug2 SL_Ver "$SL_Ver" | ||||
|   if [ "$SL_Ver" = "v1" ]; then | ||||
|     # token | ||||
|     SL_Key="${SL_Key:-$(_readaccountconf_mutable SL_Key)}" | ||||
| 
 | ||||
|     if [ -z "$SL_Key" ]; then | ||||
|       SL_Key="" | ||||
|       _err "You don't specify selectel.ru api key yet." | ||||
|       _err "Please create you key and try again." | ||||
|       return 1 | ||||
|     fi | ||||
|     #save the api key to the account conf file. | ||||
|     if [ -z "$_non_save" ]; then | ||||
|       _saveaccountconf_mutable SL_Key "$SL_Key" | ||||
|     fi | ||||
|   elif [ "$SL_Ver" = "v2" ]; then | ||||
|     # time expire token | ||||
|     SL_Expire="${SL_Expire:-$(_readaccountconf_mutable SL_Expire)}" | ||||
|     if [ -z "$SL_Expire" ]; then | ||||
|       SL_Expire=1400 # 23h 20 min | ||||
|     fi | ||||
|     if [ -z "$_non_save" ]; then | ||||
|       _saveaccountconf_mutable SL_Expire "$SL_Expire" | ||||
|     fi | ||||
|     # login service user | ||||
|     SL_Login_Name="${SL_Login_Name:-$(_readaccountconf_mutable SL_Login_Name)}" | ||||
|     if [ -z "$SL_Login_Name" ]; then | ||||
|       SL_Login_Name='' | ||||
|       _err "You did not specify the selectel.ru API service user name." | ||||
|       _err "Please provide a service user name and try again." | ||||
|       return 1 | ||||
|     fi | ||||
|     if [ -z "$_non_save" ]; then | ||||
|       _saveaccountconf_mutable SL_Login_Name "$SL_Login_Name" | ||||
|     fi | ||||
|     # user ID | ||||
|     SL_Login_ID="${SL_Login_ID:-$(_readaccountconf_mutable SL_Login_ID)}" | ||||
|     if [ -z "$SL_Login_ID" ]; then | ||||
|       SL_Login_ID='' | ||||
|       _err "You did not specify the selectel.ru API user ID." | ||||
|       _err "Please provide a user ID and try again." | ||||
|       return 1 | ||||
|     fi | ||||
|     if [ -z "$_non_save" ]; then | ||||
|       _saveaccountconf_mutable SL_Login_ID "$SL_Login_ID" | ||||
|     fi | ||||
|     # project name | ||||
|     SL_Project_Name="${SL_Project_Name:-$(_readaccountconf_mutable SL_Project_Name)}" | ||||
|     if [ -z "$SL_Project_Name" ]; then | ||||
|       SL_Project_Name='' | ||||
|       _err "You did not specify the project name." | ||||
|       _err "Please provide a project name and try again." | ||||
|       return 1 | ||||
|     fi | ||||
|     if [ -z "$_non_save" ]; then | ||||
|       _saveaccountconf_mutable SL_Project_Name "$SL_Project_Name" | ||||
|     fi | ||||
|     # service user password | ||||
|     SL_Pswd="${SL_Pswd:-$(_readaccountconf_mutable SL_Pswd)}" | ||||
|     if [ -z "$SL_Pswd" ]; then | ||||
|       SL_Pswd='' | ||||
|       _err "You did not specify the service user password." | ||||
|       _err "Please provide a service user password and try again." | ||||
|       return 1 | ||||
|     fi | ||||
|     if [ -z "$_non_save" ]; then | ||||
|       _saveaccountconf_mutable SL_Pswd "$SL_Pswd" "12345678" | ||||
|     fi | ||||
|   else | ||||
|     SL_Ver="" | ||||
|     _err "You also specified the wrong version of the selectel.ru API." | ||||
|     _err "Please provide the correct API version and try again." | ||||
|     return 1 | ||||
|   fi | ||||
|   if [ -z "$_non_save" ]; then | ||||
|     _saveaccountconf_mutable SL_Ver "$SL_Ver" | ||||
|   fi | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
|  |  | |||
|  | @ -1,13 +1,12 @@ | |||
| #!/usr/bin/env sh | ||||
| # shellcheck disable=SC2034 | ||||
| dns_Technitium_info='Technitium DNS Server | ||||
| 
 | ||||
| Site: https://technitium.com/dns/ | ||||
| dns_technitium_info='Technitium DNS Server | ||||
| Site: Technitium.com/dns/ | ||||
| Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_technitium | ||||
| Options: | ||||
|  Technitium_Server Server Address | ||||
|  Technitium_Token API Token | ||||
| Issues:https://github.com/acmesh-official/acme.sh/issues/6116 | ||||
| Issues: github.com/acmesh-official/acme.sh/issues/6116 | ||||
| Author: Henning Reich <acmesh@qupfer.de> | ||||
| ' | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,9 +1,13 @@ | |||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| # West.cn Domain api | ||||
| #WEST_Username="username" | ||||
| #WEST_Key="sADDsdasdgdsf" | ||||
| #Set key at https://www.west.cn/manager/API/APIconfig.asp | ||||
| # shellcheck disable=SC2034 | ||||
| dns_west_cn_info='West.cn | ||||
| Site: West.cn | ||||
| Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_west_cn | ||||
| Options: | ||||
|  WEST_Username API username | ||||
|  WEST_Key API Key. Set at https://www.west.cn/manager/API/APIconfig.asp | ||||
| Issues: github.com/acmesh-official/acme.sh/issues/4894 | ||||
| ' | ||||
| 
 | ||||
| REST_API="https://api.west.cn/API/v2" | ||||
| 
 | ||||
|  |  | |||
|  | @ -202,7 +202,7 @@ _get_paketnr() { | |||
|   fqdn="$1" | ||||
|   form="$2" | ||||
| 
 | ||||
|   domains=$(echo "$form" | grep '<ul class="nav header-paket-list">' | sed 's/<li/\n<li/g' | sed 's/<[^>]*>/ /g' | sed 's/^.*>\([^>]*\)$/\1/') | ||||
|   domains=$(echo "$form" | grep 'paketListData' | grep -o '"fqdn":"[^"]*"' | sed 's/.*:"\(.*\)"/\1/') | ||||
|   _debug domains "$domains" | ||||
|   domain='' | ||||
|   for domain in $domains; do | ||||
|  | @ -218,7 +218,7 @@ _get_paketnr() { | |||
|   TLD="$domain" | ||||
|   _debug domain "$domain" | ||||
|   RECORD=$(echo "$fqdn" | cut -c"1-$((${#fqdn} - ${#TLD} - 1))") | ||||
|   PAKETNR=$(echo "$domains" | grep -o " $domain.*" | sed 's/^[^,]*, *\([0-9]*\).*$/\1/') | ||||
|   PAKETNR=$(echo "$form" | grep -o "\"id\":[^{}]*\"fqdn\":\"$domain\"" | sed 's/"id":\([0-9]*\).*$/\1/') | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| #!/usr/bin/env sh | ||||
| # shellcheck disable=SC2034 | ||||
| dns_yandex360_info='Yandex 360 for Business DNS API. | ||||
| Yandex 360 for Business is a digital environment for effective collaboration. | ||||
|  Yandex 360 for Business is a digital environment for effective collaboration. | ||||
| Site: https://360.yandex.com/ | ||||
| Docs: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_yandex360 | ||||
| Options: | ||||
|  |  | |||
|  | @ -1,19 +1,23 @@ | |||
| #!/usr/bin/env sh | ||||
| # shellcheck disable=SC2034 | ||||
| dns_zoneedit_info='ZoneEdit.com | ||||
| Site: ZoneEdit.com | ||||
| Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_zoneedit | ||||
| Options: | ||||
|  ZONEEDIT_ID ID | ||||
|  ZONEEDIT_Token API Token | ||||
| Issues: github.com/acmesh-official/acme.sh/issues/6135 | ||||
| ' | ||||
| 
 | ||||
| # https://github.com/blueslow/sslcertzoneedit | ||||
| 
 | ||||
| # Only need to export the credentials once, acme.sh will save for automatic renewal. | ||||
| # export ZONEEDIT_ID="Your id" | ||||
| # export ZONEEDIT_Token="Your token" | ||||
| # acme.sh --issue --dns dns_zoneedit -d example.com -d www.example.com | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| # Usage: dns_zoneedit_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_zoneedit_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
|   _info "Using Zoneedit" | ||||
|   _info "Using ZoneEdit" | ||||
|   _debug fulldomain "$fulldomain" | ||||
|   _debug txtvalue "$txtvalue" | ||||
| 
 | ||||
|  | @ -45,7 +49,7 @@ dns_zoneedit_add() { | |||
| dns_zoneedit_rm() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
|   _info "Using Zoneedit" | ||||
|   _info "Using ZoneEdit" | ||||
|   _debug fulldomain "$fulldomain" | ||||
|   _debug txtvalue "$txtvalue" | ||||
| 
 | ||||
|  | @ -114,7 +118,7 @@ _zoneedit_api() { | |||
|       if [ "$ze_sleep" ]; then _sleep "$ze_sleep"; fi | ||||
|       return 0 | ||||
|     elif _contains "$response" "ERROR.*Minimum.*seconds"; then | ||||
|       _info "Zoneedit responded with a rate limit of..." | ||||
|       _info "ZoneEdit responded with a rate limit of..." | ||||
|       ze_ratelimit=$(echo "$response" | sed -n 's/.*Minimum \([0-9]\+\) seconds.*/\1/p') | ||||
|       if [ "$ze_ratelimit" ] && [ ! "$(echo "$ze_ratelimit" | tr -d '0-9')" ]; then | ||||
|         _info "$ze_ratelimit seconds." | ||||
|  |  | |||
|  | @ -89,7 +89,7 @@ _use_metadata() { | |||
|       _normalizeJson | | ||||
|       tr '{,}' '\n' | | ||||
|       while read -r _line; do | ||||
|         _key="$(echo "${_line%%:*}" | tr -d '"')" | ||||
|         _key="$(echo "${_line%%:*}" | tr -d \")" | ||||
|         _value="${_line#*:}" | ||||
|         _debug3 "_key" "$_key" | ||||
|         _secure_debug3 "_value" "$_value" | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 neil
						neil