diff --git a/conf/scripts/route-down.d/10-vpnclient-unset-firewall b/conf/scripts/route-down.d/10-vpnclient-unset-firewall new file mode 100644 index 0000000..7fd7b80 --- /dev/null +++ b/conf/scripts/route-down.d/10-vpnclient-unset-firewall @@ -0,0 +1,40 @@ +#!/bin/bash + +is_firewall_set() { + ip6tables -w -nvL OUTPUT | grep vpnclient_out | grep -q "${wired_device}" \ + && iptables -w -nvL OUTPUT | grep vpnclient_out | grep -q "${wired_device}" +} + +wired_device=$(ip route | awk '/default via/ { print $5; }') + +if is_firewall_set; then + rm -f /etc/yunohost/hooks.d/post_iptable_rules/90-vpnclient + + # IPv4 + + iptables -w -D INPUT -i "${wired_device}" -j vpnclient_in + iptables -w -D OUTPUT -o "${wired_device}" -j vpnclient_out + iptables -w -D FORWARD -o "${wired_device}" -j vpnclient_fwd + + iptables -w -F vpnclient_in + iptables -w -F vpnclient_out + iptables -w -F vpnclient_fwd + + iptables -w -X vpnclient_in + iptables -w -X vpnclient_out + iptables -w -X vpnclient_fwd + + # IPv6 + + ip6tables -w -D INPUT -i "${wired_device}" -j vpnclient_in + ip6tables -w -D OUTPUT -o "${wired_device}" -j vpnclient_out + ip6tables -w -D FORWARD -o "${wired_device}" -j vpnclient_fwd + + ip6tables -w -F vpnclient_in + ip6tables -w -F vpnclient_out + ip6tables -w -F vpnclient_fwd + + ip6tables -w -X vpnclient_in + ip6tables -w -X vpnclient_out + ip6tables -w -X vpnclient_fwd +fi diff --git a/conf/scripts/route-down.d/20-vpnclient-unset-dns b/conf/scripts/route-down.d/20-vpnclient-unset-dns new file mode 100644 index 0000000..7325b03 --- /dev/null +++ b/conf/scripts/route-down.d/20-vpnclient-unset-dns @@ -0,0 +1,28 @@ +#!/bin/bash + +is_dns_set() { + if [[ "$ynh_dns_method" != "custom" ]]; then + return 0 + fi + + current_dns=$(grep -o -P '^\s*nameserver\s+\K[abcdefABCDEF\d.:]+$' /etc/resolv.dnsmasq.conf | sort | uniq) + wanted_dns=$(echo "${ynh_dns}" | sed 's/,/\n/g' | sort | uniq) + [[ -e /etc/dhcp/dhclient-exit-hooks.d/ynh-vpnclient ]] \ + && [[ "$current_dns" == "$wanted_dns" ]] +} + +if is_dns_set; then + resolvconf=/etc/resolv.dnsmasq.conf + + rm -f /etc/dhcp/dhclient-exit-hooks.d/ynh-vpnclient + if [[ -e "${resolvconf}.ynh" ]]; then + mv "${resolvconf}.ynh" "${resolvconf}" + fi + + # FIXME : this situation happened to a user ... + # We could try to force regen the dns conf + # (though for now it's tightly coupled to dnsmasq) + if ! grep -q "^nameserver\s" "${resolvconf}"; then + echo "${resolvconf} does not have any nameserver line !?" >&2 + fi +fi diff --git a/conf/scripts/route-down.d/30-vpnclient-unset-server-ipv6-route b/conf/scripts/route-down.d/30-vpnclient-unset-server-ipv6-route new file mode 100644 index 0000000..d5082b6 --- /dev/null +++ b/conf/scripts/route-down.d/30-vpnclient-unset-server-ipv6-route @@ -0,0 +1,36 @@ +#!/bin/bash + +is_serverip6route_set() { + local server_ip6s=${1} + + if [[ -z "${server_ip6s}" ]]; then + return 0 + fi + + for server_ip6 in ${server_ip6s}; do + if ! ip -6 route | grep -q "^${server_ip6}"; then + return 1 + fi + done +} + +unset_serverip6route() { + local server_ip6s=${1} + local ip6_gw=${2} + local wired_device=${3} + + for server_ip6 in ${server_ip6s}; do + ip route delete "${server_ip6}/128" via "${ip6_gw}" dev "${wired_device}" + done +} + +old_ip6_gw=$(yunohost app setting vpnclient ip6_gw) +old_wired_device=$(yunohost app setting vpnclient wired_device) +old_server_ip6=$(yunohost app setting vpnclient server_ip6) + +# Check old state of the server ipv6 route +if [[ -n "${old_server_ip6}" && -n "${old_ip6_gw}" && -n "${old_wired_device}" ]]; then + if is_serverip6route_set "${old_server_ip6}"; then + unset_serverip6route "${old_server_ip6}" "${old_ip6_gw}" "${old_wired_device}" + fi +fi diff --git a/conf/scripts/route-down.d/40-unset-ipv6 b/conf/scripts/route-down.d/40-vpnclient-unset-ipv6 similarity index 100% rename from conf/scripts/route-down.d/40-unset-ipv6 rename to conf/scripts/route-down.d/40-vpnclient-unset-ipv6 diff --git a/conf/scripts/route-up.d/10-vpnclient-set-firewall b/conf/scripts/route-up.d/10-vpnclient-set-firewall new file mode 100644 index 0000000..655a409 --- /dev/null +++ b/conf/scripts/route-up.d/10-vpnclient-set-firewall @@ -0,0 +1,20 @@ +#!/bin/bash + +is_firewall_set() { + local wired_device=$(ip route | awk '/default via/ { print $5; }') + + ip6tables -w -nvL OUTPUT | grep vpnclient_out | grep -q "${wired_device}" \ + && iptables -w -nvL OUTPUT | grep vpnclient_out | grep -q "${wired_device}" +} + +if ! is_firewall_set; then + bash /etc/yunohost/apps/vpnclient/conf/hook_post-iptable-rules + cp /etc/yunohost/apps/vpnclient/conf/hook_post-iptable-rules /etc/yunohost/hooks.d/post_iptable_rules/90-vpnclient +fi + +if is_firewall_set; then + echo "[ OK ] IPv6/IPv4 firewall set" +else + echo "[FAIL] No IPv6/IPv4 firewall set" >&2 + exit 1 +fi diff --git a/conf/scripts/route-up.d/20-vpnclient-set-dns b/conf/scripts/route-up.d/20-vpnclient-set-dns new file mode 100644 index 0000000..38d877f --- /dev/null +++ b/conf/scripts/route-up.d/20-vpnclient-set-dns @@ -0,0 +1,35 @@ +#!/bin/bash + +is_dns_set() { + if [[ "$ynh_dns_method" != "custom" ]]; then + return 0 + fi + + current_dns=$(grep -o -P '^\s*nameserver\s+\K[a-fA-F\d.:]+$' /etc/resolv.dnsmasq.conf | sort | uniq) + wanted_dns=$(echo "${ynh_dns}" | sed 's/,/\n/g' | sort | uniq) + [[ -e /etc/dhcp/dhclient-exit-hooks.d/ynh-vpnclient ]] \ + && [[ "$current_dns" == "$wanted_dns" ]] +} + +ynh_dns_method=$(yunohost app setting vpnclient dns_method) +ynh_dns=$(yunohost app setting vpnclient nameservers) + +# Set host DNS resolvers +if ! is_dns_set; then + resolvconf=/etc/resolv.dnsmasq.conf + + cp -fa "${resolvconf}" "${resolvconf}.ynh" + if [[ "$ynh_dns_method" == "custom" ]]; then + cat << EOF > /etc/dhcp/dhclient-exit-hooks.d/ynh-vpnclient +echo "${ynh_dns}" | sed 's/,/\n/g' | sort | uniq | sed 's/^/nameserver /g' > ${resolvconf} +EOF + bash /etc/dhcp/dhclient-exit-hooks.d/ynh-vpnclient + fi +fi + +if is_dns_set; then + echo "[ OK ] Host DNS correctly set" +else + echo "[FAIL] No host DNS set" >&2 + exit 1 +fi diff --git a/conf/scripts/route-up.d/30-vpnclient-set-server-ipv6-route b/conf/scripts/route-up.d/30-vpnclient-set-server-ipv6-route new file mode 100644 index 0000000..d131857 --- /dev/null +++ b/conf/scripts/route-up.d/30-vpnclient-set-server-ipv6-route @@ -0,0 +1,90 @@ +#!/bin/bash + +has_nativeip6() { + ip -6 route | grep -q "default via" +} + +is_serverip6route_set() { + local server_ip6s=${1} + + if [[ -z "${server_ip6s}" ]]; then + return 0 + fi + + for server_ip6 in ${server_ip6s}; do + if ! ip -6 route | grep -q "^${server_ip6}"; then + return 1 + fi + done +} + +set_serverip6route() { + local server_ip6s=${1} + local ip6_gw=${2} + local wired_device=${3} + + for server_ip6 in ${server_ip6s}; do + ip route add "${server_ip6}/128" via "${ip6_gw}" dev "${wired_device}" + done +} + +unset_serverip6route() { + local server_ip6s=${1} + local ip6_gw=${2} + local wired_device=${3} + + for server_ip6 in ${server_ip6s}; do + ip route delete "${server_ip6}/128" via "${ip6_gw}" dev "${wired_device}" + done +} + +old_ip6_gw=$(yunohost app setting vpnclient ip6_gw) +old_wired_device=$(yunohost app setting vpnclient wired_device) +old_server_ip6=$(yunohost app setting vpnclient server_ip6) + +new_ip6_gw=$(ip -6 route | awk '/default via/ { print $3 }') +new_wired_device=$(ip route | awk '/default via/ { print $5; }') +ynh_server_names=$(grep -o -P '^\s*remote\s+\K([^\s]+)' /etc/openvpn/client.conf | sort | uniq) +new_server_ip6=$(dig AAAA +short $ynh_server_names @127.0.0.1 | grep -v '\.$' | grep -v "timed out" | sort | uniq) + +for i in $ynh_server_names; do + if [[ "${i}" =~ : ]] && [[ ! "$new_server_ip6" == *"${i}"* ]] ; then + new_server_ip6+=" ${i}" + fi +done + +echo "[INFO] Autodetected internet interface: ${new_wired_device} (last start: ${old_wired_device})" +echo "[INFO] Autodetected IPv6 address for the VPN server: ${new_server_ip6} (last start: ${old_server_ip6})" + +# Check old state of the server ipv6 route +if [[ -n "${old_server_ip6}" && -n "${old_ip6_gw}" && -n "${old_wired_device}" ]]; then + if [[ "${new_server_ip6}" != "${old_server_ip6}" || "${new_ip6_gw}" != "${old_ip6_gw}" || "${new_wired_device}" != "${old_wired_device}" ]]; then + if is_serverip6route_set "${old_server_ip6}"; then + unset_serverip6route "${old_server_ip6}" "${old_ip6_gw}" "${old_wired_device}" + fi + fi +fi + +# Set the new server ipv6 route +if has_nativeip6; then + if ! is_serverip6route_set "${new_server_ip6}"; then + set_serverip6route "${new_server_ip6}" "${new_ip6_gw}" "${new_wired_device}" + fi + + echo "[INFO] Native IPv6 detected" + echo "[INFO] Autodetected native IPv6 gateway: ${new_ip6_gw} (last start: ${old_ip6_gw})" + + if is_serverip6route_set "${new_server_ip6}"; then + echo "[ OK ] IPv6 server route correctly set" + else + echo "[FAIL] No IPv6 server route set" >&2 + exit 1 + fi +else + echo "[INFO] No native IPv6 detected" + echo "[INFO] No IPv6 server route to set" +fi + +yunohost app setting vpnclient server_ip6 --value "${new_server_ip6}" +yunohost app setting vpnclient ip6_gw --value "${new_ip6_gw}" +yunohost app setting vpnclient wired_device --value "${new_wired_device}" diff --git a/conf/scripts/route-up.d/40-set-ipv6 b/conf/scripts/route-up.d/40-set-ipv6 deleted file mode 100644 index 50ebfb8..0000000 --- a/conf/scripts/route-up.d/40-set-ipv6 +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -is_ip6addr_set() { - local ip6_addr=${1} - ip address show dev tun0 2> /dev/null | grep -q "${ip6_addr}/" -} - -ip6_addr=$(yunohost app setting "vpnclient" "ip6_addr") -if [[ -n "${ip6_addr}" ]] && [[ "${ip6_addr}" != none ]] && ! is_ip6addr_set "${ip6_addr}"; then - ip address add "${ip6_addr}/64" dev tun0 -fi diff --git a/conf/scripts/route-up.d/40-vpnclient-set-ipv6 b/conf/scripts/route-up.d/40-vpnclient-set-ipv6 new file mode 100644 index 0000000..bc7dfc1 --- /dev/null +++ b/conf/scripts/route-up.d/40-vpnclient-set-ipv6 @@ -0,0 +1,25 @@ +#!/bin/bash + +is_ip6addr_set() { + local ip6_addr=${1} + ip address show dev tun0 2> /dev/null | grep -q "${ip6_addr}/" +} + +ip6_addr=$(yunohost app setting "vpnclient" "ip6_addr") +if [[ -n "${ip6_addr}" ]] && [[ "${ip6_addr}" != none ]]; then + if ! is_ip6addr_set "${ip6_addr}"; then + ip address add "${ip6_addr}/64" dev tun0 + fi + + echo "[INFO] IPv6 delegated prefix found" + echo "[INFO] IPv6 address computed from the delegated prefix: ${ip6_addr}" + + if is_ip6addr_set "${ip6_addr}"; then + echo "[ OK ] IPv6 address correctly set" + else + echo "[FAIL] No IPv6 address set" >&2 + exit 1 + fi +else + echo "[INFO] No IPv6 delegated prefix found" +fi diff --git a/conf/ynh-vpnclient b/conf/ynh-vpnclient index 522830b..a15a051 100644 --- a/conf/ynh-vpnclient +++ b/conf/ynh-vpnclient @@ -53,136 +53,18 @@ function critical() } ################################################################################### -# IPv6 and route config stuff # +# Cleanup # ################################################################################### -has_nativeip6() { - ip -6 route | grep -q "default via" -} - -has_ip6delegatedprefix() { - [[ -n "${ynh_ip6_addr}" ]] && [[ "${ynh_ip6_addr}" != none ]] -} - -is_ip6addr_set() { - ip address show dev tun0 2> /dev/null | grep -q "${ynh_ip6_addr}/" -} - -# -# Server IPv6 route -# - -is_serverip6route_set() { - server_ip6s=${1} - - if [[ -z "${server_ip6s}" ]]; then - return 0 - fi - - for server_ip6 in ${server_ip6s}; do - if ! ip -6 route | grep -q "^${server_ip6}"; then - return 1 - fi - done -} - -set_serverip6route() { - server_ip6s=${1} - ip6_gw=${2} - wired_device=${3} - - info "Adding IPv6 server route" - for server_ip6 in ${server_ip6s}; - do - ip route add "${server_ip6}/128" via "${ip6_gw}" dev "${wired_device}" - done -} - - -unset_serverip6route() { - server_ip6s=${1} - ip6_gw=${2} - wired_device=${3} - - info "Removing IPv6 server route" - for server_ip6 in ${server_ip6s}; - do - ip route delete "${server_ip6}/128" via "${ip6_gw}" dev "${wired_device}" - done -} - -################################################################################### -# DNS rules # -################################################################################### - -is_dns_set() { - if [[ "$ynh_dns_method" == "custom" ]] - then - - current_dns=$(grep -o -P '\s*nameserver\s+\K[abcdefabcdef\d.:]+' /etc/resolv.dnsmasq.conf | sort | uniq) - wanted_dns=$(echo "${ynh_dns}" | sed 's/,/\n/g' | sort | uniq) - [ -e /etc/dhcp/dhclient-exit-hooks.d/ynh-vpnclient ]\ - && [[ "$current_dns" == "$wanted_dns" ]] - else - true - fi -} - -set_dns() { - info "Enforcing custom DNS resolvers from vpnclient" - - resolvconf=/etc/resolv.dnsmasq.conf - - cp -fa "${resolvconf}" "${resolvconf}.ynh" - if [[ "$ynh_dns_method" == "custom" ]] - then - cat << EOF > /etc/dhcp/dhclient-exit-hooks.d/ynh-vpnclient -echo "${ynh_dns}" | sed 's/,/\n/g' | sort | uniq | sed 's/^/nameserver /g' > ${resolvconf} -EOF - bash /etc/dhcp/dhclient-exit-hooks.d/ynh-vpnclient +cleanup() { + local last_exit_code="$?" + if [[ "${action}" != "stop" && "${last_exit_code}" -ne 0 ]]; then + rm -f /tmp/.ynh-vpnclient-started fi - } -unset_dns() { - resolvconf=/etc/resolv.dnsmasq.conf - - info "Removing custom DNS resolvers from vpnclient" - rm -f /etc/dhcp/dhclient-exit-hooks.d/ynh-vpnclient - [ -e "${resolvconf}.ynh" ] && mv "${resolvconf}.ynh" "${resolvconf}" - - # FIXME : this situation happened to a user ... - # We could try to force regen the dns conf - # (though for now it's tightly coupled to dnsmasq) - grep -q "^nameserver\s" "${resolvconf}" || error "${resolvconf} does not have any nameserver line !?" -} - -################################################################################### -# Firewall rules management # -################################################################################### - -is_firewall_set() { - wired_device=$(ip route | awk '/default via/ { print $5; }') - - ip6tables -w -nvL OUTPUT | grep vpnclient_out | grep -q "${wired_device}"\ - && iptables -w -nvL OUTPUT | grep vpnclient_out | grep -q "${wired_device}" -} - -set_firewall() { - info "Adding vpnclient custom rules to the firewall" - - cp /etc/yunohost/hooks.d/{90-vpnclient.tpl,post_iptable_rules/90-vpnclient} - - info "Restarting yunohost firewall..." - yunohost firewall reload >/dev/null && success "Firewall restarted!" -} - -unset_firewall() { - info "Cleaning vpnclient custom rules from the firewall" - rm -f /etc/yunohost/hooks.d/post_iptable_rules/90-vpnclient - info "Restarting yunohost firewall..." - yunohost firewall reload >/dev/null && success "Firewall restarted!" -} +# Cleanup before exit +trap cleanup 0 ################################################################################### # Time sync # @@ -197,7 +79,7 @@ sync_time() { # Try to get the date with an http request on the internetcube web site if [ $? -ne 0 ]; then info "ntp synchronization failed, falling back to curl method" - http_date=$(curl --max-time 5 -sD - labriqueinter.net | grep '^Date:' | cut -d' ' -f3-6) + http_date=$(curl --max-time 5 -sD - yunohost.org | grep -i '^Date:' | cut -d' ' -f2-) http_date_seconds=$(date -d "${http_date}" +%s) curr_date_seconds=$(date +%s) @@ -210,93 +92,13 @@ sync_time() { systemctl start ntp } -################################################################################### -# OpenVPN client start/stop procedures # -################################################################################### - -is_openvpn_running() { - systemctl is-active openvpn@client.service &> /dev/null -} - -start_openvpn() { - - # Unset firewall to let DNS and NTP resolution works - # Firewall is reset after vpn is mounted (more details on #1016) - unset_firewall - - sync_time - - info "Now actually starting OpenVPN client..." - - if systemctl start openvpn@client.service - then - info "OpenVPN client started ... waiting for tun0 interface to show up" - else - tail -n 20 /var/log/openvpn-client.log | tee -a $LOGFILE - critical "Failed to start OpenVPN :/" - fi - - for attempt in $(seq 0 20) - do - sleep 1 - if ip link show dev tun0 &> /dev/null - then - success "tun0 interface is up!" - return 0 - fi - done - - 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" - tail -n 20 /var/log/openvpn-client.log | tee -a $LOGFILE - stop_openvpn - critical "Failed to start OpenVPN client : tun0 interface did not show up" -} - -stop_openvpn() { - info "Stopping OpenVPN service" - systemctl stop openvpn@client.service - - for attempt in $(seq 0 20) - do - if ip link show dev tun0 &> /dev/null - then - info "(Waiting for tun0 to disappear if it was up)" - sleep 1 - fi - done -} - -################################################################################### -# Yunohost settings interface # -################################################################################### - -ynh_setting_get() { - app=${1} - setting=${2} - - grep "^${setting}:" "/etc/yunohost/apps/${app}/settings.yml" | sed s/^[^:]\\+:\\s*[\"\']\\?// | sed s/\\s*[\"\']\$// - # '" -} - -ynh_setting_set() { - app=${1} - setting=${2} - value=${3} - - yunohost app setting "${app}" "${setting}" -v "${value}" -} - ################################################################################### # The actual ynh vpnclient management thing # ################################################################################### -is_running() { - ((has_nativeip6 && is_serverip6route_set "${new_server_ip6}") || ! has_nativeip6)\ - && ((has_ip6delegatedprefix && is_ip6addr_set) || ! has_ip6delegatedprefix)\ - && is_dns_set && is_firewall_set && is_openvpn_running -} - check_config() { + info "Checking if configuration is valid..." + if [[ ! -e /etc/openvpn/keys/ca-server.crt ]]; then critical "You need a CA server (you can add it through the web admin)" fi @@ -324,113 +126,92 @@ check_config() { fi } -if [ "$1" != restart ]; then - +action=${1} +if [[ "$action" != restart ]]; then # Variables info "Retrieving Yunohost settings... " - ynh_service_enabled=$(ynh_setting_get vpnclient service_enabled) - ynh_ip6_addr=$(ynh_setting_get vpnclient ip6_addr) - ynh_dns_method=$(ynh_setting_get vpnclient dns_method) - ynh_dns=$(ynh_setting_get vpnclient nameservers) - - old_ip6_gw=$(ynh_setting_get vpnclient ip6_gw) - old_wired_device=$(ynh_setting_get vpnclient wired_device) - old_server_ip6=$(ynh_setting_get vpnclient server_ip6) - - new_ip6_gw=$(ip -6 route | awk '/default via/ { print $3 }') - new_wired_device=$(ip route | awk '/default via/ { print $5; }') - ynh_server_names=$(grep -o -P '^\s*remote\s+\K([^\s]+)' /etc/openvpn/client.conf | sort | uniq) - new_server_ip6=$(dig AAAA +short $ynh_server_names @127.0.0.1 | grep -v '\.$' | grep -v "timed out" | sort | uniq) - - for i in $ynh_server_names; do - if [[ "${i}" =~ : ]] && [[ ! "$new_server_ip6" == *"${i}"* ]] ; then - new_server_ip6+=" ${i}" - fi - done + ynh_service_enabled=$(yunohost app setting "vpnclient" "service_enabled") success "Settings retrieved" - fi ################################################################################### # Start / stop / restart / status handling # ################################################################################### -case "${1}" in +case "$action" in # ########## # # Starting # # ########## # start) - - if is_running; then + info "[vpnclient] Starting..." + + if [[ -e /tmp/.ynh-vpnclient.started ]] || systemctl -q is-active openvpn@client.service; then info "Service is already running" exit 0 - elif [ "${ynh_service_enabled}" -eq 0 ]; then + elif [[ "${ynh_service_enabled}" -eq 0 ]]; then warn "Service is disabled, not starting it" exit 0 fi - check_config - - info "[vpnclient] Starting..." touch /tmp/.ynh-vpnclient-started - # Run openvpn - if is_openvpn_running; then - info "(openvpn is already running)" - else - start_openvpn - fi + sync_time + check_config - # Check old state of the server ipv6 route - if [ ! -z "${old_server_ip6}" -a ! -z "${old_ip6_gw}" -a ! -z "${old_wired_device}"\ - -a \( "${new_server_ip6}" != "${old_server_ip6}" -o "${new_ip6_gw}" != "${old_ip6_gw}"\ - -o "${new_wired_device}" != "${old_wired_device}" \) ]\ - && is_serverip6route_set "${old_server_ip6}" - then - unset_serverip6route "${old_server_ip6}" "${old_ip6_gw}" "${old_wired_device}" - fi + info "Now actually starting OpenVPN client..." - # Set the new server ipv6 route - if has_nativeip6 && ! is_serverip6route_set "${new_server_ip6}" - then - set_serverip6route "${new_server_ip6}" "${new_ip6_gw}" "${new_wired_device}" + if systemctl start openvpn@client.service; then + info "OpenVPN client started ... waiting for tun0 interface to show up" + else + tail -n 20 /var/log/openvpn-client.log | tee -a $LOGFILE + critical "Failed to start OpenVPN :/" fi - # Set host DNS resolvers - if ! is_dns_set; then - set_dns + has_errors=true + for attempt in $(seq 0 20); do + sleep 1 + if ip link show dev tun0 &> /dev/null; then + success "tun0 interface is up!" + has_errors=false + break + fi + done + + if $has_errors; then + 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" + tail -n 20 /var/log/openvpn-client.log | tee -a $LOGFILE + systemctl stop openvpn@client.service + critical "Failed to start OpenVPN client : tun0 interface did not show up" fi - # Set ipv6/ipv4 firewall - if ! is_firewall_set; then - set_firewall + info "Waiting for VPN client to be ready..." + if ! timeout 180 tail -n 0 -f /var/log/openvpn-client.log | grep -q "Initialization Sequence Completed"; then + error "The VPN client didn't complete initiliasation" + tail -n 20 /var/log/openvpn-client.log | tee -a $LOGFILE + systemctl stop openvpn@client.service + critical "Failed to start OpenVPN client" fi - # Update dynamic settings - info "Saving settings..." - ynh_setting_set vpnclient server_ip6 "${new_server_ip6}" - ynh_setting_set vpnclient ip6_gw "${new_ip6_gw}" - ynh_setting_set vpnclient wired_device "${new_wired_device}" + info "Validating that VPN is up and the server is connected to internet..." - ipv4=$(ping -w3 -c1 ip.yunohost.org >/dev/null 2>&1 && curl --max-time 5 https://ip.yunohost.org --silent) - ipv6=$(ping -w3 -c1 ip6.yunohost.org >/dev/null 2>&1 && curl --max-time 5 https://ip6.yunohost.org --silent) + ipv4=$(timeout 5 ping -w3 -c1 ip.yunohost.org >/dev/null 2>&1 && curl --max-time 5 https://ip.yunohost.org --silent) + ipv6=$(timeout 5 ping -w3 -c1 ip6.yunohost.org >/dev/null 2>&1 && curl --max-time 5 https://ip6.yunohost.org --silent) - info "Validating that VPN is up and the server is connected to internet..." if ip route get 1.2.3.4 | grep -q tun0; then - if ping -c1 -w5 debian.org >/dev/null; then - success "YunoHost VPN client started!" - info "IPv4 address is $ipv4" - info "IPv6 address is $ipv6" - else - critical "The VPN is up but debian.org cannot be reached, indicating that something is probably misconfigured/blocked." - fi + if timeout 5 ping -c1 -w3 debian.org >/dev/null; then + success "YunoHost VPN client started!" + info "IPv4 address is $ipv4" + info "IPv6 address is $ipv6" + else + critical "The VPN is up but debian.org cannot be reached, indicating that something is probably misconfigured/blocked." + fi else - critical "IPv4 routes are misconfigured !?" + critical "[CRIT] IPv4 routes are misconfigured !?" fi ;; @@ -442,15 +223,17 @@ case "${1}" in info "[vpnclient] Stopping..." rm -f /tmp/.ynh-vpnclient-started - if is_serverip6route_set "${old_server_ip6}"; then - unset_serverip6route "${old_server_ip6}" "${old_ip6_gw}" "${old_wired_device}" - fi - - is_firewall_set && unset_firewall - - is_dns_set && unset_dns + if systemctl is-active -q openvpn@client.service; then + info "Stopping OpenVPN service" + systemctl stop openvpn@client.service - is_openvpn_running && stop_openvpn + for attempt in $(seq 0 20); do + if ip link show dev tun0 &> /dev/null; then + info "(Waiting for tun0 to disappear if it was up)" + sleep 1 + fi + done + fi ;; # ########## # @@ -462,81 +245,12 @@ case "${1}" in $0 start ;; - # ########## # - # Status # - # ########## # - - status) - exitcode=0 - - if [ "${ynh_service_enabled}" -eq 0 ]; then - error "VPN Client Service disabled" - exitcode=1 - fi - - info "Autodetected internet interface: ${new_wired_device} (last start: ${old_wired_device})" - info "Autodetected IPv6 address for the VPN server: ${new_server_ip6} (last start: ${old_server_ip6})" - - if has_ip6delegatedprefix; then - info "IPv6 delegated prefix found" - info "IPv6 address computed from the delegated prefix: ${ynh_ip6_addr}" - - if is_ip6addr_set; then - success "IPv6 address correctly set" - else - error "No IPv6 address set" - exitcode=1 - fi - else - info "No IPv6 delegated prefix found" - fi - - if has_nativeip6; then - info "Native IPv6 detected" - info "Autodetected native IPv6 gateway: ${new_ip6_gw} (last start: ${old_ip6_gw})" - - if is_serverip6route_set "${new_server_ip6}"; then - success "IPv6 server route correctly set" - else - error "No IPv6 server route set" - exitcode=1 - fi - else - info "No native IPv6 detected" - info "No IPv6 server route to set" - fi - - if is_firewall_set; then - success "IPv6/IPv4 firewall set" - else - info "No IPv6/IPv4 firewall set" - exitcode=1 - fi - - if is_dns_set; then - success "Host DNS correctly set" - else - error "No host DNS set" - exitcode=1 - fi - - if is_openvpn_running; then - success "Openvpn is running" - else - error "Openvpn is not running" - exitcode=1 - fi - - exit ${exitcode} - ;; - # ########## # # Halp # # ########## # *) - echo "Usage: $0 {start|stop|restart|status}" - exit 1 + echo "Usage: $0 {start|stop|restart}" ;; esac diff --git a/conf/ynh-vpnclient-checker.sh b/conf/ynh-vpnclient-checker.sh index 7c86632..2162fe2 100644 --- a/conf/ynh-vpnclient-checker.sh +++ b/conf/ynh-vpnclient-checker.sh @@ -1,7 +1,9 @@ #!/bin/bash -if [ ! -e /tmp/.ynh-vpnclient-stopped ] && ! ip route get 1.2.3.4 | grep -q tun0; then - systemctl restart ynh-vpnclient &> /dev/null +if [[ -e /tmp/.ynh-vpnclient-started ]] || ip route get 1.2.3.4 | grep -q tun0; then + echo "[INFO] Service is already running" + exit 0 +else + echo "[INFO] Restarting VPN client service" + yunohost service restart ynh-vpnclient &> /dev/null fi - -exit 0 diff --git a/scripts/_common.sh b/scripts/_common.sh index dd0ba09..2dd363e 100644 --- a/scripts/_common.sh +++ b/scripts/_common.sh @@ -40,8 +40,8 @@ function vpnclient_deploy_files_and_services() mkdir -pm 0775 /etc/openvpn/scripts/route-up.d mkdir -pm 0775 /etc/openvpn/scripts/route-down.d install -b -o root -g root -m 0755 ../conf/scripts/run-parts.sh /etc/openvpn/scripts/run-parts.sh - install -b -o root -g root -m 0755 ../conf/scripts/route-up.d/40-set-ipv6 /etc/openvpn/scripts/route-up.d/40-set-ipv6 - install -b -o root -g root -m 0755 ../conf/scripts/route-down.d/40-unset-ipv6 /etc/openvpn/scripts/route-down.d/40-unset-ipv6 + install -b -o root -g root -m 0755 ../conf/scripts/route-up.d/* /etc/openvpn/scripts/route-up.d/ + install -b -o root -g root -m 0755 ../conf/scripts/route-down.d/* /etc/openvpn/scripts/route-down.d/ #================================================= diff --git a/scripts/backup b/scripts/backup index 2b55f59..2841d4c 100644 --- a/scripts/backup +++ b/scripts/backup @@ -39,8 +39,9 @@ ynh_backup --src_path="/etc/openvpn/client.conf" --not_mandatory ynh_backup --src_path="/etc/openvpn/client.cube" --not_mandatory ynh_backup --src_path="/etc/openvpn/client.ovpn" --not_mandatory -ynh_backup --src_path="/etc/openvpn/scripts/route-up.d/40-set-ipv6" -ynh_backup --src_path="/etc/openvpn/scripts/route-down.d/40-unset-ipv6" +for FILE in $(ls /etc/openvpn/scripts/route-up.d/*-vpnclient-* /etc/openvpn/scripts/route-down.d/*-vpnclient-*); do + ynh_backup --src_path="$FILE" +done ynh_backup --src_path="/etc/openvpn/keys/" ynh_backup --src_path="/etc/openvpn/scripts/run-parts.sh" diff --git a/scripts/config b/scripts/config index 80a13b0..946f51b 100644 --- a/scripts/config +++ b/scripts/config @@ -234,8 +234,8 @@ ynh_app_config_apply() { # Stop vpn client ynh_print_info --message="Stopping vpnclient in order to edit files" - touch /tmp/.ynh-vpnclient-stopped - /usr/local/bin/ynh-vpnclient stop + ynh_systemd_action --service_name="ynh-vpnclient-checker.timer" --action="stop" + ynh_systemd_action --service_name="ynh-vpnclient" --action="stop" chown $app:$app /etc/openvpn/keys chmod go=--- /etc/openvpn/keys @@ -254,9 +254,8 @@ ynh_app_config_apply() { # Start vpn client ynh_print_info --message="Starting vpnclient service if needed" - /usr/local/bin/ynh-vpnclient start - rm -f /tmp/.ynh-vpnclient-stopped - + ynh_systemd_action --service_name="ynh-vpnclient" --action="start" + ynh_systemd_action --service_name="ynh-vpnclient-checker.timer" --action="start" } ynh_app_config_run $1 diff --git a/scripts/remove b/scripts/remove index 348c355..01f8a6b 100644 --- a/scripts/remove +++ b/scripts/remove @@ -43,15 +43,13 @@ systemctl disable $service_checker_name.timer --quiet systemctl stop $service_checker_name systemctl disable $service_checker_name --quiet -if ynh_exec_warn_less yunohost service status $service_name >/dev/null -then +if ynh_exec_warn_less yunohost service status $service_name >/dev/null; then yunohost service stop $service_name yunohost service disable $service_name --quiet yunohost service remove $service_name fi -for FILE in $(ls /etc/systemd/system/$service_name* /usr/local/bin/ynh-vpnclient* /tmp/.ynh-vpnclient-*) -do +for FILE in $(ls /etc/systemd/system/$service_name* /usr/local/bin/ynh-vpnclient* /tmp/.ynh-vpnclient-*); do ynh_secure_remove "$FILE" done @@ -68,8 +66,10 @@ ynh_secure_remove /etc/openvpn/client.ovpn # Remove openvpn script ynh_secure_remove /etc/openvpn/scripts/run-parts.sh -ynh_secure_remove "/etc/openvpn/scripts/route-up.d/40-set-ipv6" -ynh_secure_remove "/etc/openvpn/scripts/route-down.d/40-unset-ipv6" + +for FILE in $(ls /etc/openvpn/scripts/route-up.d/*-vpnclient-* /etc/openvpn/scripts/route-down.d/*-vpnclient-*); do + ynh_secure_remove "$FILE" +done # Remove YunoHost hook ynh_secure_remove /etc/yunohost/hooks.d/90-vpnclient.tpl diff --git a/scripts/restore b/scripts/restore index e92dfe5..6dfd9ae 100644 --- a/scripts/restore +++ b/scripts/restore @@ -28,23 +28,7 @@ app=$YNH_APP_INSTANCE_NAME #================================================= ynh_print_info "Restoring the app files..." -ynh_restore_file --origin_path="/usr/local/bin/$service_name-loadcubefile.sh" - -ynh_restore_file --origin_path="/etc/yunohost/hooks.d/90-vpnclient.tpl" - -ynh_restore_file --origin_path="/etc/openvpn/client.conf.tpl" -ynh_restore_file --origin_path="/etc/openvpn/client.conf" --not_mandatory -ynh_restore_file --origin_path="/etc/openvpn/client.cube" --not_mandatory -ynh_restore_file --origin_path="/etc/openvpn/client.ovpn" --not_mandatory - -ynh_restore_file --origin_path="/etc/openvpn/scripts/route-up.d/40-set-ipv6" -ynh_restore_file --origin_path="/etc/openvpn/scripts/route-down.d/40-unset-ipv6" - -ynh_restore_file --origin_path="/etc/openvpn/keys/" -ynh_restore_file --origin_path="/etc/openvpn/scripts/run-parts.sh" - -ynh_restore_file --origin_path="/usr/local/bin/$service_name" -ynh_restore_file --origin_path="/usr/local/bin/$service_checker_name.sh" +ynh_restore #================================================= # RECREATE THE DEDICATED USER @@ -69,20 +53,31 @@ ynh_install_app_dependencies "$pkg_dependencies" #================================================= ynh_print_info "Restoring the systemd configuration..." -ynh_restore_file --origin_path="/etc/systemd/system/$service_name.service" -ynh_restore_file --origin_path="/etc/systemd/system/$service_checker_name.service" -ynh_restore_file --origin_path="/etc/systemd/system/$service_checker_name.timer" -ynh_restore_file --origin_path="/etc/systemd/system/openvpn@.service.d/override.conf" systemctl daemon-reload -systemctl enable "$service_name.service" --quiet -systemctl enable "$service_checker_name.service" --quiet -systemctl enable "openvpn@.service" --quiet + +# Set default inits +# The boot order of these services are important, so they are disabled by default +# and the vpnclient service handles them. +systemctl disable openvpn --quiet +systemctl stop openvpn + +# main service + +yunohost service add $service_name --description "Tunnels the internet traffic through a VPN" --need_lock --test_status="systemctl is-active openvpn@client.service" --log "/var/log/ynh-vpnclient.log" +yunohost service enable "$service_name" + +# checker service + +systemctl start "$service_checker_name" +systemctl enable "$service_checker_name" --quiet +systemctl start "$service_checker_name.timer" +systemctl enable "$service_checker_name.timer" --quiet + #================================================= # ADVERTISE SERVICE IN ADMIN PANEL #================================================= -yunohost service add $service_name --description "Tunnels the internet traffic through a VPN" --need_lock --test_status="systemctl is-active openvpn@client.service" --log "/var/log/ynh-vpnclient.log" #================================================= # GENERIC FINALIZATION