[1/2] ipsec: Add script to ensure VPNs are always on

Message ID 20200205112425.20108-1-michael.tremer@ipfire.org
State New
Headers show
Series [1/2] ipsec: Add script to ensure VPNs are always on | expand

Commit Message

Michael Tremer Feb. 5, 2020, 11:24 a.m. UTC
With an IPFire box behind NAT it is difficult to keep an IPsec
VPN up all of the time. On-demand mode does not work when one
side cannot initiate the connection.

This patch adds a script which will check every 5 minutes or
when RED comes up if all VPNs are up and launch those which
are not.

This should ensure that we are constantly attempting to
establish the connection.

Additionally this patch changes that "always-on" VPNs will
be "routed" like "on-demand" connections. When we see traffic,
we will now automatically try to bring up the tunnel.

Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
---
 config/cron/crontab                    |  3 ++
 config/rootfiles/common/aarch64/stage2 |  1 +
 config/rootfiles/common/stage2         |  1 +
 config/rootfiles/common/x86_64/stage2  |  1 +
 html/cgi-bin/vpnmain.cgi               | 10 ++++--
 src/misc-progs/ipsecctrl.c             |  1 +
 src/scripts/ipsec-always-on            | 65 ++++++++++++++++++++++++++++++++++
 7 files changed, 79 insertions(+), 3 deletions(-)
 create mode 100644 src/scripts/ipsec-always-on

Comments

Tom Rymes Feb. 5, 2020, 3:23 p.m. UTC | #1
I probably misunderstood something here, but if we're setting all 
tunnels to auto=route, what's the difference between On-Demand and 
Always-On?

Also, this should be a nice improvement for reliability, as lots of 
folks don't know that choosing "Always-On" means "auto=start", which is 
far less reliable.

Tom

On 02/05/2020 6:24 AM, Michael Tremer wrote:
> With an IPFire box behind NAT it is difficult to keep an IPsec
> VPN up all of the time. On-demand mode does not work when one
> side cannot initiate the connection.
> 
> This patch adds a script which will check every 5 minutes or
> when RED comes up if all VPNs are up and launch those which
> are not.
> 
> This should ensure that we are constantly attempting to
> establish the connection.
> 
> Additionally this patch changes that "always-on" VPNs will
> be "routed" like "on-demand" connections. When we see traffic,
> we will now automatically try to bring up the tunnel.
> 
> Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
> ---
>   config/cron/crontab                    |  3 ++
>   config/rootfiles/common/aarch64/stage2 |  1 +
>   config/rootfiles/common/stage2         |  1 +
>   config/rootfiles/common/x86_64/stage2  |  1 +
>   html/cgi-bin/vpnmain.cgi               | 10 ++++--
>   src/misc-progs/ipsecctrl.c             |  1 +
>   src/scripts/ipsec-always-on            | 65 ++++++++++++++++++++++++++++++++++
>   7 files changed, 79 insertions(+), 3 deletions(-)
>   create mode 100644 src/scripts/ipsec-always-on
> 
> diff --git a/config/cron/crontab b/config/cron/crontab
> index 56801394e..46cbe6ece 100644
> --- a/config/cron/crontab
> +++ b/config/cron/crontab
> @@ -30,6 +30,9 @@ HOME=/
>   # Update dynamic DNS records every five minutes.
>   */5 * * * *	[ -f "/var/ipfire/red/active" ] && /usr/bin/ddns update-all
>   
> +# Make sure VPNs are up
> +*/5 * * * *	/usr/local/bin/ipsec-always-on
> +
>   # Logwatch
>   05 0 * * *	/usr/local/bin/logwatch > /var/log/logwatch/`date -I -d yesterday`; \
>   		LOGWATCH_KEEP=$(sed -ne 's/^LOGWATCH_KEEP=\([0-9]\+\)$/\1/p' /var/ipfire/logging/settings); \
> diff --git a/config/rootfiles/common/aarch64/stage2 b/config/rootfiles/common/aarch64/stage2
> index f4169a44e..eda34f743 100644
> --- a/config/rootfiles/common/aarch64/stage2
> +++ b/config/rootfiles/common/aarch64/stage2
> @@ -95,6 +95,7 @@ usr/local/bin/convert-dns-settings
>   usr/local/bin/convert-ovpn
>   usr/local/bin/filesystem-cleanup
>   usr/local/bin/hddshutdown
> +usr/local/bin/ipsec-always-on
>   usr/local/bin/ipsec-interfaces
>   usr/local/bin/makegraphs
>   usr/local/bin/qosd
> diff --git a/config/rootfiles/common/stage2 b/config/rootfiles/common/stage2
> index fca540431..cc0e1dea5 100644
> --- a/config/rootfiles/common/stage2
> +++ b/config/rootfiles/common/stage2
> @@ -94,6 +94,7 @@ usr/local/bin/convert-dns-settings
>   usr/local/bin/convert-ovpn
>   usr/local/bin/filesystem-cleanup
>   usr/local/bin/hddshutdown
> +usr/local/bin/ipsec-always-on
>   usr/local/bin/ipsec-interfaces
>   usr/local/bin/makegraphs
>   usr/local/bin/qosd
> diff --git a/config/rootfiles/common/x86_64/stage2 b/config/rootfiles/common/x86_64/stage2
> index cc67837e5..28a99ceec 100644
> --- a/config/rootfiles/common/x86_64/stage2
> +++ b/config/rootfiles/common/x86_64/stage2
> @@ -96,6 +96,7 @@ usr/local/bin/convert-dns-settings
>   usr/local/bin/convert-ovpn
>   usr/local/bin/filesystem-cleanup
>   usr/local/bin/hddshutdown
> +usr/local/bin/ipsec-always-on
>   usr/local/bin/ipsec-interfaces
>   usr/local/bin/makegraphs
>   usr/local/bin/qosd
> diff --git a/html/cgi-bin/vpnmain.cgi b/html/cgi-bin/vpnmain.cgi
> index 43cdc5aa0..b3cd3e51e 100644
> --- a/html/cgi-bin/vpnmain.cgi
> +++ b/html/cgi-bin/vpnmain.cgi
> @@ -453,7 +453,7 @@ sub writeipsecfiles {
>   
>   		my $start_action = $lconfighash{$key}[33];
>   		if (!$start_action) {
> -			$start_action = "start";
> +			$start_action = "route";
>   		}
>   
>   		my $inactivity_timeout = $lconfighash{$key}[34];
> @@ -466,13 +466,17 @@ sub writeipsecfiles {
>   			print CONF "\tauto=add\n";
>   			print CONF "\trightsourceip=$lvpnsettings{'RW_NET'}\n";
>   		} else {
> -			print CONF "\tauto=$start_action\n";
> -
>   			# If in on-demand mode, we terminate the tunnel
>   			# after 15 min of no traffic
>   			if ($start_action eq 'route' && $inactivity_timeout > 0) {
>   				print CONF "\tinactivity=$inactivity_timeout\n";
>   			}
> +
> +			# Always route connections so that we have the triggers
> +			if ($start_action eq "start") {
> +				$start_action = "route";
> +			}
> +			print CONF "\tauto=$start_action\n";
>   		}
>   
>   		# Fragmentation
> diff --git a/src/misc-progs/ipsecctrl.c b/src/misc-progs/ipsecctrl.c
> index 2a64775f0..54e3b3410 100644
> --- a/src/misc-progs/ipsecctrl.c
> +++ b/src/misc-progs/ipsecctrl.c
> @@ -216,6 +216,7 @@ int main(int argc, char *argv[]) {
>   		safe_system("/usr/lib/firewall/ipsec-policy >/dev/null");
>   		safe_system("/usr/local/bin/ipsec-interfaces >/dev/null");
>   		safe_system("/usr/sbin/ipsec restart >/dev/null");
> +		safe_system("/usr/local/bin/ipsec-always-on >/dev/null");
>                   exit(0);
>           }
>   
> diff --git a/src/scripts/ipsec-always-on b/src/scripts/ipsec-always-on
> new file mode 100644
> index 000000000..34cae169d
> --- /dev/null
> +++ b/src/scripts/ipsec-always-on
> @@ -0,0 +1,65 @@
> +#!/bin/bash
> +###############################################################################
> +#                                                                             #
> +# IPFire.org - A linux based firewall                                         #
> +# Copyright (C) 2020 IPFire Team                                              #
> +#                                                                             #
> +# This program is free software: you can redistribute it and/or modify        #
> +# it under the terms of the GNU General Public License as published by        #
> +# the Free Software Foundation, either version 3 of the License, or           #
> +# (at your option) any later version.                                         #
> +#                                                                             #
> +# This program is distributed in the hope that it will be useful,             #
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
> +# GNU General Public License for more details.                                #
> +#                                                                             #
> +# You should have received a copy of the GNU General Public License           #
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
> +#                                                                             #
> +###############################################################################
> +
> +VPN_CONFIG="/var/ipfire/vpn/config"
> +
> +VARS=(
> +	id status name lefthost type ctype psk local local_id leftsubnets
> +	remote_id remote rightsubnets x3 x4 x5 x6 x7 x8 x9 x10 x11 x12
> +	x13 x14 x15 x16 x17 x18 x19 proto x20 x21 x22
> +	route x23 mode interface_mode interface_address interface_mtu rest
> +)
> +
> +# Load IPsec configuration
> +eval $(/usr/local/bin/readhash /var/ipfire/vpn/settings)
> +
> +log() {
> +	logger -t ipsec "$@"
> +}
> +
> +main() {
> +	# Do nothing if IPsec is disabled
> +	if [ "${ENABLED}" != "on" ]; then
> +		return 0
> +	fi
> +
> +	# Do nothing if we are not online
> +	if [ ! -e "/var/ipfire/red/active" ]; then
> +		return 0
> +	fi
> +
> +	local "${VARS[@]}"
> +	while IFS="," read -r "${VARS[@]}"; do
> +		# Skip disabled connections
> +		[ "${status}" = "on" ] || continue
> +
> +		# Skip all connections that are not in always-on mode
> +		[ "${route}" = "start" ] || continue
> +
> +		# If the connection is not up, try bringing it up
> +		if ! ipsec status "${name}" | grep -q "INSTALLED"; then
> +			log "Trying to start ${name}..."
> +			ipsec stroke up-nb "${name}" &>/dev/null
> +		fi
> +	done < "${VPN_CONFIG}"
> +}
> +
> +main "$@" || exit $?
>
Michael Tremer Feb. 5, 2020, 4:53 p.m. UTC | #2
Hi,

> On 5 Feb 2020, at 15:23, Tom Rymes <trymes@rymes.com> wrote:
> 
> I probably misunderstood something here, but if we're setting all tunnels to auto=route, what's the difference between On-Demand and Always-On?

Thank you for looking at this. Good question.

For strongswan there will be no difference any more, but the script that checks the tunnels every 5 minutes will only try to bring up the “always on” tunnels.

> Also, this should be a nice improvement for reliability, as lots of folks don't know that choosing "Always-On" means "auto=start", which is far less reliable.

I think it is best to not offer these options any more. I suppose this only matters for users where one peer is behind NAT. Then you want to make sure that the tunnel is always up (but wouldn’t you normally always care about this?) or you would want the other side not to initiate a connection.

Best,
-Michael

> 
> Tom
> 
> On 02/05/2020 6:24 AM, Michael Tremer wrote:
>> With an IPFire box behind NAT it is difficult to keep an IPsec
>> VPN up all of the time. On-demand mode does not work when one
>> side cannot initiate the connection.
>> This patch adds a script which will check every 5 minutes or
>> when RED comes up if all VPNs are up and launch those which
>> are not.
>> This should ensure that we are constantly attempting to
>> establish the connection.
>> Additionally this patch changes that "always-on" VPNs will
>> be "routed" like "on-demand" connections. When we see traffic,
>> we will now automatically try to bring up the tunnel.
>> Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
>> ---
>>  config/cron/crontab                    |  3 ++
>>  config/rootfiles/common/aarch64/stage2 |  1 +
>>  config/rootfiles/common/stage2         |  1 +
>>  config/rootfiles/common/x86_64/stage2  |  1 +
>>  html/cgi-bin/vpnmain.cgi               | 10 ++++--
>>  src/misc-progs/ipsecctrl.c             |  1 +
>>  src/scripts/ipsec-always-on            | 65 ++++++++++++++++++++++++++++++++++
>>  7 files changed, 79 insertions(+), 3 deletions(-)
>>  create mode 100644 src/scripts/ipsec-always-on
>> diff --git a/config/cron/crontab b/config/cron/crontab
>> index 56801394e..46cbe6ece 100644
>> --- a/config/cron/crontab
>> +++ b/config/cron/crontab
>> @@ -30,6 +30,9 @@ HOME=/
>>  # Update dynamic DNS records every five minutes.
>>  */5 * * * *	[ -f "/var/ipfire/red/active" ] && /usr/bin/ddns update-all
>>  +# Make sure VPNs are up
>> +*/5 * * * *	/usr/local/bin/ipsec-always-on
>> +
>>  # Logwatch
>>  05 0 * * *	/usr/local/bin/logwatch > /var/log/logwatch/`date -I -d yesterday`; \
>>  		LOGWATCH_KEEP=$(sed -ne 's/^LOGWATCH_KEEP=\([0-9]\+\)$/\1/p' /var/ipfire/logging/settings); \
>> diff --git a/config/rootfiles/common/aarch64/stage2 b/config/rootfiles/common/aarch64/stage2
>> index f4169a44e..eda34f743 100644
>> --- a/config/rootfiles/common/aarch64/stage2
>> +++ b/config/rootfiles/common/aarch64/stage2
>> @@ -95,6 +95,7 @@ usr/local/bin/convert-dns-settings
>>  usr/local/bin/convert-ovpn
>>  usr/local/bin/filesystem-cleanup
>>  usr/local/bin/hddshutdown
>> +usr/local/bin/ipsec-always-on
>>  usr/local/bin/ipsec-interfaces
>>  usr/local/bin/makegraphs
>>  usr/local/bin/qosd
>> diff --git a/config/rootfiles/common/stage2 b/config/rootfiles/common/stage2
>> index fca540431..cc0e1dea5 100644
>> --- a/config/rootfiles/common/stage2
>> +++ b/config/rootfiles/common/stage2
>> @@ -94,6 +94,7 @@ usr/local/bin/convert-dns-settings
>>  usr/local/bin/convert-ovpn
>>  usr/local/bin/filesystem-cleanup
>>  usr/local/bin/hddshutdown
>> +usr/local/bin/ipsec-always-on
>>  usr/local/bin/ipsec-interfaces
>>  usr/local/bin/makegraphs
>>  usr/local/bin/qosd
>> diff --git a/config/rootfiles/common/x86_64/stage2 b/config/rootfiles/common/x86_64/stage2
>> index cc67837e5..28a99ceec 100644
>> --- a/config/rootfiles/common/x86_64/stage2
>> +++ b/config/rootfiles/common/x86_64/stage2
>> @@ -96,6 +96,7 @@ usr/local/bin/convert-dns-settings
>>  usr/local/bin/convert-ovpn
>>  usr/local/bin/filesystem-cleanup
>>  usr/local/bin/hddshutdown
>> +usr/local/bin/ipsec-always-on
>>  usr/local/bin/ipsec-interfaces
>>  usr/local/bin/makegraphs
>>  usr/local/bin/qosd
>> diff --git a/html/cgi-bin/vpnmain.cgi b/html/cgi-bin/vpnmain.cgi
>> index 43cdc5aa0..b3cd3e51e 100644
>> --- a/html/cgi-bin/vpnmain.cgi
>> +++ b/html/cgi-bin/vpnmain.cgi
>> @@ -453,7 +453,7 @@ sub writeipsecfiles {
>>    		my $start_action = $lconfighash{$key}[33];
>>  		if (!$start_action) {
>> -			$start_action = "start";
>> +			$start_action = "route";
>>  		}
>>    		my $inactivity_timeout = $lconfighash{$key}[34];
>> @@ -466,13 +466,17 @@ sub writeipsecfiles {
>>  			print CONF "\tauto=add\n";
>>  			print CONF "\trightsourceip=$lvpnsettings{'RW_NET'}\n";
>>  		} else {
>> -			print CONF "\tauto=$start_action\n";
>> -
>>  			# If in on-demand mode, we terminate the tunnel
>>  			# after 15 min of no traffic
>>  			if ($start_action eq 'route' && $inactivity_timeout > 0) {
>>  				print CONF "\tinactivity=$inactivity_timeout\n";
>>  			}
>> +
>> +			# Always route connections so that we have the triggers
>> +			if ($start_action eq "start") {
>> +				$start_action = "route";
>> +			}
>> +			print CONF "\tauto=$start_action\n";
>>  		}
>>    		# Fragmentation
>> diff --git a/src/misc-progs/ipsecctrl.c b/src/misc-progs/ipsecctrl.c
>> index 2a64775f0..54e3b3410 100644
>> --- a/src/misc-progs/ipsecctrl.c
>> +++ b/src/misc-progs/ipsecctrl.c
>> @@ -216,6 +216,7 @@ int main(int argc, char *argv[]) {
>>  		safe_system("/usr/lib/firewall/ipsec-policy >/dev/null");
>>  		safe_system("/usr/local/bin/ipsec-interfaces >/dev/null");
>>  		safe_system("/usr/sbin/ipsec restart >/dev/null");
>> +		safe_system("/usr/local/bin/ipsec-always-on >/dev/null");
>>                  exit(0);
>>          }
>>  diff --git a/src/scripts/ipsec-always-on b/src/scripts/ipsec-always-on
>> new file mode 100644
>> index 000000000..34cae169d
>> --- /dev/null
>> +++ b/src/scripts/ipsec-always-on
>> @@ -0,0 +1,65 @@
>> +#!/bin/bash
>> +###############################################################################
>> +#                                                                             #
>> +# IPFire.org - A linux based firewall                                         #
>> +# Copyright (C) 2020 IPFire Team                                              #
>> +#                                                                             #
>> +# This program is free software: you can redistribute it and/or modify        #
>> +# it under the terms of the GNU General Public License as published by        #
>> +# the Free Software Foundation, either version 3 of the License, or           #
>> +# (at your option) any later version.                                         #
>> +#                                                                             #
>> +# This program is distributed in the hope that it will be useful,             #
>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
>> +# GNU General Public License for more details.                                #
>> +#                                                                             #
>> +# You should have received a copy of the GNU General Public License           #
>> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
>> +#                                                                             #
>> +###############################################################################
>> +
>> +VPN_CONFIG="/var/ipfire/vpn/config"
>> +
>> +VARS=(
>> +	id status name lefthost type ctype psk local local_id leftsubnets
>> +	remote_id remote rightsubnets x3 x4 x5 x6 x7 x8 x9 x10 x11 x12
>> +	x13 x14 x15 x16 x17 x18 x19 proto x20 x21 x22
>> +	route x23 mode interface_mode interface_address interface_mtu rest
>> +)
>> +
>> +# Load IPsec configuration
>> +eval $(/usr/local/bin/readhash /var/ipfire/vpn/settings)
>> +
>> +log() {
>> +	logger -t ipsec "$@"
>> +}
>> +
>> +main() {
>> +	# Do nothing if IPsec is disabled
>> +	if [ "${ENABLED}" != "on" ]; then
>> +		return 0
>> +	fi
>> +
>> +	# Do nothing if we are not online
>> +	if [ ! -e "/var/ipfire/red/active" ]; then
>> +		return 0
>> +	fi
>> +
>> +	local "${VARS[@]}"
>> +	while IFS="," read -r "${VARS[@]}"; do
>> +		# Skip disabled connections
>> +		[ "${status}" = "on" ] || continue
>> +
>> +		# Skip all connections that are not in always-on mode
>> +		[ "${route}" = "start" ] || continue
>> +
>> +		# If the connection is not up, try bringing it up
>> +		if ! ipsec status "${name}" | grep -q "INSTALLED"; then
>> +			log "Trying to start ${name}..."
>> +			ipsec stroke up-nb "${name}" &>/dev/null
>> +		fi
>> +	done < "${VPN_CONFIG}"
>> +}
>> +
>> +main "$@" || exit $?
Tom Rymes Feb. 5, 2020, 5:19 p.m. UTC | #3
On 02/05/2020 11:53 AM, Michael Tremer wrote:
> Hi,
> 
>> On 5 Feb 2020, at 15:23, Tom Rymes <trymes@rymes.com> wrote:
>>
>> I probably misunderstood something here, but if we're setting all tunnels to auto=route, what's the difference between On-Demand and Always-On?
> 
> Thank you for looking at this. Good question.
> 
> For strongswan there will be no difference any more, but the script that checks the tunnels every 5 minutes will only try to bring up the “always on” tunnels.

OK, I see what you mean. May I suggest that we eliminate the distinction 
between "Always-On" and "On Demand" and just retain the time limit for 
inactivity? Tunnels set to have a limited time before being shut down to 
inactivity shouldn't be brought back up by the script and those that do 
not should be.

>> Also, this should be a nice improvement for reliability, as lots of folks don't know that choosing "Always-On" means "auto=start", which is far less reliable.
> 
> I think it is best to not offer these options any more. I suppose this only matters for users where one peer is behind NAT. Then you want to make sure that the tunnel is always up (but wouldn’t you normally always care about this?) or you would want the other side not to initiate a connection.

Maybe you're saying the same thing I just said up above?

tom
Michael Tremer Feb. 5, 2020, 5:22 p.m. UTC | #4
Hi,

> On 5 Feb 2020, at 17:19, Tom Rymes <trymes@rymes.com> wrote:
> 
> On 02/05/2020 11:53 AM, Michael Tremer wrote:
>> Hi,
>>> On 5 Feb 2020, at 15:23, Tom Rymes <trymes@rymes.com> wrote:
>>> 
>>> I probably misunderstood something here, but if we're setting all tunnels to auto=route, what's the difference between On-Demand and Always-On?
>> Thank you for looking at this. Good question.
>> For strongswan there will be no difference any more, but the script that checks the tunnels every 5 minutes will only try to bring up the “always on” tunnels.
> 
> OK, I see what you mean. May I suggest that we eliminate the distinction between "Always-On" and "On Demand" and just retain the time limit for inactivity? Tunnels set to have a limited time before being shut down to inactivity shouldn't be brought back up by the script and those that do not should be.

That would still change one more thing. We would then decide to always keep all tunnels up. I am not sure if that has any disadvantages for anyone really. But we would definitely have to drop the timeout, too, because otherwise the tunnel will be brought down and the script will bring it back up again shortly after.

This has crossed my mind when I wrote the script today.

> 
>>> Also, this should be a nice improvement for reliability, as lots of folks don't know that choosing "Always-On" means "auto=start", which is far less reliable.
>> I think it is best to not offer these options any more. I suppose this only matters for users where one peer is behind NAT. Then you want to make sure that the tunnel is always up (but wouldn’t you normally always care about this?) or you would want the other side not to initiate a connection.
> 
> Maybe you're saying the same thing I just said up above?

Yes :)

> 
> tom
Tom Rymes Feb. 5, 2020, 5:36 p.m. UTC | #5
On 02/05/2020 12:22 PM, Michael Tremer wrote:
> Hi,
> 
>> On 5 Feb 2020, at 17:19, Tom Rymes <trymes@rymes.com> wrote:

[snip]

>> OK, I see what you mean. May I suggest that we eliminate the distinction between "Always-On" and "On Demand" and just retain the time limit for inactivity? Tunnels set to have a limited time before being shut down to inactivity shouldn't be brought back up by the script and those that do not should be.
> 
> That would still change one more thing. We would then decide to always keep all tunnels up. I am not sure if that has any disadvantages for anyone really. But we would definitely have to drop the timeout, too, because otherwise the tunnel will be brought down and the script will bring it back up again shortly after.

Sorry for being unclear. There are currently eight options for 
"Inactivity Timeout", including "Unlimited". I would propose that the 
script you are adding should only bring back up tunnels whose Inactivity 
Timeout is set to "Unlimited". A tunnel with a timeout of one hour would 
time out, go down, and then the script should ignore it.

Does that make sense?

Tom
Michael Tremer Feb. 6, 2020, 3:03 p.m. UTC | #6
Hi,

> On 5 Feb 2020, at 17:36, Tom Rymes <trymes@rymes.com> wrote:
> 
> On 02/05/2020 12:22 PM, Michael Tremer wrote:
>> Hi,
>>> On 5 Feb 2020, at 17:19, Tom Rymes <trymes@rymes.com> wrote:
> 
> [snip]
> 
>>> OK, I see what you mean. May I suggest that we eliminate the distinction between "Always-On" and "On Demand" and just retain the time limit for inactivity? Tunnels set to have a limited time before being shut down to inactivity shouldn't be brought back up by the script and those that do not should be.
>> That would still change one more thing. We would then decide to always keep all tunnels up. I am not sure if that has any disadvantages for anyone really. But we would definitely have to drop the timeout, too, because otherwise the tunnel will be brought down and the script will bring it back up again shortly after.
> 
> Sorry for being unclear. There are currently eight options for "Inactivity Timeout", including "Unlimited". I would propose that the script you are adding should only bring back up tunnels whose Inactivity Timeout is set to "Unlimited". A tunnel with a timeout of one hour would time out, go down, and then the script should ignore it.

The inactivity timeout is only active when the connection is in “on demand” mode. The script ignores connections in that mode, so nothing will happen here.

> Does that make sense?
> 
> Tom
Tom Rymes Feb. 6, 2020, 8:06 p.m. UTC | #7
On 02/06/2020 10:03 AM, Michael Tremer wrote:
> Hi,
> 
>> On 5 Feb 2020, at 17:36, Tom Rymes <trymes@rymes.com> wrote:

[snip]

>> Sorry for being unclear. There are currently eight options for "Inactivity Timeout", including "Unlimited". I would propose that the script you are adding should only bring back up tunnels whose Inactivity Timeout is set to "Unlimited". A tunnel with a timeout of one hour would time out, go down, and then the script should ignore it.
> 
> The inactivity timeout is only active when the connection is in “on demand” mode. The script ignores connections in that mode, so nothing will happen here.

Right, but I had proposed to combine "Always On" and "On Demand" into 
one, as they will effectively be the same after this proposed change. 
After this change, unless I am missing something, the only difference 
will be the "Inactivity Timeout".

If "On Demand" and "Always On" are combined into "Normal" (as opposed to 
"Wait for connection initiation"), then the script can use the Inactivty 
timeout to determine which tunnels to bring up. Come to think of it, 
shouldn't the script also bring up any "On-Demand" tunnels that are set 
to a timeout of "Unlimited"? I know that we run *all* of our tunnels as 
"On-Demand/Unlimited" because auto=route is so much more reliable.

It's not really a big deal either way, but if the only difference 
between "On Demand" and "Always On" after this proposed change is going 
to be the inactivity timeout, then I'd say merge the two into one and 
use the Inactivity Timeout of "Unlimited" to denote which tunnels should 
always be up. It's cleaner and less confusing.

My $0.02.

Tom

Patch

diff --git a/config/cron/crontab b/config/cron/crontab
index 56801394e..46cbe6ece 100644
--- a/config/cron/crontab
+++ b/config/cron/crontab
@@ -30,6 +30,9 @@  HOME=/
 # Update dynamic DNS records every five minutes.
 */5 * * * *	[ -f "/var/ipfire/red/active" ] && /usr/bin/ddns update-all
 
+# Make sure VPNs are up
+*/5 * * * *	/usr/local/bin/ipsec-always-on
+
 # Logwatch
 05 0 * * *	/usr/local/bin/logwatch > /var/log/logwatch/`date -I -d yesterday`; \
 		LOGWATCH_KEEP=$(sed -ne 's/^LOGWATCH_KEEP=\([0-9]\+\)$/\1/p' /var/ipfire/logging/settings); \
diff --git a/config/rootfiles/common/aarch64/stage2 b/config/rootfiles/common/aarch64/stage2
index f4169a44e..eda34f743 100644
--- a/config/rootfiles/common/aarch64/stage2
+++ b/config/rootfiles/common/aarch64/stage2
@@ -95,6 +95,7 @@  usr/local/bin/convert-dns-settings
 usr/local/bin/convert-ovpn
 usr/local/bin/filesystem-cleanup
 usr/local/bin/hddshutdown
+usr/local/bin/ipsec-always-on
 usr/local/bin/ipsec-interfaces
 usr/local/bin/makegraphs
 usr/local/bin/qosd
diff --git a/config/rootfiles/common/stage2 b/config/rootfiles/common/stage2
index fca540431..cc0e1dea5 100644
--- a/config/rootfiles/common/stage2
+++ b/config/rootfiles/common/stage2
@@ -94,6 +94,7 @@  usr/local/bin/convert-dns-settings
 usr/local/bin/convert-ovpn
 usr/local/bin/filesystem-cleanup
 usr/local/bin/hddshutdown
+usr/local/bin/ipsec-always-on
 usr/local/bin/ipsec-interfaces
 usr/local/bin/makegraphs
 usr/local/bin/qosd
diff --git a/config/rootfiles/common/x86_64/stage2 b/config/rootfiles/common/x86_64/stage2
index cc67837e5..28a99ceec 100644
--- a/config/rootfiles/common/x86_64/stage2
+++ b/config/rootfiles/common/x86_64/stage2
@@ -96,6 +96,7 @@  usr/local/bin/convert-dns-settings
 usr/local/bin/convert-ovpn
 usr/local/bin/filesystem-cleanup
 usr/local/bin/hddshutdown
+usr/local/bin/ipsec-always-on
 usr/local/bin/ipsec-interfaces
 usr/local/bin/makegraphs
 usr/local/bin/qosd
diff --git a/html/cgi-bin/vpnmain.cgi b/html/cgi-bin/vpnmain.cgi
index 43cdc5aa0..b3cd3e51e 100644
--- a/html/cgi-bin/vpnmain.cgi
+++ b/html/cgi-bin/vpnmain.cgi
@@ -453,7 +453,7 @@  sub writeipsecfiles {
 
 		my $start_action = $lconfighash{$key}[33];
 		if (!$start_action) {
-			$start_action = "start";
+			$start_action = "route";
 		}
 
 		my $inactivity_timeout = $lconfighash{$key}[34];
@@ -466,13 +466,17 @@  sub writeipsecfiles {
 			print CONF "\tauto=add\n";
 			print CONF "\trightsourceip=$lvpnsettings{'RW_NET'}\n";
 		} else {
-			print CONF "\tauto=$start_action\n";
-
 			# If in on-demand mode, we terminate the tunnel
 			# after 15 min of no traffic
 			if ($start_action eq 'route' && $inactivity_timeout > 0) {
 				print CONF "\tinactivity=$inactivity_timeout\n";
 			}
+
+			# Always route connections so that we have the triggers
+			if ($start_action eq "start") {
+				$start_action = "route";
+			}
+			print CONF "\tauto=$start_action\n";
 		}
 
 		# Fragmentation
diff --git a/src/misc-progs/ipsecctrl.c b/src/misc-progs/ipsecctrl.c
index 2a64775f0..54e3b3410 100644
--- a/src/misc-progs/ipsecctrl.c
+++ b/src/misc-progs/ipsecctrl.c
@@ -216,6 +216,7 @@  int main(int argc, char *argv[]) {
 		safe_system("/usr/lib/firewall/ipsec-policy >/dev/null");
 		safe_system("/usr/local/bin/ipsec-interfaces >/dev/null");
 		safe_system("/usr/sbin/ipsec restart >/dev/null");
+		safe_system("/usr/local/bin/ipsec-always-on >/dev/null");
                 exit(0);
         }
 
diff --git a/src/scripts/ipsec-always-on b/src/scripts/ipsec-always-on
new file mode 100644
index 000000000..34cae169d
--- /dev/null
+++ b/src/scripts/ipsec-always-on
@@ -0,0 +1,65 @@ 
+#!/bin/bash
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2020 IPFire Team                                              #
+#                                                                             #
+# This program is free software: you can redistribute it and/or modify        #
+# it under the terms of the GNU General Public License as published by        #
+# the Free Software Foundation, either version 3 of the License, or           #
+# (at your option) any later version.                                         #
+#                                                                             #
+# This program is distributed in the hope that it will be useful,             #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
+# GNU General Public License for more details.                                #
+#                                                                             #
+# You should have received a copy of the GNU General Public License           #
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+VPN_CONFIG="/var/ipfire/vpn/config"
+
+VARS=(
+	id status name lefthost type ctype psk local local_id leftsubnets
+	remote_id remote rightsubnets x3 x4 x5 x6 x7 x8 x9 x10 x11 x12
+	x13 x14 x15 x16 x17 x18 x19 proto x20 x21 x22
+	route x23 mode interface_mode interface_address interface_mtu rest
+)
+
+# Load IPsec configuration
+eval $(/usr/local/bin/readhash /var/ipfire/vpn/settings)
+
+log() {
+	logger -t ipsec "$@"
+}
+
+main() {
+	# Do nothing if IPsec is disabled
+	if [ "${ENABLED}" != "on" ]; then
+		return 0
+	fi
+
+	# Do nothing if we are not online
+	if [ ! -e "/var/ipfire/red/active" ]; then
+		return 0
+	fi
+
+	local "${VARS[@]}"
+	while IFS="," read -r "${VARS[@]}"; do
+		# Skip disabled connections
+		[ "${status}" = "on" ] || continue
+
+		# Skip all connections that are not in always-on mode
+		[ "${route}" = "start" ] || continue
+
+		# If the connection is not up, try bringing it up
+		if ! ipsec status "${name}" | grep -q "INSTALLED"; then
+			log "Trying to start ${name}..."
+			ipsec stroke up-nb "${name}" &>/dev/null
+		fi
+	done < "${VPN_CONFIG}"
+}
+
+main "$@" || exit $?