deploy_ad-hoc_server.sh 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. #!/bin/bash
  2. # Make sure the script is run as root user
  3. if [[ ${EUID} -ne 0 ]]; then
  4. echo "This script must be run as root"
  5. exit 1
  6. fi
  7. usage () {
  8. echo "Usage: $0 -a <install|remove> -h <host> [-p <port (default 22)>]"
  9. echo " Uses Docker and SSH to build an ad-hoc server container and deploy it"
  10. echo " (via the 'install' action) to a previously configured host. The process"
  11. echo " can be reversed by use of the 'remove' action, which restores the host"
  12. echo " to its previous configuration."
  13. echo ""
  14. echo " This script must be run with root privilges (for access to the docker daemon)"
  15. echo " You will be prompted for SSH credentials and host-key authentication as needed"
  16. echo ""
  17. echo " Options:"
  18. echo " -a/--action"
  19. echo " Action. Allowed actions are: 'install', 'remove'. Mandatory"
  20. echo " -u/--user"
  21. echo " Username to connect with. Default 'root'. Optional, but user must belong to the docker group"
  22. echo " -h/--host"
  23. echo " Host (or IP Address) to connect to. Mandatory"
  24. echo " -p/--port"
  25. echo " Port to connect to. Default 22. Optional"
  26. echo ""
  27. exit 1
  28. }
  29. generate_temporary_credentials () {
  30. echo "..Generating temporary 4096 bit RSA keypair"
  31. ssh-keygen -t rsa -b 4096 -C "temp-psiphond-ad_hoc" -f psiphond-ad_hoc -N ""
  32. PUBLIC_KEY=$(cat psiphond-ad_hoc.pub)
  33. echo "${PUBLIC_KEY}" | ssh -o PreferredAuthentications=interactive,password -p $PORT $USER@$HOST cat >> $HOME/.ssh/authorized_keys
  34. if [ $? -ne 0 ]; then
  35. echo "...Failed"
  36. return 1
  37. fi
  38. }
  39. destroy_temporary_credentials () {
  40. echo "..Removing the temporary key from the remote host"
  41. PUBLIC_KEY=$(cat psiphond-ad_hoc.pub)
  42. ssh -i psiphond-ad_hoc -o IdentitiesOnly=yes -p $PORT $USER@$HOST sed -i '/temp-psiphond-ad_hoc/d' $HOME/.ssh/authorized_keys
  43. if [ $? -ne 0 ]; then
  44. echo "...Failed"
  45. return 1
  46. fi
  47. echo "..Removing the local temporary keys"
  48. rm psiphond-ad_hoc psiphond-ad_hoc.pub
  49. if [ $? -ne 0 ]; then
  50. echo "...Failed"
  51. return 1
  52. fi
  53. }
  54. docker_build_builder () {
  55. echo "..Building the docker container 'psiphond-build'"
  56. docker build -f Dockerfile-binary-builder -t psiphond-builder .
  57. if [ $? -ne 0 ]; then
  58. echo "...Failed"
  59. return 1
  60. fi
  61. }
  62. docker_build_psiphond_binary () {
  63. echo "..Building 'psiphond' binary"
  64. cd .. && docker run --rm -v $PWD/.:/go/src/github.com/Psiphon-Labs/psiphon-tunnel-core psiphond-builder
  65. if [ $? -ne 0 ]; then
  66. echo "...Failed"
  67. cd $BASE
  68. return 1
  69. fi
  70. cd $BASE
  71. stat psiphond > /dev/null 2>&1
  72. if [ $? -ne 0 ]; then
  73. echo "...'psiphond' binary file not found"
  74. return 1
  75. fi
  76. }
  77. docker_build_psiphond_container () {
  78. echo "..Building the '${CONTAINER_TAG}' container"
  79. docker build -t ${CONTAINER_TAG} .
  80. if [ $? -ne 0 ]; then
  81. echo "...Failed"
  82. return 1
  83. fi
  84. }
  85. save_image () {
  86. echo "..Saving docker image to archive"
  87. docker save ${CONTAINER_TAG} | gzip > $ARCHIVE
  88. if [ $? -ne 0 ]; then
  89. echo "...Failed"
  90. return 1
  91. fi
  92. stat $ARCHIVE > /dev/null 2>&1
  93. if [ $? -ne 0 ]; then
  94. echo "...'${ARCHIVE}' not found"
  95. return 1
  96. fi
  97. }
  98. put_and_load_image () {
  99. echo "..Copying '${ARCHIVE}' to '${HOST}:/tmp'"
  100. scp -i psiphond-ad_hoc -o IdentitiesOnly=yes -P $PORT $ARCHIVE $USER@$HOST:/tmp/
  101. if [ $? -ne 0 ]; then
  102. echo "...Failed"
  103. return 1
  104. fi
  105. echo "..Loading image into remote docker"
  106. # Variable interpolation into the remote script doesn't always work as expected, use fixed paths here instead
  107. ssh -i psiphond-ad_hoc -o IdentitiesOnly=yes -p $PORT $USER@$HOST zcat /tmp/psiphond-ad-hoc.tar.gz | docker load && rm /tmp/psiphond-ad-hoc.tar.gz
  108. if [ $? -ne 0 ]; then
  109. echo "...Failed"
  110. return 1
  111. fi
  112. }
  113. remove_image () {
  114. echo "..Removing image from remote docker"
  115. ssh -i psiphond-ad_hoc -o IdentitiesOnly=yes -p $PORT $USER@$HOST docker rmi ${CONTAINER_TAG}
  116. if [ $? -ne 0 ]; then
  117. echo "...Failed"
  118. return 1
  119. fi
  120. }
  121. put_systemd_dropin () {
  122. echo "..Creating systemd unit drop-in"
  123. cat <<- EOF > ad-hoc.conf
  124. [Service]
  125. Environment=DOCKER_CONTENT_TRUST=0
  126. # Clear previous pre-start command before setting new one
  127. # Execute these commands prior to starting the service
  128. # "-" before the command means errors are not fatal to service startup
  129. ExecStartPre=
  130. ExecStartPre=-/usr/bin/docker stop %p-run
  131. ExecStartPre=-/usr/bin/docker rm %p-run
  132. # Clear previous start command before setting new one
  133. ExecStart=
  134. ExecStart=/usr/bin/docker run --rm $CONTAINER_PORT_STRING $CONTAINER_VOLUME_STRING $CONTAINER_ULIMIT_STRING $CONTAINER_SYSCTL_STRING --name %p-run ${CONTAINER_TAG}
  135. EOF
  136. ssh -i psiphond-ad_hoc -o IdentitiesOnly=yes -p $PORT $USER@$HOST mkdir -p /etc/systemd/system/psiphond.service.d
  137. echo "..Ensuring drop-in directory is available"
  138. if [ $? -ne 0 ]; then
  139. echo "...Failed"
  140. return 1
  141. fi
  142. scp -i psiphond-ad_hoc -o IdentitiesOnly=yes -P $PORT ad-hoc.conf $USER@$HOST:/etc/systemd/system/psiphond.service.d/
  143. echo "..Copying drop-in to remote host"
  144. if [ $? -ne 0 ]; then
  145. echo "...Failed"
  146. return 1
  147. fi
  148. rm ad-hoc.conf
  149. }
  150. remove_systemd_dropin () {
  151. echo "..Removing systemd unit drop-in"
  152. ssh -i psiphond-ad_hoc -o IdentitiesOnly=yes -p $PORT $USER@$HOST [ ! -f /etc/systemd/system/psiphond.service.d/ad-hoc.conf ] || rm /etc/systemd/system/psiphond.service.d/ad-hoc.conf
  153. if [ $? -ne 0 ]; then
  154. echo "...Failed"
  155. return 1
  156. fi
  157. }
  158. reload_systemd () {
  159. echo "..Reloading systemd unit file cache"
  160. ssh -i psiphond-ad_hoc -o IdentitiesOnly=yes -p $PORT $USER@$HOST systemctl daemon-reload
  161. if [ $? -ne 0 ]; then
  162. echo "...Failed"
  163. return 1
  164. fi
  165. }
  166. restart_psiphond () {
  167. echo "..Restarting the 'psiphond' service"
  168. ssh -i psiphond-ad_hoc -o IdentitiesOnly=yes -p $PORT $USER@$HOST systemctl restart psiphond
  169. if [ $? -ne 0 ]; then
  170. echo "...Failed"
  171. return 1
  172. fi
  173. }
  174. # Locate and change to the directory containing the script
  175. BASE=$( cd "$(dirname "$0")" ; pwd -P )
  176. cd $BASE
  177. # Validate that we're in a git repository and store the revision
  178. REV=$(git rev-parse --short HEAD)
  179. if [ $? -ne 0 ]; then
  180. echo "Could not store git revision, aborting"
  181. exit 1
  182. fi
  183. # Parse command line arguments
  184. while [[ $# -gt 1 ]]; do
  185. key="$1"
  186. case $key in
  187. -a|--action)
  188. ACTION="$2"
  189. shift
  190. if [ "${ACTION}" != "install" ] && [ "${ACTION}" != "remove" ]; then
  191. echo "Got: '${ACTION}', Expected one of: 'install', or 'remove', aborting."
  192. exit 1
  193. fi
  194. ;;
  195. -u|--user)
  196. USER="$2"
  197. shift
  198. ;;
  199. -h|--host)
  200. HOST="$2"
  201. shift
  202. ;;
  203. -p|--port)
  204. PORT="$2"
  205. shift
  206. ;;
  207. *)
  208. usage
  209. ;;
  210. esac
  211. shift
  212. done
  213. # Validate all mandatory parameters were set
  214. if [ -z $ACTION ]; then
  215. echo "Action is a required parameter, aborting."
  216. echo ""
  217. usage
  218. fi
  219. if [ -z $HOST ]; then
  220. echo "Host is a required parameter, aborting."
  221. echo ""
  222. usage
  223. fi
  224. # Set default values for unset optional paramters
  225. if [ -z $USER ]; then
  226. USER=root
  227. fi
  228. if [ -z $PORT ]; then
  229. PORT=22
  230. fi
  231. # Set up other global variables
  232. TIMESTAMP=$(date +'%Y-%m-%d_%H-%M')
  233. CONTAINER_TAG="psiphond/ad-hoc:${TIMESTAMP}"
  234. ARCHIVE="psiphond-ad-hoc.tar.gz"
  235. # Display choices and pause
  236. echo "[$(date)] Ad-Hoc psiphond deploy starting."
  237. echo ""
  238. echo "Configuration:"
  239. echo " - Action: ${ACTION}"
  240. echo " - User: ${USER}"
  241. echo " - Host: ${HOST}"
  242. echo " - Port: ${PORT}"
  243. echo " - Containter Tag: ${CONTAINER_TAG}"
  244. echo " - Archive Name: ${ARCHIVE}"
  245. echo ""
  246. echo "Pausing 5 seconds to allow for ^C prior to starting"
  247. sleep 5
  248. generate_temporary_credentials
  249. if [ $? -ne 0 ]; then
  250. echo "Inability to generate temporary credentials is fatal, aborting"
  251. exit 1
  252. fi
  253. if [ "${ACTION}" == "install" ]; then
  254. docker_build_builder
  255. if [ $? -ne 0 ]; then
  256. echo "...Aborting"
  257. destroy_temporary_credentials
  258. exit 1
  259. fi
  260. docker_build_psiphond_binary
  261. if [ $? -ne 0 ]; then
  262. echo "...Aborting"
  263. destroy_temporary_credentials
  264. exit 1
  265. fi
  266. docker_build_psiphond_container
  267. if [ $? -ne 0 ]; then
  268. echo "...Aborting"
  269. destroy_temporary_credentials
  270. exit 1
  271. fi
  272. save_image
  273. if [ $? -ne 0 ]; then
  274. echo "...Aborting"
  275. destroy_temporary_credentials
  276. exit 1
  277. fi
  278. put_and_load_image
  279. if [ $? -ne 0 ]; then
  280. echo "...Aborting"
  281. destroy_temporary_credentials
  282. exit 1
  283. fi
  284. put_systemd_dropin
  285. if [ $? -ne 0 ]; then
  286. echo "...Aborting"
  287. destroy_temporary_credentials
  288. exit 1
  289. fi
  290. reload_systemd
  291. if [ $? -ne 0 ]; then
  292. echo "...Aborting"
  293. destroy_temporary_credentials
  294. exit 1
  295. fi
  296. restart_psiphond
  297. if [ $? -ne 0 ]; then
  298. echo "...Aborting"
  299. destroy_temporary_credentials
  300. exit 1
  301. fi
  302. elif [ "${ACTION}" == "remove" ]; then
  303. remove_systemd_dropin
  304. if [ $? -ne 0 ]; then
  305. echo "...Aborting"
  306. destroy_temporary_credentials
  307. exit 1
  308. fi
  309. reload_systemd
  310. if [ $? -ne 0 ]; then
  311. echo "...Aborting"
  312. destroy_temporary_credentials
  313. exit 1
  314. fi
  315. restart_psiphond
  316. if [ $? -ne 0 ]; then
  317. echo "...Aborting"
  318. destroy_temporary_credentials
  319. exit 1
  320. fi
  321. remove_image
  322. if [ $? -ne 0 ]; then
  323. echo "...Aborting"
  324. destroy_temporary_credentials
  325. exit 1
  326. fi
  327. else
  328. echo "Parameter validation passed, but action was not 'install' or 'remove', aborting"
  329. exit 1
  330. fi
  331. destroy_temporary_credentials
  332. echo "[$(date)] Ad-Hoc psiphond deploy ended."