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.

462 lines
15 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
2 years ago
8 years ago
8 years ago
8 years ago
2 years ago
2 years ago
8 years ago
2 years ago
2 years ago
8 years ago
2 years ago
2 years ago
2 years ago
2 years ago
8 years ago
8 years ago
8 years ago
8 years ago
  1. #!/usr/bin/env sh
  2. # Script to deploy certificates to remote server by SSH
  3. # Note that SSH must be able to login to remote host without a password...
  4. # SSH Keys must have been exchanged with the remote host. Validate and
  5. # test that you can login to USER@SERVER from the host running acme.sh before
  6. # using this script.
  7. #
  8. # The following variables exported from environment will be used.
  9. # If not set then values previously saved in domain.conf file are used.
  10. #
  11. # Only a username is required. All others are optional.
  12. #
  13. # The following examples are for QNAP NAS running QTS 4.2
  14. # export DEPLOY_SSH_CMD="" # defaults to "ssh -T"
  15. # export DEPLOY_SSH_USER="admin" # required
  16. # export DEPLOY_SSH_SERVER="host1 host2:8022 192.168.0.1:9022" # defaults to domain name, support multiple servers with optional port
  17. # export DEPLOY_SSH_KEYFILE="/etc/stunnel/stunnel.pem"
  18. # export DEPLOY_SSH_CERTFILE="/etc/stunnel/stunnel.pem"
  19. # export DEPLOY_SSH_CAFILE="/etc/stunnel/uca.pem"
  20. # export DEPLOY_SSH_FULLCHAIN=""
  21. # export DEPLOY_SSH_REMOTE_CMD="/etc/init.d/stunnel.sh restart"
  22. # export DEPLOY_SSH_BACKUP="" # yes or no, default to yes or previously saved value
  23. # export DEPLOY_SSH_BACKUP_PATH=".acme_ssh_deploy" # path on remote system. Defaults to .acme_ssh_deploy
  24. # export DEPLOY_SSH_MULTI_CALL="" # yes or no, default to no or previously saved value
  25. # export DEPLOY_SSH_USE_SCP="" yes or no, default to no
  26. # export DEPLOY_SSH_SCP_CMD="" defaults to "scp -q"
  27. #
  28. ######## Public functions #####################
  29. #domain keyfile certfile cafile fullchain
  30. ssh_deploy() {
  31. _cdomain="$1"
  32. _ckey="$2"
  33. _ccert="$3"
  34. _cca="$4"
  35. _cfullchain="$5"
  36. _deploy_ssh_servers=""
  37. _debug _cdomain "$_cdomain"
  38. _debug _ckey "$_ckey"
  39. _debug _ccert "$_ccert"
  40. _debug _cca "$_cca"
  41. _debug _cfullchain "$_cfullchain"
  42. # USER is required to login by SSH to remote host.
  43. _migratedeployconf Le_Deploy_ssh_user DEPLOY_SSH_USER
  44. _getdeployconf DEPLOY_SSH_USER
  45. _debug2 DEPLOY_SSH_USER "$DEPLOY_SSH_USER"
  46. if [ -z "$DEPLOY_SSH_USER" ]; then
  47. _err "DEPLOY_SSH_USER not defined."
  48. return 1
  49. fi
  50. _savedeployconf DEPLOY_SSH_USER "$DEPLOY_SSH_USER"
  51. # SERVER is optional. If not provided then use _cdomain
  52. _migratedeployconf Le_Deploy_ssh_server DEPLOY_SSH_SERVER
  53. _getdeployconf DEPLOY_SSH_SERVER
  54. _debug2 DEPLOY_SSH_SERVER "$DEPLOY_SSH_SERVER"
  55. if [ -z "$DEPLOY_SSH_SERVER" ]; then
  56. DEPLOY_SSH_SERVER="$_cdomain"
  57. fi
  58. _savedeployconf DEPLOY_SSH_SERVER "$DEPLOY_SSH_SERVER"
  59. # CMD is optional. If not provided then use ssh
  60. _migratedeployconf Le_Deploy_ssh_cmd DEPLOY_SSH_CMD
  61. _getdeployconf DEPLOY_SSH_CMD
  62. _debug2 DEPLOY_SSH_CMD "$DEPLOY_SSH_CMD"
  63. if [ -z "$DEPLOY_SSH_CMD" ]; then
  64. DEPLOY_SSH_CMD="ssh -T"
  65. fi
  66. _savedeployconf DEPLOY_SSH_CMD "$DEPLOY_SSH_CMD"
  67. # BACKUP is optional. If not provided then default to previously saved value or yes.
  68. _migratedeployconf Le_Deploy_ssh_backup DEPLOY_SSH_BACKUP
  69. _getdeployconf DEPLOY_SSH_BACKUP
  70. _debug2 DEPLOY_SSH_BACKUP "$DEPLOY_SSH_BACKUP"
  71. if [ -z "$DEPLOY_SSH_BACKUP" ]; then
  72. DEPLOY_SSH_BACKUP="yes"
  73. fi
  74. _savedeployconf DEPLOY_SSH_BACKUP "$DEPLOY_SSH_BACKUP"
  75. # BACKUP_PATH is optional. If not provided then default to previously saved value or .acme_ssh_deploy
  76. _migratedeployconf Le_Deploy_ssh_backup_path DEPLOY_SSH_BACKUP_PATH
  77. _getdeployconf DEPLOY_SSH_BACKUP_PATH
  78. _debug2 DEPLOY_SSH_BACKUP_PATH "$DEPLOY_SSH_BACKUP_PATH"
  79. if [ -z "$DEPLOY_SSH_BACKUP_PATH" ]; then
  80. DEPLOY_SSH_BACKUP_PATH=".acme_ssh_deploy"
  81. fi
  82. _savedeployconf DEPLOY_SSH_BACKUP_PATH "$DEPLOY_SSH_BACKUP_PATH"
  83. # MULTI_CALL is optional. If not provided then default to previously saved
  84. # value (which may be undefined... equivalent to "no").
  85. _migratedeployconf Le_Deploy_ssh_multi_call DEPLOY_SSH_MULTI_CALL
  86. _getdeployconf DEPLOY_SSH_MULTI_CALL
  87. _debug2 DEPLOY_SSH_MULTI_CALL "$DEPLOY_SSH_MULTI_CALL"
  88. if [ -z "$DEPLOY_SSH_MULTI_CALL" ]; then
  89. DEPLOY_SSH_MULTI_CALL="no"
  90. fi
  91. _savedeployconf DEPLOY_SSH_MULTI_CALL "$DEPLOY_SSH_MULTI_CALL"
  92. # KEYFILE is optional.
  93. # If provided then private key will be copied to provided filename.
  94. _migratedeployconf Le_Deploy_ssh_keyfile DEPLOY_SSH_KEYFILE
  95. _getdeployconf DEPLOY_SSH_KEYFILE
  96. _debug2 DEPLOY_SSH_KEYFILE "$DEPLOY_SSH_KEYFILE"
  97. if [ -n "$DEPLOY_SSH_KEYFILE" ]; then
  98. _savedeployconf DEPLOY_SSH_KEYFILE "$DEPLOY_SSH_KEYFILE"
  99. fi
  100. # CERTFILE is optional.
  101. # If provided then certificate will be copied or appended to provided filename.
  102. _migratedeployconf Le_Deploy_ssh_certfile DEPLOY_SSH_CERTFILE
  103. _getdeployconf DEPLOY_SSH_CERTFILE
  104. _debug2 DEPLOY_SSH_CERTFILE "$DEPLOY_SSH_CERTFILE"
  105. if [ -n "$DEPLOY_SSH_CERTFILE" ]; then
  106. _savedeployconf DEPLOY_SSH_CERTFILE "$DEPLOY_SSH_CERTFILE"
  107. fi
  108. # CAFILE is optional.
  109. # If provided then CA intermediate certificate will be copied or appended to provided filename.
  110. _migratedeployconf Le_Deploy_ssh_cafile DEPLOY_SSH_CAFILE
  111. _getdeployconf DEPLOY_SSH_CAFILE
  112. _debug2 DEPLOY_SSH_CAFILE "$DEPLOY_SSH_CAFILE"
  113. if [ -n "$DEPLOY_SSH_CAFILE" ]; then
  114. _savedeployconf DEPLOY_SSH_CAFILE "$DEPLOY_SSH_CAFILE"
  115. fi
  116. # FULLCHAIN is optional.
  117. # If provided then fullchain certificate will be copied or appended to provided filename.
  118. _migratedeployconf Le_Deploy_ssh_fullchain DEPLOY_SSH_FULLCHAIN
  119. _getdeployconf DEPLOY_SSH_FULLCHAIN
  120. _debug2 DEPLOY_SSH_FULLCHAIN "$DEPLOY_SSH_FULLCHAIN"
  121. if [ -n "$DEPLOY_SSH_FULLCHAIN" ]; then
  122. _savedeployconf DEPLOY_SSH_FULLCHAIN "$DEPLOY_SSH_FULLCHAIN"
  123. fi
  124. # REMOTE_CMD is optional.
  125. # If provided then this command will be executed on remote host.
  126. _migratedeployconf Le_Deploy_ssh_remote_cmd DEPLOY_SSH_REMOTE_CMD
  127. _getdeployconf DEPLOY_SSH_REMOTE_CMD
  128. _debug2 DEPLOY_SSH_REMOTE_CMD "$DEPLOY_SSH_REMOTE_CMD"
  129. if [ -n "$DEPLOY_SSH_REMOTE_CMD" ]; then
  130. _savedeployconf DEPLOY_SSH_REMOTE_CMD "$DEPLOY_SSH_REMOTE_CMD"
  131. fi
  132. # USE_SCP is optional. If not provided then default to previously saved
  133. # value (which may be undefined... equivalent to "no").
  134. _getdeployconf DEPLOY_SSH_USE_SCP
  135. _debug2 DEPLOY_SSH_USE_SCP "$DEPLOY_SSH_USE_SCP"
  136. if [ -z "$DEPLOY_SSH_USE_SCP" ]; then
  137. DEPLOY_SSH_USE_SCP="no"
  138. fi
  139. _savedeployconf DEPLOY_SSH_USE_SCP "$DEPLOY_SSH_USE_SCP"
  140. # SCP_CMD is optional. If not provided then use scp
  141. _getdeployconf DEPLOY_SSH_SCP_CMD
  142. _debug2 DEPLOY_SSH_SCP_CMD "$DEPLOY_SSH_SCP_CMD"
  143. if [ -z "$DEPLOY_SSH_SCP_CMD" ]; then
  144. DEPLOY_SSH_SCP_CMD="scp -q"
  145. fi
  146. _savedeployconf DEPLOY_SSH_SCP_CMD "$DEPLOY_SSH_SCP_CMD"
  147. if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
  148. DEPLOY_SSH_MULTI_CALL="yes"
  149. _info "Using scp as alternate method for copying files. Multicall Mode is implicit"
  150. elif [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
  151. _info "Using MULTI_CALL mode... Required commands sent in multiple calls to remote host"
  152. else
  153. _info "Required commands batched and sent in single call to remote host"
  154. fi
  155. _deploy_ssh_servers="$DEPLOY_SSH_SERVER"
  156. for DEPLOY_SSH_SERVER in $_deploy_ssh_servers; do
  157. _ssh_deploy
  158. done
  159. }
  160. _ssh_deploy() {
  161. _err_code=0
  162. _cmdstr=""
  163. _backupprefix=""
  164. _backupdir=""
  165. _local_cert_file=""
  166. _local_ca_file=""
  167. _local_full_file=""
  168. case $DEPLOY_SSH_SERVER in
  169. *:*)
  170. _host=${DEPLOY_SSH_SERVER%:*}
  171. _port=${DEPLOY_SSH_SERVER##*:}
  172. ;;
  173. *)
  174. _host=$DEPLOY_SSH_SERVER
  175. _port=
  176. ;;
  177. esac
  178. _info "Deploy certificates to remote server $DEPLOY_SSH_USER@$_host:$_port"
  179. if [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
  180. _backupprefix="$DEPLOY_SSH_BACKUP_PATH/$_cdomain-backup"
  181. _backupdir="$_backupprefix-$(_utc_date | tr ' ' '-')"
  182. # run cleanup on the backup directory, erase all older
  183. # than 180 days (15552000 seconds).
  184. _cmdstr="{ now=\"\$(date -u +%s)\"; for fn in $_backupprefix*; \
  185. do if [ -d \"\$fn\" ] && [ \"\$(expr \$now - \$(date -ur \$fn +%s) )\" -ge \"15552000\" ]; \
  186. then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; done; }; $_cmdstr"
  187. # Alternate version of above... _cmdstr="find $_backupprefix* -type d -mtime +180 2>/dev/null | xargs rm -rf; $_cmdstr"
  188. # Create our backup directory for overwritten cert files.
  189. _cmdstr="mkdir -p $_backupdir; $_cmdstr"
  190. _info "Backup of old certificate files will be placed in remote directory $_backupdir"
  191. _info "Backup directories erased after 180 days."
  192. if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
  193. if ! _ssh_remote_cmd "$_cmdstr"; then
  194. return $_err_code
  195. fi
  196. _cmdstr=""
  197. fi
  198. fi
  199. if [ -n "$DEPLOY_SSH_KEYFILE" ]; then
  200. if [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
  201. # backup file we are about to overwrite.
  202. _cmdstr="$_cmdstr cp $DEPLOY_SSH_KEYFILE $_backupdir >/dev/null;"
  203. if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
  204. if ! _ssh_remote_cmd "$_cmdstr"; then
  205. return $_err_code
  206. fi
  207. _cmdstr=""
  208. fi
  209. fi
  210. # copy new key into file.
  211. if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
  212. # scp the file
  213. if ! _scp_remote_cmd "$_ckey" "$DEPLOY_SSH_KEYFILE"; then
  214. return $_err_code
  215. fi
  216. else
  217. # ssh echo to the file
  218. _cmdstr="$_cmdstr echo \"$(cat "$_ckey")\" > $DEPLOY_SSH_KEYFILE;"
  219. _info "will copy private key to remote file $DEPLOY_SSH_KEYFILE"
  220. if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
  221. if ! _ssh_remote_cmd "$_cmdstr"; then
  222. return $_err_code
  223. fi
  224. _cmdstr=""
  225. fi
  226. fi
  227. fi
  228. if [ -n "$DEPLOY_SSH_CERTFILE" ]; then
  229. _pipe=">"
  230. if [ "$DEPLOY_SSH_CERTFILE" = "$DEPLOY_SSH_KEYFILE" ]; then
  231. # if filename is same as previous file then append.
  232. _pipe=">>"
  233. elif [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
  234. # backup file we are about to overwrite.
  235. _cmdstr="$_cmdstr cp $DEPLOY_SSH_CERTFILE $_backupdir >/dev/null;"
  236. if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
  237. if ! _ssh_remote_cmd "$_cmdstr"; then
  238. return $_err_code
  239. fi
  240. _cmdstr=""
  241. fi
  242. fi
  243. # copy new certificate into file.
  244. if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
  245. # scp the file
  246. _local_cert_file=$(_mktemp)
  247. if [ "$DEPLOY_SSH_CERTFILE" = "$DEPLOY_SSH_KEYFILE" ]; then
  248. cat "$_ckey" >>"$_local_cert_file"
  249. fi
  250. cat "$_ccert" >>"$_local_cert_file"
  251. if ! _scp_remote_cmd "$_local_cert_file" "$DEPLOY_SSH_CERTFILE"; then
  252. return $_err_code
  253. fi
  254. else
  255. # ssh echo to the file
  256. _cmdstr="$_cmdstr echo \"$(cat "$_ccert")\" $_pipe $DEPLOY_SSH_CERTFILE;"
  257. _info "will copy certificate to remote file $DEPLOY_SSH_CERTFILE"
  258. if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
  259. if ! _ssh_remote_cmd "$_cmdstr"; then
  260. return $_err_code
  261. fi
  262. _cmdstr=""
  263. fi
  264. fi
  265. fi
  266. if [ -n "$DEPLOY_SSH_CAFILE" ]; then
  267. _pipe=">"
  268. if [ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_KEYFILE" ] ||
  269. [ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_CERTFILE" ]; then
  270. # if filename is same as previous file then append.
  271. _pipe=">>"
  272. elif [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
  273. # backup file we are about to overwrite.
  274. _cmdstr="$_cmdstr cp $DEPLOY_SSH_CAFILE $_backupdir >/dev/null;"
  275. if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
  276. if ! _ssh_remote_cmd "$_cmdstr"; then
  277. return $_err_code
  278. fi
  279. _cmdstr=""
  280. fi
  281. fi
  282. # copy new certificate into file.
  283. if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
  284. # scp the file
  285. _local_ca_file=$(_mktemp)
  286. if [ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_KEYFILE" ]; then
  287. cat "$_ckey" >>"$_local_ca_file"
  288. fi
  289. if [ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_CERTFILE" ]; then
  290. cat "$_ccert" >>"$_local_ca_file"
  291. fi
  292. cat "$_cca" >>"$_local_ca_file"
  293. if ! _scp_remote_cmd "$_local_ca_file" "$DEPLOY_SSH_CAFILE"; then
  294. return $_err_code
  295. fi
  296. else
  297. # ssh echo to the file
  298. _cmdstr="$_cmdstr echo \"$(cat "$_cca")\" $_pipe $DEPLOY_SSH_CAFILE;"
  299. _info "will copy CA file to remote file $DEPLOY_SSH_CAFILE"
  300. if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
  301. if ! _ssh_remote_cmd "$_cmdstr"; then
  302. return $_err_code
  303. fi
  304. _cmdstr=""
  305. fi
  306. fi
  307. fi
  308. if [ -n "$DEPLOY_SSH_FULLCHAIN" ]; then
  309. _pipe=">"
  310. if [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_KEYFILE" ] ||
  311. [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_CERTFILE" ] ||
  312. [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_CAFILE" ]; then
  313. # if filename is same as previous file then append.
  314. _pipe=">>"
  315. elif [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
  316. # backup file we are about to overwrite.
  317. _cmdstr="$_cmdstr cp $DEPLOY_SSH_FULLCHAIN $_backupdir >/dev/null;"
  318. if [ "$DEPLOY_SSH_FULLCHAIN" = "yes" ]; then
  319. if ! _ssh_remote_cmd "$_cmdstr"; then
  320. return $_err_code
  321. fi
  322. _cmdstr=""
  323. fi
  324. fi
  325. # copy new certificate into file.
  326. if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
  327. # scp the file
  328. _local_full_file=$(_mktemp)
  329. if [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_KEYFILE" ]; then
  330. cat "$_ckey" >>"$_local_full_file"
  331. fi
  332. if [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_CERTFILE" ]; then
  333. cat "$_ccert" >>"$_local_full_file"
  334. fi
  335. if [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_CAFILE" ]; then
  336. cat "$_cca" >>"$_local_full_file"
  337. fi
  338. cat "$_cfullchain" >>"$_local_full_file"
  339. if ! _scp_remote_cmd "$_local_full_file" "$DEPLOY_SSH_FULLCHAIN"; then
  340. return $_err_code
  341. fi
  342. else
  343. # ssh echo to the file
  344. _cmdstr="$_cmdstr echo \"$(cat "$_cfullchain")\" $_pipe $DEPLOY_SSH_FULLCHAIN;"
  345. _info "will copy fullchain to remote file $DEPLOY_SSH_FULLCHAIN"
  346. if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
  347. if ! _ssh_remote_cmd "$_cmdstr"; then
  348. return $_err_code
  349. fi
  350. _cmdstr=""
  351. fi
  352. fi
  353. fi
  354. # cleanup local files if any
  355. if [ -f "$_local_cert_file" ]; then
  356. rm -f "$_local_cert_file"
  357. fi
  358. if [ -f "$_local_ca_file" ]; then
  359. rm -f "$_local_ca_file"
  360. fi
  361. if [ -f "$_local_full_file" ]; then
  362. rm -f "$_local_full_file"
  363. fi
  364. if [ -n "$DEPLOY_SSH_REMOTE_CMD" ]; then
  365. _cmdstr="$_cmdstr $DEPLOY_SSH_REMOTE_CMD;"
  366. _info "Will execute remote command $DEPLOY_SSH_REMOTE_CMD"
  367. if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
  368. if ! _ssh_remote_cmd "$_cmdstr"; then
  369. return $_err_code
  370. fi
  371. _cmdstr=""
  372. fi
  373. fi
  374. # if commands not all sent in multiple calls then all commands sent in a single SSH call now...
  375. if [ -n "$_cmdstr" ]; then
  376. if ! _ssh_remote_cmd "$_cmdstr"; then
  377. return $_err_code
  378. fi
  379. fi
  380. # cleanup in case all is ok
  381. return 0
  382. }
  383. #cmd
  384. _ssh_remote_cmd() {
  385. _cmd="$1"
  386. _ssh_cmd="$DEPLOY_SSH_CMD"
  387. if [ -n "$_port" ]; then
  388. _ssh_cmd="$_ssh_cmd -p $_port"
  389. fi
  390. _secure_debug "Remote commands to execute: $_cmd"
  391. _info "Submitting sequence of commands to remote server by $_ssh_cmd"
  392. # quotations in bash cmd below intended. Squash travis spellcheck error
  393. # shellcheck disable=SC2029
  394. $_ssh_cmd "$DEPLOY_SSH_USER@$_host" sh -c "'$_cmd'"
  395. _err_code="$?"
  396. if [ "$_err_code" != "0" ]; then
  397. _err "Error code $_err_code returned from ssh"
  398. fi
  399. return $_err_code
  400. }
  401. # cmd scp
  402. _scp_remote_cmd() {
  403. _src=$1
  404. _dest=$2
  405. _scp_cmd="$DEPLOY_SSH_SCP_CMD"
  406. if [ -n "$_port" ]; then
  407. _scp_cmd="$_scp_cmd -P $_port"
  408. fi
  409. _secure_debug "Remote copy source $_src to destination $_dest"
  410. _info "Submitting secure copy by $_scp_cmd"
  411. $_scp_cmd "$_src" "$DEPLOY_SSH_USER"@"$_host":"$_dest"
  412. _err_code="$?"
  413. if [ "$_err_code" != "0" ]; then
  414. _err "Error code $_err_code returned from scp"
  415. fi
  416. return $_err_code
  417. }