Skip to content

ipsec: connections VTI support for dynamic local/remote IP #6781

Closed

Description

@pfoo

Important notices

Before you add a new report, we ask you kindly to acknowledge the following:

Describe the bug

With legacy IPsec configuration it is possible to :

  • input FQDN in ipsec remote outter IP configuration.
  • use interface name for local outter IP configuration.

VTI interface was then created/modified according to FQDN resolution and/or interface IP change, this was usefull when having a dynamic local IP (assigned to the interface) or a dynamic remote IP.

Main issue : With the new IPsec Connection UI it is impossible to add FQDN or interface name in the Virtual Tunnel Interfaces tab, which breaks dynamic IP VTI configurations.

IPSec configuration itself should work as we can input FQDN in local_addrs/remote_addrs fields, however the help text state that "If FQDNs are assigned, they are resolved every time a configuration lookup is done."
Secondary issue : An option to regularly check if the FQDN changed it's IP (or maybe allow to use alias) and reload ipsec and associated automatic-pass firewall rules might be useful as it will prevent unnecessary use of %any.

For now new IPsec connection UI seems broken for dynamic local/remote IP in routed/VTI setup.

To Reproduce

Steps to reproduce the behavior:

  1. Go to VPN -> IPsec -> Virtual Tunnel Interfaces
  2. Click on 'Add'
  3. Try to enter anything else than an IP address in 'Local address' or 'Remote address'
  4. See error : 'Please specify a valid address.'

Expected behavior

Allow IPsec routed/VTI configurations to works with local and remote dynamic IPs :

  • VTI tab -> For remote addresses : Add ability to enter FQDN and/or aliases and resolve them regularly and update VTI interface accordingly.
  • VTI tab -> For local addresses : Add ability to track an interface to update the VTI local IP accordingly. This would also means we need to specify if we want to track ipv4 or ipv6 of the interface.
  • IPSec Connection tab -> make sure FQDN are regularly resolved or allow to use aliases and reload ipsec when the IP changes.

Describe alternatives you considered

The only workaround would be to manually update the VTI interface configuration every time the local/remote IP changes.

Additional context

Some reports on the forum :
https://forum.opnsense.org/index.php?topic=35140.0
https://forum.opnsense.org/index.php?topic=33117.0

Environment

OPNsense 23.7.2-amd64

Activity

  1. AdSchellevis commented on Aug 25, 2023

    @AdSchellevis
    Member
  2. pfoo commented on Aug 25, 2023

    @pfoo
    Author

    No it won't be as reliable as with a static IP. But I have a working VTI configuration with dynamic IP for months now on opnsense, years even on pfsense.

    As soon as you have dynamic IP somewhere, it does not work reliably anymore, VTI or not, as you will have to tolerate some downtime.

    The tunnel endpoints can be changed live with a single ifconfig command. The main issue is detecting when it needs to be changed.

    Unfortunately a lot of us are still stuck to dynamic IP especially on non-professional home ISP

  3. AdSchellevis commented on Aug 26, 2023

    @AdSchellevis
    Member

    if/when if_ipsec(4) supports the endpoints to be changed without recreating the interface, we might reconsider putting time in this, but until that time, this isn't a priority as it will break somewhere and lead to hard to explain support cases. While developing the new module we tried different options (binding to non existing endpoints, changing on updates), but it's just not reliable due to upstream reasons.

  4. 8191 commented on Sep 1, 2023

    @8191
    Member

    No it won't be as reliable as with a static IP. But I have a working VTI configuration with dynamic IP for months now on opnsense, years even on pfsense.

    I double that. I use IPsec VTI since a few years now (I believe starting with 21.1) and did not have any major problems so far.
    In my case the IPs do not change frequently, only once each few months, I'd say. Anyway, restarting the IPsec tunnel on the centre firewall is still easier than modifying the configuration (causing potential config errors) after each IP change.

  5. AdSchellevis commented on Sep 1, 2023

    @AdSchellevis
    Member

    You can keep using the legacy tunnels, maybe eventually we think of something that is sustainable or an upstream change will offer the possibility to change if_ipsec(4) addressing on the fly without breaking traffic at some point.

  6. added
    featureAdding new functionality
    and removed
    supportCommunity support or awaiting triage
    on Oct 18, 2023
  7. self-assigned this
    on Oct 18, 2023
  8. cektom commented on Oct 29, 2023

    @cektom

    +1 for this. I'm also using a Site-to-Site Tunnel with dynamic IPs on both sides for over a year now. With some monit rules to check the tunnel health and dead peer detection enabled, the downtime was ~5-10 minutes every few weeks.

  9. bipbip365 commented on Nov 23, 2023

    @bipbip365

    @cektom
    Hello, I might have rules to check tunnel health and dead peer detection enabled. I need to put the same configuration in place

  10. AdSchellevis commented on Feb 12, 2024

    @AdSchellevis
    Member

    This 816ecae should do the trick, but now I'm looking for people to try it out.

  11. joni1993 commented on Feb 12, 2024

    @joni1993

    I could successfully set this up with one dynamic IP and one static at least. (Leaving both addresses empty).

  12. 13 remaining items

  13. joni1993 commented on Apr 23, 2024

    @joni1993

    Are they really set up similar?
    I do not know where the generated files are coming from but looking at /usr/local/etc/swanctl/swanctl.conf, the old method generates local_addrs and remote_addrs configuration for the connection, while the new method does not generate such configuration entries.

    On the other hand the new method generates the updown event handler in the children section (which the old one does not) - and manually executing the handler like /usr/local/opnsense/scripts/ipsec/updown_event.py --connection_child 782d9cb1-8af0-4890-a8bf-007eb1934394 --action up --reqid 100 --local MYIP --remote REMOTEIP throws:

    Traceback (most recent call last):
      File "/usr/local/lib/python3.9/configparser.py", line 789, in get
        value = d[option]
      File "/usr/local/lib/python3.9/collections/__init__.py", line 941, in __getitem__
        return self.__missing__(key)            # support subclasses that define __missing__
      File "/usr/local/lib/python3.9/collections/__init__.py", line 933, in __missing__
        raise KeyError(key)
    KeyError: 'connection_child'
    
    During handling of the above exception, another exception occurred:
    
    Traceback (most recent call last):
      File "/usr/local/opnsense/scripts/ipsec/updown_event.py", line 64, in <module>
        or cnf.get(section, 'connection_child') == cmd_args.connection_child:
      File "/usr/local/lib/python3.9/configparser.py", line 792, in get
        raise NoOptionError(option, section)
    configparser.NoOptionError: No option 'connection_child' in section: 'vti_72691b462a734a82963ade379b5fb4b5'
    
  14. joni1993 commented on Apr 23, 2024

    @joni1993

    Changing the updown event script as follows:

    
    diff --git a/src/opnsense/scripts/ipsec/updown_event.py b/src/opnsense/scripts/ipsec/updown_event.py
    index dd3030460..6facc4b13 100755
    --- a/src/opnsense/scripts/ipsec/updown_event.py
    +++ b/src/opnsense/scripts/ipsec/updown_event.py
    @@ -59,9 +59,8 @@ if __name__ == '__main__':
                 spds = []
                 vtis = []
                 for section in cnf.sections():
    -                if cnf.get(section, 'reqid') == cmd_args.reqid \
    -                        or cnf.get(section, 'connection_child') == cmd_args.connection_child:
    -                    if section.startswith('spd_'):
    +                if cnf.get(section, 'reqid') == cmd_args.reqid:
    +                    if cnf.has_option(section,"connection_child") and cnf.get(section, 'connection_child') == cmd_args.connection_child and section.startswith('spd_'):
                             spds.append({
                                 'reqid': cmd_args.reqid,
                                 'local' : cmd_args.local,
    

    Adds the tunnel property to the interface correctly and bringing up the tunnel. Only one manual disconnect/connect was needed after changing the script - disabling and enabling the vti was not enough. (why? - when creating a vti one want's to fire the event to bring up the tunnel?)

    I don't know if the patch is correct for the SPD part though.. but the vti part of src/opnsense/service/templates/OPNsense/IPsec/reqid_events.conf definetely never have the connection_child section

  15. AdSchellevis commented on Apr 23, 2024

    @AdSchellevis
    Member

    @joni1993 what does the reqid_events.conf looks like on your end? it's unlikely there are VTI sections in the configuration when properly configured with static addresses

    {% if vti.enabled == '1' and vti.local|default('') == '' and vti.remote|default('') == '' %}

    Without sections starting with vti_ the ifconfig commands are not being executed.

  16. joni1993 commented on Apr 23, 2024

    @joni1993

    I am not using static addresses - thats the whole point of this issue right? ;)

    "Phase 1" (Connections):
    Local:
    Remote:

    "Local Authentication" (Connections):
    Round: 0
    Authentication: PSK
    id: [A static CNAME pointing to a DynDns domain]

    "Remote Authentication" (Connections):
    Round: 0
    Authentication: PSK
    id:

    "Children" (Connections):
    Mode: Tunnel
    Start action: Trap+Start
    DPD action: Start
    Reqid: 100
    Local: 0.0.0.0/0
    Remote: 0.0.0.0/0

    Pre-Shared-Keys:
    Local Identifier:
    Remote Identifier:
    PSK:
    Type: PSK

    Virtual Tunnel Interface:
    Reqid: 100
    Local address: (because it uses dynamic ips)
    Remote address: (because both need to be empty if one is)
    Tunnel local address: [x.y.z.1]
    Tunnel remote address: [x.y.z.2]

    reqid_events:

    ...
    [vti_6066f6a169734e1089435d68c390ff17]
    reqid = 100
    description = VPN
    uuid = 6066f6a1-6973-4e10-8943-5d68c390ff17
    ...
    
  17. AdSchellevis commented on Apr 23, 2024

    @AdSchellevis
    Member

    @joni1993 you mentioned your setup worked before this change, dynamic addresses weren't supported before 21.1.4 for the new connections.

  18. joni1993 commented on Apr 23, 2024

    @joni1993

    It worked when i was on 21.1.1 + manually applying the patch.

    But i suspect it maybe worked because back then i did just disable the old config and enabled the new config at the same time and when applying some "old state"/IKE/Phase2 got reused for the new config - which made it seem working.

    But yesterday after updating to 24.1.6, it did not work anymore, so i suspect it initially only worked because of some quirks/coincidence/leftovers-states.

  19. AdSchellevis commented on Apr 23, 2024

    @AdSchellevis
    Member

    if the log contains the event being processed in :

    syslog.syslog(syslog.LOG_NOTICE, '[UPDOWN] received %s event for reqid %s' % (cmd_args.action, cmd_args.reqid))

    we can add some additional logging to help local debugging, in that case just open a new ticket to request more logging in this event.

  20. joni1993 commented on Apr 23, 2024

    @joni1993

    Yes it does include the line - but the script probably fails as written in my above comment - because the tunnel property is never applied.

    Traceback (most recent call last):
      File "/usr/local/lib/python3.9/configparser.py", line 789, in get
        value = d[option]
      File "/usr/local/lib/python3.9/collections/__init__.py", line 941, in __getitem__
        return self.__missing__(key)            # support subclasses that define __missing__
      File "/usr/local/lib/python3.9/collections/__init__.py", line 933, in __missing__
        raise KeyError(key)
    KeyError: 'connection_child'
    
    During handling of the above exception, another exception occurred:
    
    Traceback (most recent call last):
      File "/usr/local/opnsense/scripts/ipsec/updown_event.py", line 64, in <module>
        or cnf.get(section, 'connection_child') == cmd_args.connection_child:
      File "/usr/local/lib/python3.9/configparser.py", line 792, in get
        raise NoOptionError(option, section)
    configparser.NoOptionError: No option 'connection_child' in section: 'vti_72691b462a734a82963ade379b5fb4b5'
    
  21. AdSchellevis commented on Apr 24, 2024

    @AdSchellevis
    Member

    @joni1993 ah, got it, missed that part in your initial comment, I'll take a look

  22. AdSchellevis commented on Apr 24, 2024

    @AdSchellevis
    Member

    @joni1993 can you try b0bf317 ?

  23. joni1993 commented on Apr 24, 2024

    @joni1993

    Yes - this works and brings up the tunnel. Interestingly after disconnecting the tunnel and connecting again after tearing down and bringing up the VTI, the static routes for the gateway did not get applied.

  24. AdSchellevis commented on Apr 24, 2024

    @AdSchellevis
    Member

    the event shouldn't try to enforce routing, but if we only change the outer addressing, the inner shouldn't change anyway

  25. joni1993 commented on Apr 24, 2024

    @joni1993

    I agree. Somehow the Gateway Interface magically changed to some arbitrary interface...

    Anyway I'd call this case closed :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

featureAdding new functionalityroadmapMajor roadmap item

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions