123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 |
- #!/usr/bin/env sh
- # shellcheck disable=SC2034
- dns_opnsense_info='OPNsense Server
- Site: docs.opnsense.org/development/api.html
- Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_opnsense
- Options:
- OPNs_Host Server Hostname. E.g. "opnsense.example.com"
- OPNs_Port Port. Default: "443".
- OPNs_Key API Key
- OPNs_Token API Token
- OPNs_Api_Insecure Insecure TLS. 0: check for cert validity, 1: always accept
- Issues: github.com/acmesh-official/acme.sh/issues/2480
- '
- ######## Public functions #####################
- #Usage: add _acme-challenge.www.domain.com "123456789ABCDEF0000000000000000000000000000000000000"
- #fulldomain
- #txtvalue
- OPNs_DefaultPort=443
- OPNs_DefaultApi_Insecure=0
- dns_opnsense_add() {
- fulldomain=$1
- txtvalue=$2
- _opns_check_auth || return 1
- if ! set_record "$fulldomain" "$txtvalue"; then
- return 1
- fi
- return 0
- }
- #fulldomain
- dns_opnsense_rm() {
- fulldomain=$1
- txtvalue=$2
- _opns_check_auth || return 1
- if ! rm_record "$fulldomain" "$txtvalue"; then
- return 1
- fi
- return 0
- }
- set_record() {
- fulldomain=$1
- new_challenge=$2
- _info "Adding record $fulldomain with challenge: $new_challenge"
- _debug "Detect root zone"
- if ! _get_root "$fulldomain"; then
- _err "invalid domain"
- return 1
- fi
- _debug _domain "$_domain"
- _debug _host "$_host"
- _debug _domainid "$_domainid"
- _return_str=""
- _record_string=""
- _build_record_string "$_domainid" "$_host" "$new_challenge"
- _uuid=""
- if _existingchallenge "$_domain" "$_host" "$new_challenge"; then
- # Update
- if _opns_rest "POST" "/record/setRecord/${_uuid}" "$_record_string"; then
- _return_str="$response"
- else
- return 1
- fi
- else
- #create
- if _opns_rest "POST" "/record/addRecord" "$_record_string"; then
- _return_str="$response"
- else
- return 1
- fi
- fi
- if echo "$_return_str" | _egrep_o "\"result\":\"saved\"" >/dev/null; then
- _opns_rest "POST" "/service/reconfigure" "{}"
- _debug "Record created"
- else
- _err "Error creating record $_record_string"
- return 1
- fi
- return 0
- }
- rm_record() {
- fulldomain=$1
- new_challenge="$2"
- _info "Remove record $fulldomain with challenge: $new_challenge"
- _debug "Detect root zone"
- if ! _get_root "$fulldomain"; then
- _err "invalid domain"
- return 1
- fi
- _debug _domain "$_domain"
- _debug _host "$_host"
- _debug _domainid "$_domainid"
- _uuid=""
- if _existingchallenge "$_domain" "$_host" "$new_challenge"; then
- # Delete
- if _opns_rest "POST" "/record/delRecord/${_uuid}" "\{\}"; then
- if echo "$response" | _egrep_o "\"result\":\"deleted\"" >/dev/null; then
- _debug "Record deleted"
- _opns_rest "POST" "/service/reconfigure" "{}"
- _debug "Service reconfigured"
- else
- _err "Error deleting record $_host from domain $fulldomain"
- return 1
- fi
- else
- _err "Error requesting deletion of record $_host from domain $fulldomain"
- return 1
- fi
- else
- _info "Record not found, nothing to remove"
- fi
- return 0
- }
- #################### Private functions below ##################################
- #_acme-challenge.www.domain.com
- #returns
- # _domainid=domid
- #_domain=domain.com
- _get_root() {
- domain=$1
- i=2
- p=1
- if _opns_rest "GET" "/domain/searchPrimaryDomain"; then
- _domain_response="$response"
- else
- return 1
- fi
- while true; do
- h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
- if [ -z "$h" ]; then
- #not valid
- return 1
- fi
- _debug h "$h"
- lines=$(echo "$_domain_response" | sed 's/{/\n/g')
- for line in $lines; do
- id=$(echo "$line" | _egrep_o "\"uuid\":\"[a-z0-9\-]*\",\"enabled\":\"1\",\"type\":\"primary\",.*\"domainname\":\"${h}\"" | cut -d ':' -f 2 | cut -d '"' -f 2)
- if [ -n "$id" ]; then
- _debug id "$id"
- _host=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
- _domain="${h}"
- _domainid="${id}"
- return 0
- fi
- done
- p=$i
- i=$(_math "$i" + 1)
- done
- _debug "$domain not found"
- return 1
- }
- _opns_rest() {
- method=$1
- ep=$2
- data=$3
- #Percent encode user and token
- key=$(echo "$OPNs_Key" | tr -d "\n\r" | _url_encode)
- token=$(echo "$OPNs_Token" | tr -d "\n\r" | _url_encode)
- opnsense_url="https://${key}:${token}@${OPNs_Host}:${OPNs_Port:-$OPNs_DefaultPort}/api/bind${ep}"
- export _H1="Content-Type: application/json"
- _debug2 "Try to call api: https://${OPNs_Host}:${OPNs_Port:-$OPNs_DefaultPort}/api/bind${ep}"
- if [ ! "$method" = "GET" ]; then
- _debug data "$data"
- export _H1="Content-Type: application/json"
- response="$(_post "$data" "$opnsense_url" "" "$method")"
- else
- export _H1=""
- response="$(_get "$opnsense_url")"
- fi
- if [ "$?" != "0" ]; then
- _err "error $ep"
- return 1
- fi
- _debug2 response "$response"
- return 0
- }
- _build_record_string() {
- _record_string="{\"record\":{\"enabled\":\"1\",\"domain\":\"$1\",\"name\":\"$2\",\"type\":\"TXT\",\"value\":\"$3\"}}"
- }
- _existingchallenge() {
- if _opns_rest "GET" "/record/searchRecord"; then
- _record_response="$response"
- else
- return 1
- fi
- _uuid=""
- _uuid=$(echo "$_record_response" | _egrep_o "\"uuid\":\"[a-z0-9\-]*\",\"enabled\":\"[01]\",\"domain\":\"[a-z0-9\-]*\",\"%domain\":\"$1\",\"name\":\"$2\",\"type\":\"TXT\",\"value\":\"$3\"" | cut -d ':' -f 2 | cut -d '"' -f 2)
- if [ -n "$_uuid" ]; then
- _debug uuid "$_uuid"
- return 0
- fi
- _debug "${2}.${1} record not found"
- return 1
- }
- _opns_check_auth() {
- OPNs_Host="${OPNs_Host:-$(_readaccountconf_mutable OPNs_Host)}"
- OPNs_Port="${OPNs_Port:-$(_readaccountconf_mutable OPNs_Port)}"
- OPNs_Key="${OPNs_Key:-$(_readaccountconf_mutable OPNs_Key)}"
- OPNs_Token="${OPNs_Token:-$(_readaccountconf_mutable OPNs_Token)}"
- OPNs_Api_Insecure="${OPNs_Api_Insecure:-$(_readaccountconf_mutable OPNs_Api_Insecure)}"
- if [ -z "$OPNs_Host" ]; then
- _err "You don't specify OPNsense address."
- return 1
- else
- _saveaccountconf_mutable OPNs_Host "$OPNs_Host"
- fi
- if ! printf '%s' "$OPNs_Port" | grep '^[0-9]*$' >/dev/null; then
- _err 'OPNs_Port specified but not numeric value'
- return 1
- elif [ -z "$OPNs_Port" ]; then
- _info "OPNSense port not specified. Defaulting to using port $OPNs_DefaultPort"
- else
- _saveaccountconf_mutable OPNs_Port "$OPNs_Port"
- fi
- if ! printf '%s' "$OPNs_Api_Insecure" | grep '^[01]$' >/dev/null; then
- _err 'OPNs_Api_Insecure specified but not 0/1 value'
- return 1
- elif [ -n "$OPNs_Api_Insecure" ]; then
- _saveaccountconf_mutable OPNs_Api_Insecure "$OPNs_Api_Insecure"
- fi
- export HTTPS_INSECURE="${OPNs_Api_Insecure:-$OPNs_DefaultApi_Insecure}"
- if [ -z "$OPNs_Key" ]; then
- _err "you have not specified your OPNsense api key id."
- _err "Please set OPNs_Key and try again."
- return 1
- else
- _saveaccountconf_mutable OPNs_Key "$OPNs_Key"
- fi
- if [ -z "$OPNs_Token" ]; then
- _err "you have not specified your OPNsense token."
- _err "Please create OPNs_Token and try again."
- return 1
- else
- _saveaccountconf_mutable OPNs_Token "$OPNs_Token"
- fi
- if ! _opns_rest "GET" "/general/get"; then
- _err "Call to OPNsense API interface failed. Unable to access OPNsense API."
- return 1
- fi
- return 0
- }
|