dns_opnsense.sh 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. #!/usr/bin/env sh
  2. # shellcheck disable=SC2034
  3. dns_opnsense_info='OPNsense Server
  4. Site: docs.opnsense.org/development/api.html
  5. Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_opnsense
  6. Options:
  7. OPNs_Host Server Hostname. E.g. "opnsense.example.com"
  8. OPNs_Port Port. Default: "443".
  9. OPNs_Key API Key
  10. OPNs_Token API Token
  11. OPNs_Api_Insecure Insecure TLS. 0: check for cert validity, 1: always accept
  12. Issues: github.com/acmesh-official/acme.sh/issues/2480
  13. '
  14. ######## Public functions #####################
  15. #Usage: add _acme-challenge.www.domain.com "123456789ABCDEF0000000000000000000000000000000000000"
  16. #fulldomain
  17. #txtvalue
  18. OPNs_DefaultPort=443
  19. OPNs_DefaultApi_Insecure=0
  20. dns_opnsense_add() {
  21. fulldomain=$1
  22. txtvalue=$2
  23. _opns_check_auth || return 1
  24. if ! set_record "$fulldomain" "$txtvalue"; then
  25. return 1
  26. fi
  27. return 0
  28. }
  29. #fulldomain
  30. dns_opnsense_rm() {
  31. fulldomain=$1
  32. txtvalue=$2
  33. _opns_check_auth || return 1
  34. if ! rm_record "$fulldomain" "$txtvalue"; then
  35. return 1
  36. fi
  37. return 0
  38. }
  39. set_record() {
  40. fulldomain=$1
  41. new_challenge=$2
  42. _info "Adding record $fulldomain with challenge: $new_challenge"
  43. _debug "Detect root zone"
  44. if ! _get_root "$fulldomain"; then
  45. _err "invalid domain"
  46. return 1
  47. fi
  48. _debug _domain "$_domain"
  49. _debug _host "$_host"
  50. _debug _domainid "$_domainid"
  51. _return_str=""
  52. _record_string=""
  53. _build_record_string "$_domainid" "$_host" "$new_challenge"
  54. _uuid=""
  55. if _existingchallenge "$_domain" "$_host" "$new_challenge"; then
  56. # Update
  57. if _opns_rest "POST" "/record/setRecord/${_uuid}" "$_record_string"; then
  58. _return_str="$response"
  59. else
  60. return 1
  61. fi
  62. else
  63. #create
  64. if _opns_rest "POST" "/record/addRecord" "$_record_string"; then
  65. _return_str="$response"
  66. else
  67. return 1
  68. fi
  69. fi
  70. if echo "$_return_str" | _egrep_o "\"result\":\"saved\"" >/dev/null; then
  71. _opns_rest "POST" "/service/reconfigure" "{}"
  72. _debug "Record created"
  73. else
  74. _err "Error creating record $_record_string"
  75. return 1
  76. fi
  77. return 0
  78. }
  79. rm_record() {
  80. fulldomain=$1
  81. new_challenge="$2"
  82. _info "Remove record $fulldomain with challenge: $new_challenge"
  83. _debug "Detect root zone"
  84. if ! _get_root "$fulldomain"; then
  85. _err "invalid domain"
  86. return 1
  87. fi
  88. _debug _domain "$_domain"
  89. _debug _host "$_host"
  90. _debug _domainid "$_domainid"
  91. _uuid=""
  92. if _existingchallenge "$_domain" "$_host" "$new_challenge"; then
  93. # Delete
  94. if _opns_rest "POST" "/record/delRecord/${_uuid}" "\{\}"; then
  95. if echo "$response" | _egrep_o "\"result\":\"deleted\"" >/dev/null; then
  96. _debug "Record deleted"
  97. _opns_rest "POST" "/service/reconfigure" "{}"
  98. _debug "Service reconfigured"
  99. else
  100. _err "Error deleting record $_host from domain $fulldomain"
  101. return 1
  102. fi
  103. else
  104. _err "Error requesting deletion of record $_host from domain $fulldomain"
  105. return 1
  106. fi
  107. else
  108. _info "Record not found, nothing to remove"
  109. fi
  110. return 0
  111. }
  112. #################### Private functions below ##################################
  113. #_acme-challenge.www.domain.com
  114. #returns
  115. # _domainid=domid
  116. #_domain=domain.com
  117. _get_root() {
  118. domain=$1
  119. i=2
  120. p=1
  121. if _opns_rest "GET" "/domain/searchPrimaryDomain"; then
  122. _domain_response="$response"
  123. else
  124. return 1
  125. fi
  126. while true; do
  127. h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
  128. if [ -z "$h" ]; then
  129. #not valid
  130. return 1
  131. fi
  132. _debug h "$h"
  133. lines=$(echo "$_domain_response" | sed 's/{/\n/g')
  134. for line in $lines; do
  135. id=$(echo "$line" | _egrep_o "\"uuid\":\"[a-z0-9\-]*\",\"enabled\":\"1\",\"type\":\"primary\",.*\"domainname\":\"${h}\"" | cut -d ':' -f 2 | cut -d '"' -f 2)
  136. if [ -n "$id" ]; then
  137. _debug id "$id"
  138. _host=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
  139. _domain="${h}"
  140. _domainid="${id}"
  141. return 0
  142. fi
  143. done
  144. p=$i
  145. i=$(_math "$i" + 1)
  146. done
  147. _debug "$domain not found"
  148. return 1
  149. }
  150. _opns_rest() {
  151. method=$1
  152. ep=$2
  153. data=$3
  154. #Percent encode user and token
  155. key=$(echo "$OPNs_Key" | tr -d "\n\r" | _url_encode)
  156. token=$(echo "$OPNs_Token" | tr -d "\n\r" | _url_encode)
  157. opnsense_url="https://${key}:${token}@${OPNs_Host}:${OPNs_Port:-$OPNs_DefaultPort}/api/bind${ep}"
  158. export _H1="Content-Type: application/json"
  159. _debug2 "Try to call api: https://${OPNs_Host}:${OPNs_Port:-$OPNs_DefaultPort}/api/bind${ep}"
  160. if [ ! "$method" = "GET" ]; then
  161. _debug data "$data"
  162. export _H1="Content-Type: application/json"
  163. response="$(_post "$data" "$opnsense_url" "" "$method")"
  164. else
  165. export _H1=""
  166. response="$(_get "$opnsense_url")"
  167. fi
  168. if [ "$?" != "0" ]; then
  169. _err "error $ep"
  170. return 1
  171. fi
  172. _debug2 response "$response"
  173. return 0
  174. }
  175. _build_record_string() {
  176. _record_string="{\"record\":{\"enabled\":\"1\",\"domain\":\"$1\",\"name\":\"$2\",\"type\":\"TXT\",\"value\":\"$3\"}}"
  177. }
  178. _existingchallenge() {
  179. if _opns_rest "GET" "/record/searchRecord"; then
  180. _record_response="$response"
  181. else
  182. return 1
  183. fi
  184. _uuid=""
  185. _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)
  186. if [ -n "$_uuid" ]; then
  187. _debug uuid "$_uuid"
  188. return 0
  189. fi
  190. _debug "${2}.${1} record not found"
  191. return 1
  192. }
  193. _opns_check_auth() {
  194. OPNs_Host="${OPNs_Host:-$(_readaccountconf_mutable OPNs_Host)}"
  195. OPNs_Port="${OPNs_Port:-$(_readaccountconf_mutable OPNs_Port)}"
  196. OPNs_Key="${OPNs_Key:-$(_readaccountconf_mutable OPNs_Key)}"
  197. OPNs_Token="${OPNs_Token:-$(_readaccountconf_mutable OPNs_Token)}"
  198. OPNs_Api_Insecure="${OPNs_Api_Insecure:-$(_readaccountconf_mutable OPNs_Api_Insecure)}"
  199. if [ -z "$OPNs_Host" ]; then
  200. _err "You don't specify OPNsense address."
  201. return 1
  202. else
  203. _saveaccountconf_mutable OPNs_Host "$OPNs_Host"
  204. fi
  205. if ! printf '%s' "$OPNs_Port" | grep '^[0-9]*$' >/dev/null; then
  206. _err 'OPNs_Port specified but not numeric value'
  207. return 1
  208. elif [ -z "$OPNs_Port" ]; then
  209. _info "OPNSense port not specified. Defaulting to using port $OPNs_DefaultPort"
  210. else
  211. _saveaccountconf_mutable OPNs_Port "$OPNs_Port"
  212. fi
  213. if ! printf '%s' "$OPNs_Api_Insecure" | grep '^[01]$' >/dev/null; then
  214. _err 'OPNs_Api_Insecure specified but not 0/1 value'
  215. return 1
  216. elif [ -n "$OPNs_Api_Insecure" ]; then
  217. _saveaccountconf_mutable OPNs_Api_Insecure "$OPNs_Api_Insecure"
  218. fi
  219. export HTTPS_INSECURE="${OPNs_Api_Insecure:-$OPNs_DefaultApi_Insecure}"
  220. if [ -z "$OPNs_Key" ]; then
  221. _err "you have not specified your OPNsense api key id."
  222. _err "Please set OPNs_Key and try again."
  223. return 1
  224. else
  225. _saveaccountconf_mutable OPNs_Key "$OPNs_Key"
  226. fi
  227. if [ -z "$OPNs_Token" ]; then
  228. _err "you have not specified your OPNsense token."
  229. _err "Please create OPNs_Token and try again."
  230. return 1
  231. else
  232. _saveaccountconf_mutable OPNs_Token "$OPNs_Token"
  233. fi
  234. if ! _opns_rest "GET" "/general/get"; then
  235. _err "Call to OPNsense API interface failed. Unable to access OPNsense API."
  236. return 1
  237. fi
  238. return 0
  239. }