nm-upgrade.sh 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. #!/bin/bash
  2. # make sure current version is 0.17.1 before continuing
  3. check_version() {
  4. IMG_TAG=$(yq -r '.services.netmaker.image' docker-compose.yml)
  5. if [[ "$IMG_TAG" == *"v0.17.1"* ]]; then
  6. echo "version is $IMG_TAG"
  7. else
  8. echo "error, current version is $IMG_TAG"
  9. echo "please upgrade to v0.17.1 in order to use the upgrade script"
  10. exit 1
  11. fi
  12. }
  13. # wait a number of seconds, print a log
  14. wait_seconds() {
  15. for ((a=1; a <= $1; a++))
  16. do
  17. echo ". . ."
  18. sleep 1
  19. done
  20. }
  21. # confirm a choice, or exit script
  22. confirm() {
  23. while true; do
  24. read -p 'Does everything look right? [y/n]: ' yn
  25. case $yn in
  26. [Yy]* ) override="true"; break;;
  27. [Nn]* ) echo "exiting..."; exit 1;;
  28. * ) echo "Please answer yes or no.";;
  29. esac
  30. done
  31. }
  32. # install system dependencies necessary for script to run
  33. install_dependencies() {
  34. OS=$(uname)
  35. is_ubuntu=$(sudo cat /etc/lsb-release | grep "Ubuntu")
  36. if [ "${is_ubuntu}" != "" ]; then
  37. dependencies="yq jq wireguard jq docker.io docker-compose"
  38. update_cmd='apt update'
  39. install_cmd='snap install'
  40. elif [ -f /etc/debian_version ]; then
  41. dependencies="yq jq wireguard jq docker.io docker-compose"
  42. update_cmd='apt update'
  43. install_cmd='apt install -y'
  44. elif [ -f /etc/centos-release ]; then
  45. dependencies="wireguard jq docker.io docker-compose"
  46. update_cmd='yum update'
  47. install_cmd='yum install -y'
  48. elif [ -f /etc/fedora-release ]; then
  49. dependencies="wireguard jq docker.io docker-compose"
  50. update_cmd='dnf update'
  51. install_cmd='dnf install -y'
  52. elif [ -f /etc/redhat-release ]; then
  53. dependencies="wireguard jq docker.io docker-compose"
  54. update_cmd='yum update'
  55. install_cmd='yum install -y'
  56. elif [ -f /etc/arch-release ]; then
  57. dependecies="wireguard-tools jq docker.io docker-compose netclient"
  58. update_cmd='pacman -Sy'
  59. install_cmd='pacman -S --noconfirm'
  60. else
  61. echo "OS not supported for automatic install"
  62. exit 1
  63. fi
  64. set -- $dependencies
  65. ${update_cmd}
  66. set +e
  67. while [ -n "$1" ]; do
  68. is_installed=$(dpkg-query -W --showformat='${Status}\n' $1 | grep "install ok installed")
  69. if [ "${is_installed}" != "" ]; then
  70. echo " " $1 is installed
  71. else
  72. echo " " $1 is not installed. Attempting install.
  73. ${install_cmd} $1
  74. sleep 5
  75. if [ "${OS}" = "OpenWRT" ] || [ "${OS}" = "TurrisOS" ]; then
  76. is_installed=$(opkg list-installed $1 | grep $1)
  77. else
  78. is_installed=$(dpkg-query -W --showformat='${Status}\n' $1 | grep "install ok installed")
  79. fi
  80. if [ "${is_installed}" != "" ]; then
  81. echo " " $1 is installed
  82. elif [ -x "$(command -v $1)" ]; then
  83. echo " " $1 is installed
  84. else
  85. echo " " FAILED TO INSTALL $1
  86. echo " " This may break functionality.
  87. fi
  88. fi
  89. shift
  90. done
  91. set -e
  92. echo "-----------------------------------------------------"
  93. echo "dependency install complete"
  94. echo "-----------------------------------------------------"
  95. }
  96. # retrieve server settings from existing compose file
  97. collect_server_settings() {
  98. MASTER_KEY=$(yq -r .services.netmaker.environment.MASTER_KEY docker-compose.yml)
  99. echo "-----------------------------------------------------"
  100. echo "Is $MASTER_KEY the correct master key for your Netmaker installation?"
  101. echo "-----------------------------------------------------"
  102. select mkey_option in "yes" "no (enter manually)"; do
  103. case $REPLY in
  104. 1)
  105. echo "using $MASTER_KEY for master key"
  106. break
  107. ;;
  108. 2)
  109. read -p "Enter Master Key: " mkey
  110. MASTER_KEY=$mkey
  111. echo "using $MASTER_KEY"
  112. break
  113. ;;
  114. *) echo "invalid option $REPLY, choose 1 or 2";;
  115. esac
  116. done
  117. SERVER_HTTP_HOST=$(yq -r .services.netmaker.environment.SERVER_HTTP_HOST docker-compose.yml)
  118. echo "-----------------------------------------------------"
  119. echo "Is $SERVER_HTTP_HOST the correct api endpoint for your Netmaker installation?"
  120. echo "-----------------------------------------------------"
  121. select endpoint_option in "yes" "no (enter manually)"; do
  122. case $REPLY in
  123. 1)
  124. echo "using $SERVER_HTTP_HOST for api endpoint"
  125. break
  126. ;;
  127. 2)
  128. read -p "Enter API Endpoint: " endpoint
  129. SERVER_HTTP_HOST=$endpoint
  130. echo "using $SERVER_HTTP_HOST"
  131. break
  132. ;;
  133. *) echo "invalid option $REPLY";;
  134. esac
  135. done
  136. BROKER_NAME=$(yq -r .services.netmaker.environment.SERVER_NAME docker-compose.yml)
  137. echo "-----------------------------------------------------"
  138. echo "Is $BROKER_NAME the correct domain for your MQ broker?"
  139. echo "-----------------------------------------------------"
  140. select broker_option in "yes" "no (enter manually)"; do
  141. case $REPLY in
  142. 1)
  143. echo "using $BROKER_NAME for endpoint"
  144. break
  145. ;;
  146. 2)
  147. read -p "Enter Broker Domain: " broker
  148. BROKER_NAME=$broker
  149. echo "using $BROKER_NAME"
  150. break
  151. ;;
  152. *) echo "invalid option $REPLY";;
  153. esac
  154. done
  155. SERVER_NAME=${BROKER_NAME#"broker."}
  156. echo "-----------------------------------------------------"
  157. echo "Is $SERVER_NAME the correct base domain for your installation?"
  158. echo "-----------------------------------------------------"
  159. select domain_option in "yes" "no (enter manually)"; do
  160. case $REPLY in
  161. 1)
  162. echo "using $SERVER_NAME for domain"
  163. break
  164. ;;
  165. 2)
  166. read -p "Enter Server Domain: " broker
  167. SERVER_NAME=$server
  168. echo "using $SERVER_NAME"
  169. break
  170. ;;
  171. *) echo "invalid option $REPLY";;
  172. esac
  173. done
  174. STUN_NAME="stun.$SERVER_NAME"
  175. echo "-----------------------------------------------------"
  176. echo "Netmaker v0.18.0 requires a new DNS entry for $STUN_NAME."
  177. echo "Please confirm this is added to your DNS provider before continuing"
  178. echo "(note: this is not required if using an nip.io address)"
  179. echo "-----------------------------------------------------"
  180. confirm
  181. }
  182. # get existing server node configuration
  183. collect_node_settings() {
  184. curl -s -H "Authorization: Bearer $MASTER_KEY" -H 'Content-Type: application/json' https://$SERVER_HTTP_HOST/api/nodes | jq -c '[ .[] | select(.isserver=="yes") ]' > nodejson.tmp
  185. NODE_LEN=$(jq length nodejson.tmp)
  186. HAS_INGRESS="no"
  187. if [ "$NODE_LEN" -gt 0 ]; then
  188. echo "===SERVER NODES==="
  189. for i in $(seq 1 $NODE_LEN); do
  190. NUM=$(($i-1))
  191. echo " SERVER NODE $NUM:"
  192. echo " network: $(jq -r ".[$NUM].network" ./nodejson.tmp)"
  193. echo " name: $(jq -r ".[$NUM].name" ./nodejson.tmp)"
  194. echo " private ipv4: $(jq -r ".[$NUM].address" ./nodejson.tmp)"
  195. echo " private ipv6: $(jq -r ".[$NUM].address6" ./nodejson.tmp)"
  196. echo " is egress: $(jq -r ".[$NUM].isegressgateway" ./nodejson.tmp)"
  197. if [[ $(jq -r ".[$NUM].isegressgateway" ./nodejson.tmp) == "yes" ]]; then
  198. echo " egress range: $(jq -r ".[$NUM].egressgatewayranges" ./nodejson.tmp)"
  199. fi
  200. echo " is ingress: $(jq -r ".[$NUM].isingressgateway" ./nodejson.tmp)"
  201. if [[ $(jq -r ".[$NUM].isingressgateway" ./nodejson.tmp) == "yes" ]]; then
  202. HAS_INGRESS="yes"
  203. fi
  204. echo " is relay: $(jq -r ".[$NUM].isrelay" ./nodejson.tmp)"
  205. if [[ $(jq -r ".[$NUM].isrelay" ./nodejson.tmp) == "yes" ]]; then
  206. HAS_RELAY="yes"
  207. echo " relay addrs: $(jq -r ".[$NUM].relayaddrs" ./nodejson.tmp | tr -d '[]\n"[:space:]')"
  208. fi
  209. echo " is failover: $(jq -r ".[$NUM].failover" ./nodejson.tmp)"
  210. echo " ------------"
  211. done
  212. echo "=================="
  213. else
  214. echo "no nodes to parse"
  215. fi
  216. echo "Please confirm that the above output matches the server nodes in your Netmaker server."
  217. confirm
  218. if [[ $HAS_INGRESS == "yes" ]]; then
  219. echo "WARNING: Your server contains an Ingress Gateway. After upgrading, existing Ext Clients will be lost and must be recreated. Please confirm that you would like to continue."
  220. confirm
  221. fi
  222. }
  223. # set compose file with proper values
  224. set_compose() {
  225. # DEV_TEMP - Temporary instructions for testing
  226. sed -i "s/v0.17.1/testing/g" /root/docker-compose.yml
  227. # RELEASE_REPLACE - Use this once release is ready
  228. #sed -i "s/v0.17.1/v0.18.0/g" /root/docker-compose.yml
  229. yq ".services.netmaker.environment.SERVER_NAME = \"$SERVER_NAME\"" -i /root/docker-compose.yml
  230. yq ".services.netmaker.environment += {\"BROKER_NAME\": \"$BROKER_NAME\"}" -i /root/docker-compose.yml
  231. yq ".services.netmaker.environment += {\"STUN_NAME\": \"$STUN_NAME\"}" -i /root/docker-compose.yml
  232. yq ".services.netmaker.environment += {\"STUN_PORT\": \"3478\"}" -i /root/docker-compose.yml
  233. }
  234. start_containers() {
  235. docker-compose -f /root/docker-compose.yml up -d
  236. }
  237. # make sure caddy is working
  238. test_caddy() {
  239. echo "Testing Caddy setup (please be patient, this may take 1-2 minutes)"
  240. for i in 1 2 3 4 5 6 7 8
  241. do
  242. curlresponse=$(curl -vIs https://${SERVER_HTTP_HOST} 2>&1)
  243. if [[ "$i" == 8 ]]; then
  244. echo " Caddy is having an issue setting up certificates, please investigate (docker logs caddy)"
  245. echo " Exiting..."
  246. exit 1
  247. elif [[ "$curlresponse" == *"failed to verify the legitimacy of the server"* ]]; then
  248. echo " Certificates not yet configured, retrying..."
  249. elif [[ "$curlresponse" == *"left intact"* ]]; then
  250. echo " Certificates ok"
  251. break
  252. else
  253. secs=$(($i*5+10))
  254. echo " Issue establishing connection...retrying in $secs seconds..."
  255. fi
  256. sleep $secs
  257. done
  258. }
  259. setup_netclient() {
  260. # DEV_TEMP - Temporary instructions for testing
  261. wget https://fileserver.netmaker.org/testing/netclient
  262. chmod +x netclient
  263. ./netclient install
  264. # RELEASE_REPLACE - Use this once release is ready
  265. # if [ -f /etc/debian_version ]; then
  266. # curl -sL 'https://apt.netmaker.org/gpg.key' | sudo tee /etc/apt/trusted.gpg.d/netclient.asc
  267. # curl -sL 'https://apt.netmaker.org/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/netclient.list
  268. # sudo apt update
  269. # sudo apt install netclient
  270. # elif [ -f /etc/centos-release ]; then
  271. # curl -sL 'https://rpm.netmaker.org/gpg.key' | sudo tee /tmp/gpg.key
  272. # curl -sL 'https://rpm.netmaker.org/netclient-repo' | sudo tee /etc/yum.repos.d/netclient.repo
  273. # sudo rpm --import /tmp/gpg.key
  274. # sudo dnf check-update
  275. # sudo dnf install netclient
  276. # elif [ -f /etc/fedora-release ]; then
  277. # curl -sL 'https://rpm.netmaker.org/gpg.key' | sudo tee /tmp/gpg.key
  278. # curl -sL 'https://rpm.netmaker.org/netclient-repo' | sudo tee /etc/yum.repos.d/netclient.repo
  279. # sudo rpm --import /tmp/gpg.key
  280. # sudo dnf check-update
  281. # sudo dnf install netclient
  282. # elif [ -f /etc/redhat-release ]; then
  283. # curl -sL 'https://rpm.netmaker.org/gpg.key' | sudo tee /tmp/gpg.key
  284. # curl -sL 'https://rpm.netmaker.org/netclient-repo' | sudo tee /etc/yum.repos.d/netclient.repo
  285. # sudo rpm --import /tmp/gpg.key
  286. # sudo dnf check-update(
  287. # sudo dnf install netclient
  288. # elif [ -f /etc/arch-release ]; then
  289. # yay -S netclient
  290. # else
  291. # echo "OS not supported for automatic install"
  292. # exit 1
  293. # fi
  294. # if [ -z "${install_cmd}" ]; then
  295. # echo "OS unsupported for automatic dependency install"
  296. # exit 1
  297. # fi
  298. }
  299. setup_nmctl() {
  300. # DEV_TEMP - Temporary instructions for testing
  301. wget https://fileserver.netmaker.org/testing/nmctl
  302. # RELEASE_REPLACE - Use this once release is ready
  303. # wget https://github.com/gravitl/netmaker/releases/download/v0.17.1/nmctl
  304. chmod +x nmctl
  305. echo "using server $SERVER_HTTP_HOST"
  306. echo "using master key $MASTER_KEY"
  307. ./nmctl context set default --endpoint="https://$SERVER_HTTP_HOST" --master_key="$MASTER_KEY"
  308. ./nmctl context use default
  309. RESP=$(./nmctl network list)
  310. if [[ $RESP == *"unauthorized"* ]]; then
  311. echo "Unable to properly configure NMCTL, exiting..."
  312. exit 1
  313. fi
  314. }
  315. join_networks() {
  316. NODE_LEN=$(jq length nodejson.tmp)
  317. HAS_INGRESS="no"
  318. if [ "$NODE_LEN" -gt 0 ]; then
  319. for i in $(seq 1 $NODE_LEN); do
  320. NUM=$(($i-1))
  321. NETWORK=$(jq -r ".[$NUM].network" ./nodejson.tmp)
  322. echo " joining network $NETWORK with following settings. Please confirm:"
  323. echo " network: $(jq -r ".[$NUM].network" ./nodejson.tmp)"
  324. echo " name: $(jq -r ".[$NUM].name" ./nodejson.tmp)"
  325. echo " private ipv4: $(jq -r ".[$NUM].address" ./nodejson.tmp)"
  326. echo " private ipv6: $(jq -r ".[$NUM].address6" ./nodejson.tmp)"
  327. echo " is egress: $(jq -r ".[$NUM].isegressgateway" ./nodejson.tmp)"
  328. if [[ $(jq -r ".[$NUM].isegressgateway" ./nodejson.tmp) == "yes" ]]; then
  329. HAS_EGRESS="yes"
  330. echo " egress range: $(jq -r ".[$NUM].egressgatewayranges" ./nodejson.tmp)"
  331. fi
  332. echo " is ingress: $(jq -r ".[$NUM].isingressgateway" ./nodejson.tmp)"
  333. if [[ $(jq -r ".[$NUM].isingressgateway" ./nodejson.tmp) == "yes" ]]; then
  334. HAS_INGRESS="yes"
  335. fi
  336. echo " is relay: $(jq -r ".[$NUM].isrelay" ./nodejson.tmp)"
  337. if [[ $(jq -r ".[$NUM].isrelay" ./nodejson.tmp) == "yes" ]]; then
  338. HAS_RELAY="yes"
  339. RELAY_ADDRS=$(jq -r ".[$NUM].relayaddrs" ./nodejson.tmp | tr -d '[]\n"[:space:]')
  340. fi
  341. echo " is failover: $(jq -r ".[$NUM].failover" ./nodejson.tmp)"
  342. if [[ $(jq -r ".[$NUM].failover" ./nodejson.tmp) == "yes" ]]; then
  343. HAS_FAILOVER="yes"
  344. fi
  345. echo " ------------"
  346. confirm
  347. echo "running command: ./nmctl keys create $NETWORK 1"
  348. KEY_JSON=$(./nmctl keys create $NETWORK 1)
  349. KEY=$(echo $KEY_JSON | jq -r .accessstring)
  350. echo "join key created: $KEY"
  351. NAME=$(jq -r ".[$NUM].name" ./nodejson.tmp)
  352. ADDRESS=$(jq -r ".[$NUM].address" ./nodejson.tmp)
  353. ADDRESS6=$(jq -r ".[$NUM].address6" ./nodejson.tmp)
  354. if [[ ! -z "$ADDRESS6" ]]; then
  355. echo "joining with command: netclient join -t $KEY --name=$NAME --address=$ADDRESS --address6=$ADDRESS6
  356. "
  357. confirm
  358. netclient join -t $KEY --name=$NAME --address=$ADDRESS --address6=$ADDRESS6
  359. else
  360. echo "joining with command: netclient join -t $KEY --name=$NAME --address=$ADDRESS"
  361. confirm
  362. netclient join -t $KEY --name=$NAME --address=$ADDRESS
  363. fi
  364. NODE_ID=$(sudo cat /etc/netclient/nodes.yml | yq -r .$NETWORK.commonnode.id)
  365. echo "join complete. New node ID: $NODE_ID"
  366. if [[ $NUM -eq 0 ]]; then
  367. HOST_ID=$(sudo cat /etc/netclient/netclient.yml | yq -r .host.id)
  368. echo "For first join, making host a default"
  369. echo "Host ID: $HOST_ID"
  370. # set as a default host
  371. # TODO - this command is not working
  372. ./nmctl host update $HOST_ID --default
  373. fi
  374. # create an egress if necessary
  375. if [[ $HAS_EGRESS == "yes" ]]; then
  376. echo "Egress is currently unimplemented. Wait for 0.18.1"
  377. fi
  378. echo "HAS INGRESS: $HAS_INGRESS"
  379. # create an ingress if necessary
  380. if [[ $HAS_INGRESS == "yes" ]]; then
  381. if [[ $HAS_FAILOVER == "yes" ]]; then
  382. echo "creating ingress and failover..."
  383. ./nmctl node create_ingress $NETWORK $NODE_ID --failover
  384. else
  385. echo "creating ingress..."
  386. ./nmctl node create_ingress $NETWORK $NODE_ID
  387. fi
  388. fi
  389. # relay
  390. if [[ $HAS_RELAY == "yes" ]]; then
  391. echo "creating relay..."
  392. ./nmctl node create_relay $NETWORK $NODE_ID $RELAY_ADDRS
  393. fi
  394. done
  395. echo "=================="
  396. else
  397. echo "no networks to join"
  398. fi
  399. }
  400. cat << "EOF"
  401. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  402. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  403. The Netmaker Upgrade Script: Upgrading to v0.18.0 so you don't have to!
  404. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  405. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  406. EOF
  407. set -e
  408. if [ $(id -u) -ne 0 ]; then
  409. echo "This script must be run as root"
  410. exit 1
  411. fi
  412. echo "...installing dependencies for script"
  413. install_dependencies
  414. echo "...confirming version is correct"
  415. check_version
  416. echo "...collecting necessary server settings"
  417. collect_server_settings
  418. echo "...setup nmctl"
  419. setup_nmctl
  420. echo "...retrieving current server node settings"
  421. collect_node_settings
  422. echo "...backing up docker compose to docker-compose.yml.backup"
  423. cp /root/docker-compose.yml /root/docker-compose.yml.backup
  424. echo "...setting docker-compose values"
  425. set_compose
  426. echo "...starting containers"
  427. start_containers
  428. wait_seconds 3
  429. echo "..testing Caddy proxy"
  430. test_caddy
  431. echo "..testing Netmaker health"
  432. # TODO, implement health check
  433. # netmaker_health_check
  434. # wait_seconds 2
  435. echo "...setting up netclient (this may take a minute, be patient)"
  436. setup_netclient
  437. wait_seconds 2
  438. echo "...join networks"
  439. join_networks
  440. echo "-----------------------------------------------------------------"
  441. echo "-----------------------------------------------------------------"
  442. echo "Netmaker setup is now complete. You are ready to begin using Netmaker."
  443. echo "Visit dashboard.$SERVER_NAME to log in"
  444. echo "-----------------------------------------------------------------"
  445. echo "-----------------------------------------------------------------"