nm-upgrade.sh 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. #!/bin/bash
  2. # check_version - 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_seconds - 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 - 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_dependencies - 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. # collect_server_settings - 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. # collect_node_settings - 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 - 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. yq ".services.netmaker.ports += \"3478:3478/udp\"" -i /root/docker-compose.yml
  234. }
  235. # start_containers - run docker-compose up -d
  236. start_containers() {
  237. docker-compose -f /root/docker-compose.yml up -d
  238. }
  239. # test_caddy - make sure caddy is working
  240. test_caddy() {
  241. echo "Testing Caddy setup (please be patient, this may take 1-2 minutes)"
  242. for i in 1 2 3 4 5 6 7 8
  243. do
  244. curlresponse=$(curl -vIs https://${SERVER_HTTP_HOST} 2>&1)
  245. if [[ "$i" == 8 ]]; then
  246. echo " Caddy is having an issue setting up certificates, please investigate (docker logs caddy)"
  247. echo " Exiting..."
  248. exit 1
  249. elif [[ "$curlresponse" == *"failed to verify the legitimacy of the server"* ]]; then
  250. echo " Certificates not yet configured, retrying..."
  251. elif [[ "$curlresponse" == *"left intact"* ]]; then
  252. echo " Certificates ok"
  253. break
  254. else
  255. secs=$(($i*5+10))
  256. echo " Issue establishing connection...retrying in $secs seconds..."
  257. fi
  258. sleep $secs
  259. done
  260. }
  261. # setup_netclient - installs netclient locally
  262. setup_netclient() {
  263. # DEV_TEMP - Temporary instructions for testing
  264. wget https://fileserver.netmaker.org/testing/netclient
  265. chmod +x netclient
  266. ./netclient install
  267. # RELEASE_REPLACE - Use this once release is ready
  268. # if [ -f /etc/debian_version ]; then
  269. # curl -sL 'https://apt.netmaker.org/gpg.key' | sudo tee /etc/apt/trusted.gpg.d/netclient.asc
  270. # curl -sL 'https://apt.netmaker.org/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/netclient.list
  271. # sudo apt update
  272. # sudo apt install netclient
  273. # elif [ -f /etc/centos-release ]; then
  274. # curl -sL 'https://rpm.netmaker.org/gpg.key' | sudo tee /tmp/gpg.key
  275. # curl -sL 'https://rpm.netmaker.org/netclient-repo' | sudo tee /etc/yum.repos.d/netclient.repo
  276. # sudo rpm --import /tmp/gpg.key
  277. # sudo dnf check-update
  278. # sudo dnf install netclient
  279. # elif [ -f /etc/fedora-release ]; then
  280. # curl -sL 'https://rpm.netmaker.org/gpg.key' | sudo tee /tmp/gpg.key
  281. # curl -sL 'https://rpm.netmaker.org/netclient-repo' | sudo tee /etc/yum.repos.d/netclient.repo
  282. # sudo rpm --import /tmp/gpg.key
  283. # sudo dnf check-update
  284. # sudo dnf install netclient
  285. # elif [ -f /etc/redhat-release ]; then
  286. # curl -sL 'https://rpm.netmaker.org/gpg.key' | sudo tee /tmp/gpg.key
  287. # curl -sL 'https://rpm.netmaker.org/netclient-repo' | sudo tee /etc/yum.repos.d/netclient.repo
  288. # sudo rpm --import /tmp/gpg.key
  289. # sudo dnf check-update(
  290. # sudo dnf install netclient
  291. # elif [ -f /etc/arch-release ]; then
  292. # yay -S netclient
  293. # else
  294. # echo "OS not supported for automatic install"
  295. # exit 1
  296. # fi
  297. # if [ -z "${install_cmd}" ]; then
  298. # echo "OS unsupported for automatic dependency install"
  299. # exit 1
  300. # fi
  301. }
  302. # setup_nmctl - pulls nmctl and makes it executable
  303. setup_nmctl() {
  304. # DEV_TEMP - Temporary instructions for testing
  305. wget https://fileserver.netmaker.org/testing/nmctl
  306. # RELEASE_REPLACE - Use this once release is ready
  307. # wget https://github.com/gravitl/netmaker/releases/download/v0.17.1/nmctl
  308. chmod +x nmctl
  309. echo "using server $SERVER_HTTP_HOST"
  310. echo "using master key $MASTER_KEY"
  311. ./nmctl context set default --endpoint="https://$SERVER_HTTP_HOST" --master_key="$MASTER_KEY"
  312. ./nmctl context use default
  313. RESP=$(./nmctl network list)
  314. if [[ $RESP == *"unauthorized"* ]]; then
  315. echo "Unable to properly configure NMCTL, exiting..."
  316. exit 1
  317. fi
  318. }
  319. # join_networks - joins netclient into the networks using old settings
  320. join_networks() {
  321. NODE_LEN=$(jq length nodejson.tmp)
  322. HAS_INGRESS="no"
  323. if [ "$NODE_LEN" -gt 0 ]; then
  324. for i in $(seq 1 $NODE_LEN); do
  325. NUM=$(($i-1))
  326. NETWORK=$(jq -r ".[$NUM].network" ./nodejson.tmp)
  327. echo " joining network $NETWORK with following settings. Please confirm:"
  328. echo " network: $(jq -r ".[$NUM].network" ./nodejson.tmp)"
  329. echo " name: $(jq -r ".[$NUM].name" ./nodejson.tmp)"
  330. echo " private ipv4: $(jq -r ".[$NUM].address" ./nodejson.tmp)"
  331. echo " private ipv6: $(jq -r ".[$NUM].address6" ./nodejson.tmp)"
  332. echo " is egress: $(jq -r ".[$NUM].isegressgateway" ./nodejson.tmp)"
  333. if [[ $(jq -r ".[$NUM].isegressgateway" ./nodejson.tmp) == "yes" ]]; then
  334. HAS_EGRESS="yes"
  335. echo " egress range: $(jq -r ".[$NUM].egressgatewayranges" ./nodejson.tmp)"
  336. fi
  337. echo " is ingress: $(jq -r ".[$NUM].isingressgateway" ./nodejson.tmp)"
  338. if [[ $(jq -r ".[$NUM].isingressgateway" ./nodejson.tmp) == "yes" ]]; then
  339. HAS_INGRESS="yes"
  340. fi
  341. echo " is relay: $(jq -r ".[$NUM].isrelay" ./nodejson.tmp)"
  342. if [[ $(jq -r ".[$NUM].isrelay" ./nodejson.tmp) == "yes" ]]; then
  343. HAS_RELAY="yes"
  344. RELAY_ADDRS=$(jq -r ".[$NUM].relayaddrs" ./nodejson.tmp | tr -d '[]\n"[:space:]')
  345. fi
  346. echo " is failover: $(jq -r ".[$NUM].failover" ./nodejson.tmp)"
  347. if [[ $(jq -r ".[$NUM].failover" ./nodejson.tmp) == "yes" ]]; then
  348. HAS_FAILOVER="yes"
  349. fi
  350. echo " ------------"
  351. confirm
  352. echo "running command: ./nmctl keys create $NETWORK 1"
  353. KEY_JSON=$(./nmctl keys create $NETWORK 1)
  354. KEY=$(echo $KEY_JSON | jq -r .accessstring)
  355. echo "join key created: $KEY"
  356. NAME=$(jq -r ".[$NUM].name" ./nodejson.tmp)
  357. ADDRESS=$(jq -r ".[$NUM].address" ./nodejson.tmp)
  358. ADDRESS6=$(jq -r ".[$NUM].address6" ./nodejson.tmp)
  359. if [[ ! -z "$ADDRESS6" ]]; then
  360. echo "joining with command: netclient join -t $KEY --name=$NAME --address=$ADDRESS --address6=$ADDRESS6
  361. "
  362. confirm
  363. netclient join -t $KEY --name=$NAME --address=$ADDRESS --address6=$ADDRESS6
  364. else
  365. echo "joining with command: netclient join -t $KEY --name=$NAME --address=$ADDRESS"
  366. confirm
  367. netclient join -t $KEY --name=$NAME --address=$ADDRESS
  368. fi
  369. NODE_ID=$(sudo cat /etc/netclient/nodes.yml | yq -r .$NETWORK.commonnode.id)
  370. echo "join complete. New node ID: $NODE_ID"
  371. if [[ $NUM -eq 0 ]]; then
  372. HOST_ID=$(sudo cat /etc/netclient/netclient.yml | yq -r .host.id)
  373. echo "For first join, making host a default"
  374. echo "Host ID: $HOST_ID"
  375. # set as a default host
  376. # TODO - this command is not working
  377. ./nmctl host update $HOST_ID --default
  378. fi
  379. # create an egress if necessary
  380. if [[ $HAS_EGRESS == "yes" ]]; then
  381. echo "Egress is currently unimplemented. Wait for 0.18.1"
  382. fi
  383. echo "HAS INGRESS: $HAS_INGRESS"
  384. # create an ingress if necessary
  385. if [[ $HAS_INGRESS == "yes" ]]; then
  386. if [[ $HAS_FAILOVER == "yes" ]]; then
  387. echo "creating ingress and failover..."
  388. ./nmctl node create_ingress $NETWORK $NODE_ID --failover
  389. else
  390. echo "creating ingress..."
  391. ./nmctl node create_ingress $NETWORK $NODE_ID
  392. fi
  393. fi
  394. # relay
  395. if [[ $HAS_RELAY == "yes" ]]; then
  396. echo "creating relay..."
  397. ./nmctl node create_relay $NETWORK $NODE_ID $RELAY_ADDRS
  398. fi
  399. done
  400. echo "=================="
  401. else
  402. echo "no networks to join"
  403. fi
  404. }
  405. cat << "EOF"
  406. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  407. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  408. The Netmaker Upgrade Script: Upgrading to v0.18.0 so you don't have to!
  409. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  410. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  411. EOF
  412. set -e
  413. if [ $(id -u) -ne 0 ]; then
  414. echo "This script must be run as root"
  415. exit 1
  416. fi
  417. echo "...installing dependencies for script"
  418. install_dependencies
  419. echo "...confirming version is correct"
  420. check_version
  421. echo "...collecting necessary server settings"
  422. collect_server_settings
  423. echo "...setup nmctl"
  424. setup_nmctl
  425. echo "...retrieving current server node settings"
  426. collect_node_settings
  427. echo "...backing up docker compose to docker-compose.yml.backup"
  428. cp /root/docker-compose.yml /root/docker-compose.yml.backup
  429. echo "...setting docker-compose values"
  430. set_compose
  431. echo "...starting containers"
  432. start_containers
  433. wait_seconds 3
  434. echo "..testing Caddy proxy"
  435. test_caddy
  436. echo "..testing Netmaker health"
  437. # TODO, implement health check
  438. # netmaker_health_check
  439. # wait_seconds 2
  440. echo "...setting up netclient (this may take a minute, be patient)"
  441. setup_netclient
  442. wait_seconds 2
  443. echo "...join networks"
  444. join_networks
  445. echo "-----------------------------------------------------------------"
  446. echo "-----------------------------------------------------------------"
  447. echo "Netmaker setup is now complete. You are ready to begin using Netmaker."
  448. echo "Visit dashboard.$SERVER_NAME to log in"
  449. echo "-----------------------------------------------------------------"
  450. echo "-----------------------------------------------------------------"