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.

456 lines
15 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 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="qnap" # defaults to domain name, support multiple servers with optional port (eg. "host1 host2:8022")
  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. (*:*) _host=${DEPLOY_SSH_SERVER%:*} _port=${DEPLOY_SSH_SERVER##*:};;
  170. (*) _host=$DEPLOY_SSH_SERVER _port=;;
  171. esac
  172. _info "Deploy certificates to remote server $DEPLOY_SSH_USER@$_host:$_port"
  173. if [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
  174. _backupprefix="$DEPLOY_SSH_BACKUP_PATH/$_cdomain-backup"
  175. _backupdir="$_backupprefix-$(_utc_date | tr ' ' '-')"
  176. # run cleanup on the backup directory, erase all older
  177. # than 180 days (15552000 seconds).
  178. _cmdstr="{ now=\"\$(date -u +%s)\"; for fn in $_backupprefix*; \
  179. do if [ -d \"\$fn\" ] && [ \"\$(expr \$now - \$(date -ur \$fn +%s) )\" -ge \"15552000\" ]; \
  180. then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; done; }; $_cmdstr"
  181. # Alternate version of above... _cmdstr="find $_backupprefix* -type d -mtime +180 2>/dev/null | xargs rm -rf; $_cmdstr"
  182. # Create our backup directory for overwritten cert files.
  183. _cmdstr="mkdir -p $_backupdir; $_cmdstr"
  184. _info "Backup of old certificate files will be placed in remote directory $_backupdir"
  185. _info "Backup directories erased after 180 days."
  186. if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
  187. if ! _ssh_remote_cmd "$_cmdstr"; then
  188. return $_err_code
  189. fi
  190. _cmdstr=""
  191. fi
  192. fi
  193. if [ -n "$DEPLOY_SSH_KEYFILE" ]; then
  194. if [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
  195. # backup file we are about to overwrite.
  196. _cmdstr="$_cmdstr cp $DEPLOY_SSH_KEYFILE $_backupdir >/dev/null;"
  197. if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
  198. if ! _ssh_remote_cmd "$_cmdstr"; then
  199. return $_err_code
  200. fi
  201. _cmdstr=""
  202. fi
  203. fi
  204. # copy new key into file.
  205. if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
  206. # scp the file
  207. if ! _scp_remote_cmd "$_ckey" "$DEPLOY_SSH_KEYFILE"; then
  208. return $_err_code
  209. fi
  210. else
  211. # ssh echo to the file
  212. _cmdstr="$_cmdstr echo \"$(cat "$_ckey")\" > $DEPLOY_SSH_KEYFILE;"
  213. _info "will copy private key to remote file $DEPLOY_SSH_KEYFILE"
  214. if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
  215. if ! _ssh_remote_cmd "$_cmdstr"; then
  216. return $_err_code
  217. fi
  218. _cmdstr=""
  219. fi
  220. fi
  221. fi
  222. if [ -n "$DEPLOY_SSH_CERTFILE" ]; then
  223. _pipe=">"
  224. if [ "$DEPLOY_SSH_CERTFILE" = "$DEPLOY_SSH_KEYFILE" ]; then
  225. # if filename is same as previous file then append.
  226. _pipe=">>"
  227. elif [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
  228. # backup file we are about to overwrite.
  229. _cmdstr="$_cmdstr cp $DEPLOY_SSH_CERTFILE $_backupdir >/dev/null;"
  230. if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
  231. if ! _ssh_remote_cmd "$_cmdstr"; then
  232. return $_err_code
  233. fi
  234. _cmdstr=""
  235. fi
  236. fi
  237. # copy new certificate into file.
  238. if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
  239. # scp the file
  240. _local_cert_file=$(_mktemp)
  241. if [ "$DEPLOY_SSH_CERTFILE" = "$DEPLOY_SSH_KEYFILE" ]; then
  242. cat "$_ckey" >> "$_local_cert_file"
  243. fi
  244. cat "$_ccert" >> "$_local_cert_file"
  245. if ! _scp_remote_cmd "$_local_cert_file" "$DEPLOY_SSH_CERTFILE"; then
  246. return $_err_code
  247. fi
  248. else
  249. # ssh echo to the file
  250. _cmdstr="$_cmdstr echo \"$(cat "$_ccert")\" $_pipe $DEPLOY_SSH_CERTFILE;"
  251. _info "will copy certificate to remote file $DEPLOY_SSH_CERTFILE"
  252. if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
  253. if ! _ssh_remote_cmd "$_cmdstr"; then
  254. return $_err_code
  255. fi
  256. _cmdstr=""
  257. fi
  258. fi
  259. fi
  260. if [ -n "$DEPLOY_SSH_CAFILE" ]; then
  261. _pipe=">"
  262. if [ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_KEYFILE" ] ||
  263. [ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_CERTFILE" ]; then
  264. # if filename is same as previous file then append.
  265. _pipe=">>"
  266. elif [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
  267. # backup file we are about to overwrite.
  268. _cmdstr="$_cmdstr cp $DEPLOY_SSH_CAFILE $_backupdir >/dev/null;"
  269. if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
  270. if ! _ssh_remote_cmd "$_cmdstr"; then
  271. return $_err_code
  272. fi
  273. _cmdstr=""
  274. fi
  275. fi
  276. # copy new certificate into file.
  277. if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
  278. # scp the file
  279. _local_ca_file=$(_mktemp)
  280. if [ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_KEYFILE" ]; then
  281. cat "$_ckey" >> "$_local_ca_file"
  282. fi
  283. if [ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_CERTFILE" ]; then
  284. cat "$_ccert" >> "$_local_ca_file"
  285. fi
  286. cat "$_cca" >>"$_local_ca_file"
  287. if ! _scp_remote_cmd "$_local_ca_file" "$DEPLOY_SSH_CAFILE"; then
  288. return $_err_code
  289. fi
  290. else
  291. # ssh echo to the file
  292. _cmdstr="$_cmdstr echo \"$(cat "$_cca")\" $_pipe $DEPLOY_SSH_CAFILE;"
  293. _info "will copy CA file to remote file $DEPLOY_SSH_CAFILE"
  294. if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
  295. if ! _ssh_remote_cmd "$_cmdstr"; then
  296. return $_err_code
  297. fi
  298. _cmdstr=""
  299. fi
  300. fi
  301. fi
  302. if [ -n "$DEPLOY_SSH_FULLCHAIN" ]; then
  303. _pipe=">"
  304. if [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_KEYFILE" ] ||
  305. [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_CERTFILE" ] ||
  306. [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_CAFILE" ]; then
  307. # if filename is same as previous file then append.
  308. _pipe=">>"
  309. elif [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
  310. # backup file we are about to overwrite.
  311. _cmdstr="$_cmdstr cp $DEPLOY_SSH_FULLCHAIN $_backupdir >/dev/null;"
  312. if [ "$DEPLOY_SSH_FULLCHAIN" = "yes" ]; then
  313. if ! _ssh_remote_cmd "$_cmdstr"; then
  314. return $_err_code
  315. fi
  316. _cmdstr=""
  317. fi
  318. fi
  319. # copy new certificate into file.
  320. if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
  321. # scp the file
  322. _local_full_file=$(_mktemp)
  323. if [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_KEYFILE" ]; then
  324. cat "$_ckey" >> "$_local_full_file"
  325. fi
  326. if [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_CERTFILE" ]; then
  327. cat "$_ccert" >> "$_local_full_file"
  328. fi
  329. if [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_CAFILE" ]; then
  330. cat "$_cca" >> "$_local_full_file"
  331. fi
  332. cat "$_cfullchain" >> "$_local_full_file"
  333. if ! _scp_remote_cmd "$_local_full_file" "$DEPLOY_SSH_FULLCHAIN"; then
  334. return $_err_code
  335. fi
  336. else
  337. # ssh echo to the file
  338. _cmdstr="$_cmdstr echo \"$(cat "$_cfullchain")\" $_pipe $DEPLOY_SSH_FULLCHAIN;"
  339. _info "will copy fullchain to remote file $DEPLOY_SSH_FULLCHAIN"
  340. if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
  341. if ! _ssh_remote_cmd "$_cmdstr"; then
  342. return $_err_code
  343. fi
  344. _cmdstr=""
  345. fi
  346. fi
  347. fi
  348. # cleanup local files if any
  349. if [ -f "$_local_cert_file" ]; then
  350. rm -f "$_local_cert_file"
  351. fi
  352. if [ -f "$_local_ca_file" ]; then
  353. rm -f "$_local_ca_file"
  354. fi
  355. if [ -f "$_local_full_file" ]; then
  356. rm -f "$_local_full_file"
  357. fi
  358. if [ -n "$DEPLOY_SSH_REMOTE_CMD" ]; then
  359. _cmdstr="$_cmdstr $DEPLOY_SSH_REMOTE_CMD;"
  360. _info "Will execute remote command $DEPLOY_SSH_REMOTE_CMD"
  361. if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
  362. if ! _ssh_remote_cmd "$_cmdstr"; then
  363. return $_err_code
  364. fi
  365. _cmdstr=""
  366. fi
  367. fi
  368. # if commands not all sent in multiple calls then all commands sent in a single SSH call now...
  369. if [ -n "$_cmdstr" ]; then
  370. if ! _ssh_remote_cmd "$_cmdstr"; then
  371. return $_err_code
  372. fi
  373. fi
  374. # cleanup in case all is ok
  375. return 0
  376. }
  377. #cmd
  378. _ssh_remote_cmd() {
  379. _cmd="$1"
  380. _ssh_cmd="$DEPLOY_SSH_CMD"
  381. if [ -n "$_port" ]; then
  382. _ssh_cmd="$_ssh_cmd -p $_port"
  383. fi
  384. _secure_debug "Remote commands to execute: $_cmd"
  385. _info "Submitting sequence of commands to remote server by $_ssh_cmd"
  386. # quotations in bash cmd below intended. Squash travis spellcheck error
  387. # shellcheck disable=SC2029
  388. $_ssh_cmd "$DEPLOY_SSH_USER@$_host" sh -c "'$_cmd'"
  389. _err_code="$?"
  390. if [ "$_err_code" != "0" ]; then
  391. _err "Error code $_err_code returned from ssh"
  392. fi
  393. return $_err_code
  394. }
  395. # cmd scp
  396. _scp_remote_cmd() {
  397. _src=$1
  398. _dest=$2
  399. _scp_cmd="$DEPLOY_SSH_SCP_CMD"
  400. if [ -n "$_port" ]; then
  401. _scp_cmd="$_scp_cmd -P $_port"
  402. fi
  403. _secure_debug "Remote copy source $_src to destination $_dest"
  404. _info "Submitting secure copy by $_scp_cmd"
  405. $_scp_cmd "$_src" "$DEPLOY_SSH_USER"@"$_host":"$_dest"
  406. _err_code="$?"
  407. if [ "$_err_code" != "0" ]; then
  408. _err "Error code $_err_code returned from scp"
  409. fi
  410. return $_err_code
  411. }