You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

510 lines
15 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. #!/usr/bin/env sh
  2. #Author StefanAbl
  3. #Usage specify a private keyfile to use with dynv6 'export KEY="path/to/keyfile"'
  4. #or use the HTTP REST API by by specifying a token 'export DYNV6_TOKEN="value"
  5. #if no keyfile is specified, you will be asked if you want to create one in /home/$USER/.ssh/dynv6 and /home/$USER/.ssh/dynv6.pub
  6. dynv6_api="https://dynv6.com/api/v2"
  7. ######## Public functions #####################
  8. # Please Read this guide first: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide
  9. #Usage: dns_dynv6_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
  10. dns_dynv6_add() {
  11. fulldomain=$1
  12. txtvalue=$2
  13. _info "Using dynv6 api"
  14. _debug fulldomain "$fulldomain"
  15. _debug txtvalue "$txtvalue"
  16. <<<<<<< HEAD
  17. <<<<<<< HEAD
  18. =======
  19. >>>>>>> no supporting HTTP API as well
  20. _get_authentication
  21. if [ "$dynv6_token" ]; then
  22. _dns_dynv6_add_http
  23. return $?
  24. <<<<<<< HEAD
  25. <<<<<<< HEAD
  26. =======
  27. _get_keyfile
  28. _info "using keyfile $dynv6_keyfile"
  29. _your_hosts="$(ssh -i "$dynv6_keyfile" api@dynv6.com hosts)"
  30. if ! _get_domain "$fulldomain" "$_your_hosts"; then
  31. _err "Host not found on your account"
  32. return 1
  33. fi
  34. _debug "found host on your account"
  35. returnval="$(ssh -i "$dynv6_keyfile" api@dynv6.com hosts \""$_host"\" records set \""$_record"\" txt data \""$txtvalue"\")"
  36. _debug "Dynv6 returend this after record was added: $returnval"
  37. if _contains "$returnval" "created"; then
  38. return 0
  39. elif _contains "$returnval" "updated"; then
  40. return 0
  41. >>>>>>> first attempt to make travis happy
  42. else
  43. =======
  44. else
  45. >>>>>>> no supporting HTTP API as well
  46. =======
  47. else
  48. >>>>>>> formatting
  49. _info "using key file $dynv6_keyfile"
  50. _your_hosts="$(ssh -i "$dynv6_keyfile" api@dynv6.com hosts)"
  51. if ! _get_domain "$fulldomain" "$_your_hosts"; then
  52. _err "Host not found on your account"
  53. return 1
  54. fi
  55. _debug "found host on your account"
  56. returnval="$(ssh -i "$dynv6_keyfile" api@dynv6.com hosts \""$_host"\" records set \""$_record"\" txt data \""$txtvalue"\")"
  57. _debug "Dynv6 returned this after record was added: $returnval"
  58. if _contains "$returnval" "created"; then
  59. return 0
  60. elif _contains "$returnval" "updated"; then
  61. return 0
  62. else
  63. _err "Something went wrong! it does not seem like the record was added successfully"
  64. return 1
  65. fi
  66. return 1
  67. fi
  68. return 1
  69. }
  70. #Usage: fulldomain txtvalue
  71. #Remove the txt record after validation.
  72. dns_dynv6_rm() {
  73. fulldomain=$1
  74. txtvalue=$2
  75. _info "Using dynv6 API"
  76. _debug fulldomain "$fulldomain"
  77. _debug txtvalue "$txtvalue"
  78. <<<<<<< HEAD
  79. <<<<<<< HEAD
  80. _get_authentication
  81. if [ "$dynv6_token" ]; then
  82. _dns_dynv6_rm_http
  83. return $?
  84. else
  85. =======
  86. _get_authentication
  87. if [ "$dynv6_token" ]; then
  88. _dns_dynv6_rm_http
  89. return $?
  90. <<<<<<< HEAD
  91. else
  92. >>>>>>> no supporting HTTP API as well
  93. =======
  94. else
  95. >>>>>>> formatting
  96. _info "using key file $dynv6_keyfile"
  97. _your_hosts="$(ssh -i "$dynv6_keyfile" api@dynv6.com hosts)"
  98. if ! _get_domain "$fulldomain" "$_your_hosts"; then
  99. _err "Host not found on your account"
  100. return 1
  101. fi
  102. _debug "found host on your account"
  103. _info "$(ssh -i "$dynv6_keyfile" api@dynv6.com hosts "\"$_host\"" records del "\"$_record\"" txt)"
  104. return 0
  105. <<<<<<< HEAD
  106. fi
  107. =======
  108. _get_keyfile
  109. _info "using keyfile $dynv6_keyfile"
  110. _your_hosts="$(ssh -i "$dynv6_keyfile" api@dynv6.com hosts)"
  111. if ! _get_domain "$fulldomain" "$_your_hosts"; then
  112. _err "Host not found on your account"
  113. return 1
  114. fi
  115. _debug "found host on your account"
  116. _info "$(ssh -i "$dynv6_keyfile" api@dynv6.com hosts "\"$_host\"" records del "\"$_record\"" txt)"
  117. return 0
  118. >>>>>>> first attempt to make travis happy
  119. =======
  120. fi
  121. >>>>>>> no supporting HTTP API as well
  122. }
  123. #################### Private functions below ##################################
  124. #Usage: No Input required
  125. #returns
  126. #dynv6_keyfile the path to the new key file that has been generated
  127. _generate_new_key() {
  128. dynv6_keyfile="$(eval echo ~"$USER")/.ssh/dynv6"
  129. _info "Path to key file used: $dynv6_keyfile"
  130. if [ ! -f "$dynv6_keyfile" ] && [ ! -f "$dynv6_keyfile.pub" ]; then
  131. _debug "generating key in $dynv6_keyfile and $dynv6_keyfile.pub"
  132. ssh-keygen -f "$dynv6_keyfile" -t ssh-ed25519 -N ''
  133. else
  134. _err "There is already a file in $dynv6_keyfile or $dynv6_keyfile.pub"
  135. return 1
  136. fi
  137. }
  138. #Usage: _acme-challenge.www.example.dynv6.net "$_your_hosts"
  139. #where _your_hosts is the output of ssh -i ~/.ssh/dynv6.pub api@dynv6.com hosts
  140. #returns
  141. #_host= example.dynv6.net
  142. #_record=_acme-challenge.www
  143. #aborts if not a valid domain
  144. _get_domain() {
  145. #_your_hosts="$(ssh -i ~/.ssh/dynv6.pub api@dynv6.com hosts)"
  146. _full_domain="$1"
  147. _your_hosts="$2"
  148. _your_hosts="$(echo "$_your_hosts" | awk '/\./ {print $1}')"
  149. for l in $_your_hosts; do
  150. #echo "host: $l"
  151. if test "${_full_domain#*$l}" != "$_full_domain"; then
  152. _record="${_full_domain%.$l}"
  153. _host=$l
  154. _debug "The host is $_host and the record $_record"
  155. return 0
  156. fi
  157. done
  158. _err "Either their is no such host on your dnyv6 account or it cannot be accessed with this key"
  159. return 1
  160. }
  161. # Usage: No input required
  162. #returns
  163. #dynv6_keyfile path to the key that will be used
  164. _get_authentication() {
  165. <<<<<<< HEAD
  166. <<<<<<< HEAD
  167. dynv6_token="${DYNV6_TOKEN:-$(_readaccountconf_mutable dynv6_token)}"
  168. if [ "$dynv6_token" ]; then
  169. _debug "Found HTTP Token. Going to use the HTTP API and not the SSH API"
  170. if [ "$DYNV6_TOKEN" ]; then
  171. _saveaccountconf_mutable dynv6_token "$dynv6_token"
  172. fi
  173. else
  174. =======
  175. if [ "$DYNV6_TOKEN" ]; then
  176. _debug "Going to use the HTTP Token you specifed and saving it for futur use"
  177. _saveaccountconf_mutable dynv6_token "$DYNV6_TOKEN"
  178. dynv6_token="$DYNV6_TOKEN"
  179. elif [ "$(_readaccountconf_mutable dynv6_token)" ]; then
  180. _debug "Found a previously used HTTP token going to use that"
  181. dynv6_token="$(_readaccountconf_mutable dynv6_token)"
  182. else
  183. >>>>>>> no supporting HTTP API as well
  184. =======
  185. if [ "$DYNV6_TOKEN" ]; then
  186. _debug "Going to use the HTTP Token you specifed and saving it for futur use"
  187. _saveaccountconf_mutable dynv6_token "$DYNV6_TOKEN"
  188. dynv6_token="$DYNV6_TOKEN"
  189. elif [ "$(_readaccountconf_mutable dynv6_token)" ]; then
  190. _debug "Found a previously used HTTP token going to use that"
  191. dynv6_token="$(_readaccountconf_mutable dynv6_token)"
  192. else
  193. >>>>>>> formatting
  194. _debug "no HTTP token found. Looking for an SSH key"
  195. dynv6_keyfile="${dynv6_keyfile:-$(_readaccountconf_mutable dynv6_keyfile)}"
  196. _debug "Your key is $dynv6_keyfile"
  197. if [ -z "$dynv6_keyfile" ]; then
  198. if [ -z "$KEY" ]; then
  199. _err "You did not specify a key to use with dynv6"
  200. _info "Creating new dynv6 API key to add to dynv6.com"
  201. _generate_new_key
  202. _info "Please add this key to dynv6.com $(cat "$dynv6_keyfile.pub")"
  203. _info "Hit Enter to continue"
  204. read -r _
  205. #save the credentials to the account conf file.
  206. else
  207. dynv6_keyfile="$KEY"
  208. fi
  209. _saveaccountconf_mutable dynv6_keyfile "$dynv6_keyfile"
  210. <<<<<<< HEAD
  211. fi
  212. fi
  213. }
  214. _dns_dynv6_add_http() {
  215. _debug "Got HTTP token form _get_authentication method. Going to use the HTTP API"
  216. if ! _get_zone_id "$fulldomain"; then
  217. _err "Could not find a matching zone for $fulldomain. Maybe your HTTP Token is not authorized to access the zone"
  218. return 1
  219. fi
  220. _get_zone_name "$_zone_id"
  221. record="${fulldomain%%.$_zone_name}"
  222. _set_record TXT "$record" "$txtvalue"
  223. if _contains "$response" "$txtvalue"; then
  224. _info "Successfully added record"
  225. return 0
  226. else
  227. _err "Something went wrong while adding the record"
  228. return 1
  229. fi
  230. }
  231. _dns_dynv6_rm_http() {
  232. _debug "Got HTTP token form _get_authentication method. Going to use the HTTP API"
  233. if ! _get_zone_id "$fulldomain"; then
  234. _err "Could not find a matching zone for $fulldomain. Maybe your HTTP Token is not authorized to access the zone"
  235. return 1
  236. fi
  237. _get_zone_name "$_zone_id"
  238. record="${fulldomain%%.$_zone_name}"
  239. _get_record_id "$_zone_id" "$record" "$txtvalue"
  240. _del_record "$_zone_id" "$_record_id"
  241. if [ -z "$response" ]; then
  242. _info "Successfully deleted record"
  243. return 0
  244. else
  245. _err "Something went wrong while deleting the record"
  246. return 1
  247. fi
  248. }
  249. #get the zoneid for a specifc record or zone
  250. #usage: _get_zone_id §record
  251. #where $record is the record to get the id for
  252. #returns _zone_id the id of the zone
  253. _get_zone_id() {
  254. record="$1"
  255. _debug "getting zone id for $record"
  256. _dynv6_rest GET zones
  257. zones="$(echo "$response" | tr '}' '\n' | tr ',' '\n' | grep name | sed 's/\[//g' | tr -d '{' | tr -d '"')"
  258. #echo $zones
  259. selected=""
  260. for z in $zones; do
  261. z="${z#name:}"
  262. _debug zone: "$z"
  263. if _contains "$record" "$z"; then
  264. _debug "$z found in $record"
  265. selected="$z"
  266. fi
  267. done
  268. if [ -z "$selected" ]; then
  269. _err "no zone found"
  270. return 1
  271. fi
  272. zone_id="$(echo "$response" | tr '}' '\n' | grep "$selected" | tr ',' '\n' | grep id | tr -d '"')"
  273. _zone_id="${zone_id#id:}"
  274. _debug "zone id: $_zone_id"
  275. }
  276. _get_zone_name() {
  277. _zone_id="$1"
  278. _dynv6_rest GET zones/"$_zone_id"
  279. _zone_name="$(echo "$response" | tr ',' '\n' | tr -d '{' | grep name | tr -d '"')"
  280. _zone_name="${_zone_name#name:}"
  281. }
  282. #usaage _get_record_id $zone_id $record
  283. # where zone_id is thevalue returned by _get_zone_id
  284. # and record ist in the form _acme.www for an fqdn of _acme.www.example.com
  285. # returns _record_id
  286. _get_record_id() {
  287. _zone_id="$1"
  288. record="$2"
  289. value="$3"
  290. _dynv6_rest GET "zones/$_zone_id/records"
  291. if ! _get_record_id_from_response "$response"; then
  292. _err "no such record $record found in zone $_zone_id"
  293. return 1
  294. fi
  295. }
  296. _get_record_id_from_response() {
  297. response="$1"
  298. _record_id="$(echo "$response" | tr '}' '\n' | grep "\"name\":\"$record\"" | grep "\"data\":\"$value\"" | tr ',' '\n' | grep id | tr -d '"' | tr -d 'id:')"
  299. #_record_id="${_record_id#id:}"
  300. if [ -z "$_record_id" ]; then
  301. _err "no such record: $record found in zone $_zone_id"
  302. return 1
  303. fi
  304. _debug "record id: $_record_id"
  305. return 0
  306. }
  307. #usage: _set_record TXT _acme_challenge.www longvalue 12345678
  308. #zone id is optional can also be set as vairable bevor calling this method
  309. _set_record() {
  310. type="$1"
  311. record="$2"
  312. value="$3"
  313. if [ "$4" ]; then
  314. _zone_id="$4"
  315. fi
  316. data="{\"name\": \"$record\", \"data\": \"$value\", \"type\": \"$type\"}"
  317. #data='{ "name": "acme.test.thorn.dynv6.net", "type": "A", "data": "192.168.0.1"}'
  318. echo "$data"
  319. #"{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"
  320. _dynv6_rest POST "zones/$_zone_id/records" "$data"
  321. }
  322. _del_record() {
  323. _zone_id=$1
  324. _record_id=$2
  325. _dynv6_rest DELETE zones/"$_zone_id"/records/"$_record_id"
  326. }
  327. _dynv6_rest() {
  328. m=$1 #method GET,POST,DELETE or PUT
  329. ep="$2" #the endpoint
  330. data="$3"
  331. _debug "$ep"
  332. token_trimmed=$(echo "$dynv6_token" | tr -d '"')
  333. export _H1="Authorization: Bearer $token_trimmed"
  334. export _H2="Content-Type: application/json"
  335. if [ "$m" != "GET" ]; then
  336. _debug data "$data"
  337. response="$(_post "$data" "$dynv6_api/$ep" "" "$m")"
  338. else
  339. response="$(_get "$dynv6_api/$ep")"
  340. =======
  341. fi
  342. >>>>>>> no supporting HTTP API as well
  343. fi
  344. }
  345. _dns_dynv6_add_http() {
  346. _debug "Got HTTP token form _get_authentication method. Going to use the HTTP API"
  347. if ! _get_zone_id "$fulldomain"; then
  348. _err "Could not find a matching zone for $fulldomain. Maybe your HTTP Token is not authorized to access the zone"
  349. return 1
  350. fi
  351. _get_zone_name "$_zone_id"
  352. record="${fulldomain%%.$_zone_name}"
  353. _set_record TXT "$record" "$txtvalue"
  354. if _contains "$response" "$txtvalue"; then
  355. _info "Successfully added record"
  356. return 0
  357. else
  358. _err "Something went wrong while adding the record"
  359. return 1
  360. fi
  361. }
  362. _dns_dynv6_rm_http() {
  363. _debug "Got HTTP token form _get_authentication method. Going to use the HTTP API"
  364. if ! _get_zone_id "$fulldomain"; then
  365. _err "Could not find a matching zone for $fulldomain. Maybe your HTTP Token is not authorized to access the zone"
  366. return 1
  367. fi
  368. _get_zone_name "$_zone_id"
  369. record="${fulldomain%%.$_zone_name}"
  370. _get_record_id "$_zone_id" "$record" "$txtvalue"
  371. _del_record "$_zone_id" "$_record_id"
  372. if [ -z "$response" ]; then
  373. _info "Successfully deleted record"
  374. return 0
  375. else
  376. _err "Something went wrong while deleting the record"
  377. return 1
  378. fi
  379. }
  380. #get the zoneid for a specifc record or zone
  381. #usage: _get_zone_id §record
  382. #where $record is the record to get the id for
  383. #returns _zone_id the id of the zone
  384. _get_zone_id() {
  385. record="$1"
  386. _debug "getting zone id for $record"
  387. _dynv6_rest GET zones
  388. zones="$(echo "$response" | tr '}' '\n' | tr ',' '\n' | grep name | sed 's/\[//g' | tr -d '{' | tr -d '"')"
  389. #echo $zones
  390. selected=""
  391. for z in $zones; do
  392. z="${z#name:}"
  393. _debug zone: "$z"
  394. if _contains "$record" "$z"; then
  395. _debug "$z found in $record"
  396. selected="$z"
  397. fi
  398. done
  399. if [ -z "$selected" ]; then
  400. _err "no zone found"
  401. return 1
  402. fi
  403. zone_id="$(echo "$response" | tr '}' '\n' | grep "$selected" | tr ',' '\n' | grep id | tr -d '"')"
  404. _zone_id="${zone_id#id:}"
  405. _debug "zone id: $_zone_id"
  406. }
  407. _get_zone_name() {
  408. _zone_id="$1"
  409. _dynv6_rest GET zones/"$_zone_id"
  410. _zone_name="$(echo "$response" | tr ',' '\n' | tr -d '{' | grep name | tr -d '"')"
  411. _zone_name="${_zone_name#name:}"
  412. }
  413. #usaage _get_record_id $zone_id $record
  414. # where zone_id is thevalue returned by _get_zone_id
  415. # and record ist in the form _acme.www for an fqdn of _acme.www.example.com
  416. # returns _record_id
  417. _get_record_id() {
  418. _zone_id="$1"
  419. record="$2"
  420. value="$3"
  421. _dynv6_rest GET "zones/$_zone_id/records"
  422. if ! _get_record_id_from_response "$response"; then
  423. _err "no such record $record found in zone $_zone_id"
  424. return 1
  425. fi
  426. }
  427. _get_record_id_from_response() {
  428. response="$1"
  429. _record_id="$(echo "$response" | tr '}' '\n' | grep "\"name\":\"$record\"" | grep "\"data\":\"$value\"" | tr ',' '\n' | grep id | tr -d '"' | tr -d 'id:')"
  430. #_record_id="${_record_id#id:}"
  431. if [ -z "$_record_id" ]; then
  432. _err "no such record: $record found in zone $_zone_id"
  433. return 1
  434. fi
  435. _debug "record id: $_record_id"
  436. return 0
  437. }
  438. #usage: _set_record TXT _acme_challenge.www longvalue 12345678
  439. #zone id is optional can also be set as vairable bevor calling this method
  440. _set_record() {
  441. type="$1"
  442. record="$2"
  443. value="$3"
  444. if [ "$4" ]; then
  445. _zone_id="$4"
  446. fi
  447. data="{\"name\": \"$record\", \"data\": \"$value\", \"type\": \"$type\"}"
  448. #data='{ "name": "acme.test.thorn.dynv6.net", "type": "A", "data": "192.168.0.1"}'
  449. echo "$data"
  450. #"{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"
  451. _dynv6_rest POST "zones/$_zone_id/records" "$data"
  452. }
  453. _del_record() {
  454. _zone_id=$1
  455. _record_id=$2
  456. _dynv6_rest DELETE zones/"$_zone_id"/records/"$_record_id"
  457. }
  458. _dynv6_rest() {
  459. m=$1 #method GET,POST,DELETE or PUT
  460. ep="$2" #the endpoint
  461. data="$3"
  462. _debug "$ep"
  463. token_trimmed=$(echo "$dynv6_token" | tr -d '"')
  464. export _H1="Authorization: Bearer $token_trimmed"
  465. export _H2="Content-Type: application/json"
  466. if [ "$m" != "GET" ]; then
  467. _debug data "$data"
  468. response="$(_post "$data" "$dynv6_api/$ep" "" "$m")"
  469. else
  470. response="$(_get "$dynv6_api/$ep")"
  471. fi
  472. if [ "$?" != "0" ]; then
  473. _err "error $ep"
  474. return 1
  475. fi
  476. _debug2 response "$response"
  477. return 0
  478. }