ynh-vpnclient 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. #!/bin/bash
  2. # VPN Client app for YunoHost
  3. # Copyright (C) 2015 Julien Vaubourg <julien@vaubourg.com>
  4. # Contribute at https://github.com/labriqueinternet/vpnclient_ynh
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU Affero General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU Affero General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU Affero General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. ###################################################################################
  19. # Logging helpers #
  20. ###################################################################################
  21. LOGFILE="/var/log/ynh-vpnclient.log"
  22. touch $LOGFILE
  23. chown root:root $LOGFILE
  24. chmod 600 $LOGFILE
  25. function success()
  26. {
  27. echo "[ OK ] $1" | tee -a $LOGFILE
  28. }
  29. function info()
  30. {
  31. echo "[INFO] $1" | tee -a $LOGFILE
  32. }
  33. function warn()
  34. {
  35. echo "[WARN] $1" | tee -a $LOGFILE >&2
  36. }
  37. function error()
  38. {
  39. echo "[FAIL] $1" | tee -a $LOGFILE >&2
  40. }
  41. function critical()
  42. {
  43. echo "[CRIT] $1" | tee -a $LOGFILE >&2
  44. exit 1
  45. }
  46. ###################################################################################
  47. # Cleanup #
  48. ###################################################################################
  49. cleanup() {
  50. local last_exit_code="$?"
  51. if [[ "${action}" != "stop" && "${last_exit_code}" -ne 0 ]]; then
  52. rm -f /tmp/.ynh-vpnclient-started
  53. fi
  54. }
  55. # Cleanup before exit
  56. trap cleanup 0
  57. ###################################################################################
  58. # Time sync #
  59. ###################################################################################
  60. sync_time() {
  61. info "Now synchronizing time using ntp..."
  62. systemctl stop ntp
  63. timeout 20 ntpd -qg &> /dev/null
  64. # Some networks drop ntp port (udp 123).
  65. # Try to get the date with an http request on the internetcube web site
  66. if [ $? -ne 0 ]; then
  67. info "ntp synchronization failed, falling back to curl method"
  68. http_date=$(curl --max-time 5 -sD - yunohost.org | grep -i '^Date:' | cut -d' ' -f2-)
  69. http_date_seconds=$(date -d "${http_date}" +%s)
  70. curr_date_seconds=$(date +%s)
  71. # Set the new date if it's greater than the current date
  72. # So it does if 1970 year or if old fake-hwclock date is used
  73. if [ $http_date_seconds -ge $curr_date_seconds ]; then
  74. date -s "${http_date}"
  75. fi
  76. fi
  77. systemctl start ntp
  78. }
  79. ###################################################################################
  80. # The actual ynh vpnclient management thing #
  81. ###################################################################################
  82. check_config() {
  83. info "Checking if configuration is valid..."
  84. if [[ ! -e /etc/openvpn/keys/ca-server.crt ]]; then
  85. critical "You need a CA server (you can add it through the web admin)"
  86. fi
  87. if ! openssl x509 -in /etc/openvpn/keys/ca-server.crt -noout -checkend 0 >/dev/null; then
  88. ca_server_cert_expired_date=$(openssl x509 -in /etc/openvpn/keys/ca-server.crt -noout -enddate | cut -d '=' -f 2)
  89. critical "The CA server expired on $ca_server_cert_expired_date"
  90. fi
  91. if [[ ! -e /etc/openvpn/keys/user.crt || ! -e /etc/openvpn/keys/user.key ]]; then
  92. if [[ -s /etc/openvpn/keys/credentials ]]; then
  93. login_user=$(sed -n 1p /etc/openvpn/keys/credentials)
  94. login_passphrase=$(sed -n 2p /etc/openvpn/keys/credentials)
  95. else
  96. login_user=""
  97. login_passphrase=""
  98. fi
  99. if [[ $login_user == "" || $login_passphrase == "" ]]; then
  100. critical "You need either a client certificate, either a username, or both (you can add one through the web admin)"
  101. fi
  102. elif [[ -e /etc/openvpn/keys/user.crt ]] && ! openssl x509 -in /etc/openvpn/keys/user.crt -noout -checkend 0 >/dev/null; then
  103. user_cert_expired_date=$(openssl x509 -in /etc/openvpn/keys/user.crt -noout -enddate | cut -d '=' -f 2)
  104. critical "The client certificate expired on $user_cert_expired_date"
  105. fi
  106. }
  107. is_firewall_set() {
  108. local wired_device="$1"
  109. ip6tables -w -nvL OUTPUT | grep vpnclient_out | grep -q "${wired_device}" \
  110. && iptables -w -nvL OUTPUT | grep vpnclient_out | grep -q "${wired_device}"
  111. }
  112. set_firewall() {
  113. local wired_device=$(ip route | awk '/default via/ { print $5; }')
  114. if ! is_firewall_set "${wired_device}"; then
  115. bash /etc/yunohost/apps/vpnclient/conf/hook_post-iptable-rules
  116. fi
  117. cp /etc/yunohost/apps/vpnclient/conf/hook_post-iptable-rules /etc/yunohost/hooks.d/post_iptable_rules/90-vpnclient
  118. if is_firewall_set "${wired_device}"; then
  119. success "IPv6/IPv4 firewall set"
  120. else
  121. error "[FAIL] No IPv6/IPv4 firewall set" >&2
  122. fi
  123. }
  124. unset_firewall() {
  125. local wired_device=$(ip route | awk '/default via/ { print $5; }')
  126. rm -f /etc/yunohost/hooks.d/post_iptable_rules/90-vpnclient
  127. if is_firewall_set "${wired_device}"; then
  128. # IPv4
  129. iptables -w -D INPUT -i "${wired_device}" -j vpnclient_in
  130. iptables -w -D OUTPUT -o "${wired_device}" -j vpnclient_out
  131. iptables -w -D FORWARD -o "${wired_device}" -j vpnclient_fwd
  132. iptables -w -F vpnclient_in
  133. iptables -w -F vpnclient_out
  134. iptables -w -F vpnclient_fwd
  135. iptables -w -X vpnclient_in
  136. iptables -w -X vpnclient_out
  137. iptables -w -X vpnclient_fwd
  138. # IPv6
  139. ip6tables -w -D INPUT -i "${wired_device}" -j vpnclient_in
  140. ip6tables -w -D OUTPUT -o "${wired_device}" -j vpnclient_out
  141. ip6tables -w -D FORWARD -o "${wired_device}" -j vpnclient_fwd
  142. ip6tables -w -F vpnclient_in
  143. ip6tables -w -F vpnclient_out
  144. ip6tables -w -F vpnclient_fwd
  145. ip6tables -w -X vpnclient_in
  146. ip6tables -w -X vpnclient_out
  147. ip6tables -w -X vpnclient_fwd
  148. fi
  149. }
  150. action=${1}
  151. if [[ "$action" != restart ]]; then
  152. # Variables
  153. info "Retrieving Yunohost settings... "
  154. ynh_service_enabled=$(yunohost app setting "vpnclient" "service_enabled")
  155. success "Settings retrieved"
  156. fi
  157. ###################################################################################
  158. # Start / stop / restart / status handling #
  159. ###################################################################################
  160. case "$action" in
  161. # ########## #
  162. # Starting #
  163. # ########## #
  164. start)
  165. info "[vpnclient] Starting..."
  166. if [[ -e /tmp/.ynh-vpnclient.started ]] || systemctl -q is-active openvpn@client.service; then
  167. info "Service is already running"
  168. exit 0
  169. elif [[ "${ynh_service_enabled}" -eq 0 ]]; then
  170. warn "Service is disabled, not starting it"
  171. exit 0
  172. fi
  173. touch /tmp/.ynh-vpnclient-started
  174. sync_time
  175. check_config
  176. set_firewall
  177. info "Now actually starting OpenVPN client..."
  178. if systemctl start openvpn@client.service; then
  179. info "OpenVPN client started ... waiting for tun0 interface to show up"
  180. else
  181. tail -n 20 /var/log/openvpn-client.log | tee -a $LOGFILE
  182. critical "Failed to start OpenVPN :/"
  183. fi
  184. has_errors=true
  185. for attempt in $(seq 0 20); do
  186. sleep 1
  187. if ip link show dev tun0 &> /dev/null; then
  188. success "tun0 interface is up!"
  189. has_errors=false
  190. break
  191. fi
  192. done
  193. if $has_errors; then
  194. error "Tun0 interface did not show up ... most likely an issue happening in OpenVPN client ... below is an extract of the log that might be relevant to pinpoint the issue"
  195. tail -n 20 /var/log/openvpn-client.log | tee -a $LOGFILE
  196. systemctl stop openvpn@client.service
  197. critical "Failed to start OpenVPN client : tun0 interface did not show up"
  198. fi
  199. info "Waiting for VPN client to be ready..."
  200. if ! timeout 180 tail -n 0 -f /var/log/openvpn-client.log | grep -q "Initialization Sequence Completed"; then
  201. error "The VPN client didn't complete initiliasation"
  202. tail -n 20 /var/log/openvpn-client.log | tee -a $LOGFILE
  203. systemctl stop openvpn@client.service
  204. critical "Failed to start OpenVPN client"
  205. fi
  206. info "Validating that VPN is up and the server is connected to internet..."
  207. ipv4=$(timeout 5 ping -w3 -c1 ip.yunohost.org >/dev/null 2>&1 && curl --max-time 5 https://ip.yunohost.org --silent)
  208. ipv6=$(timeout 5 ping -w3 -c1 ip6.yunohost.org >/dev/null 2>&1 && curl --max-time 5 https://ip6.yunohost.org --silent)
  209. if ip route get 1.2.3.4 | grep -q tun0; then
  210. if timeout 5 ping -c1 -w3 debian.org >/dev/null; then
  211. success "YunoHost VPN client started!"
  212. info "IPv4 address is $ipv4"
  213. info "IPv6 address is $ipv6"
  214. else
  215. critical "The VPN is up but debian.org cannot be reached, indicating that something is probably misconfigured/blocked."
  216. fi
  217. else
  218. critical "IPv4 routes are misconfigured !?"
  219. fi
  220. ;;
  221. # ########## #
  222. # Stopping #
  223. # ########## #
  224. stop)
  225. info "[vpnclient] Stopping..."
  226. rm -f /tmp/.ynh-vpnclient-started
  227. if systemctl is-active -q openvpn@client.service; then
  228. info "Stopping OpenVPN service"
  229. systemctl stop openvpn@client.service
  230. for attempt in $(seq 0 20); do
  231. if ip link show dev tun0 &> /dev/null; then
  232. info "(Waiting for tun0 to disappear if it was up)"
  233. sleep 1
  234. fi
  235. done
  236. fi
  237. unset_firewall
  238. ;;
  239. # ########## #
  240. # Restart #
  241. # ########## #
  242. restart)
  243. $0 stop
  244. $0 start
  245. ;;
  246. # ########## #
  247. # Halp #
  248. # ########## #
  249. *)
  250. echo "Usage: $0 {start|stop|restart}"
  251. ;;
  252. esac
  253. exit 0