How to create an OpenVPN tun interface with a stable name

I have an OpenVPN client that runs as a service inside a jail and creates a TUN interface dynamically on connection success.
Because of firewall and routing configuration I want the newly created interface to have a stable name that would not change when I restart OpenVPN or the jail.

According to documentation, I can set a fixed name inside openvpn.conf:
Code:
dev tun5
This would result in OpenVPN creating the interface with name "tun5" each time.

The problem is, while this works the first time and I can also restart the openvpn service without any problems, once I destroy the jail and start it again, OpenVPN fails to start with the error message: Cannot open TUN/TAP dev /dev/tun5: No such file or directory (errno=2)
Creating the interface manually is also not possible: ifconfig: interface tun5 already exists, although it is not listed by ifconfig neither inside nor outside the jail.

I can see the previous jails as dying via jls -ad.

Apparently when a jail is destroyed and becomes dying, it somehow blocks the TUN interface names which were created inside it, so the next instance of the jail cannot use tun5 anymore.

What could be done in this case?
1. The best would be if I could enforce a specified fixed name for OpenVPN to use and create dynamically, even when previous jail instances are still dying.
2. If not possible, how could I couple the firewall and routing configuration to a dynamically created interface name? This would not be a preferred solution for me because I specify the interface name in other configurations even before the jail is generated and I'd like to have a stable way of referring to the interface at jail generation time. But if 1 is not feasible, I could try and implement some means of addressing a dynamically changing name.

Update: I think it has something to do with cleaning up the jails. I had success when I destroyed the interface explicitly before destroying the jail. Then when next started, the same interface can be created successfully. I am unable to do this via exec.prestop though, so manually cleaning up is not a sustainable option for me. Any suggestions?
 
Update: I think it has something to do with cleaning up the jails. I had success when I destroyed the interface explicitly before destroying the jail. Then when next started, the same interface can be created successfully. I am unable to do this via exec.prestop though, so manually cleaning up is not a sustainable option for me. Any suggestions?

I just had a related challenge in a server scenario, and I drew the same conclusion as you did. To prevent this "blockage" in VNET jails, the interfaces must be destroyed (in the case of tun's) or removed from the jail BEFORE the jail is stopped.

For my application, I was able to do it via exec.prestop by just leveraging the jexec command like this:

Code:
vnet;
vnet.interface = "ixv$i";
...
exec.prestop = "/usr/sbin/jexec $name /usr/sbin/service openvpn stop";
exec.prestop += "/usr/sbin/jexec $name /sbin/ifconfig tun$i destroy";
exec.prestop += "/sbin/ifconfig ${vnet.interface} -vnet $name";
...
lan { $i = "1"; }

In my openvpn.conf file, I do like you suggest and put in:
Code:
dev tun5
(In my case, I had six jails and each having its own tun interface)

This way, I could also configure PF to do filtering on just this tun interface.

As you observed, by destroying the tun interface and removing, in my case, a physical interface (specifically a virtual function) from the VNET jail before stopping it, I can re-create the same tun interface and use the same physical interface next time I start the jail.

The trick for me really was realizing that I could use a a variable to hold a single digit ID to be used for the interfaces, and that keeps it consistent. :)
 
I just had a related challenge in a server scenario, and I drew the same conclusion as you did. To prevent this "blockage" in VNET jails, the interfaces must be destroyed (in the case of tun's) or removed from the jail BEFORE the jail is stopped.

For my application, I was able to do it via exec.prestop by just leveraging the jexec command like this:

Code:
vnet;
vnet.interface = "ixv$i";
...
exec.prestop = "/usr/sbin/jexec $name /usr/sbin/service openvpn stop";
exec.prestop += "/usr/sbin/jexec $name /sbin/ifconfig tun$i destroy";
exec.prestop += "/sbin/ifconfig ${vnet.interface} -vnet $name";
...
lan { $i = "1"; }

In my openvpn.conf file, I do like you suggest and put in:
Code:
dev tun5
(In my case, I had six jails and each having its own tun interface)

This way, I could also configure PF to do filtering on just this tun interface.

As you observed, by destroying the tun interface and removing, in my case, a physical interface (specifically a virtual function) from the VNET jail before stopping it, I can re-create the same tun interface and use the same physical interface next time I start the jail.

The trick for me really was realizing that I could use a a variable to hold a single digit ID to be used for the interfaces, and that keeps it consistent. :)
Did you get pf to work also in the vnet jail with openvpn? I can get everything to work, I can start and stop the jail no issues but as soon as I start using pf in the jail it cannot stop without hanging. Even after I disable pf in the jail and reboot the system it still hangs. pflog0 interface is still inside the jail even when pf is disabled in the jail. Did you take any steps to stop pf and pflog before the jail stops?
 
get everything to work, I can start and stop the jail no issues but as soon as I start using pf in the jail it cannot stop without hanging. Even

Yes, I did. It all "just worked" using the configuration step I described in my earlier post. Which version of FreeBSD are you using? The one I was using when I wrote the earlier post was 14.2.
 
Yes, I did. It all "just worked" using the configuration step I described in my earlier post. Which version of FreeBSD are you using? The one I was using when I wrote the earlier post was 14.2.
I think I figured this out finally. There were 3 major issues I was facing:

1. OpenVPN was starting too early in the rc.order. It would oftentimes fail to create routes on startup. I created another rc.d script that openvpn requires before it will start. In that script it requires networking and has a small delay before it completes.

2. OpenVPN wasn't stopping at the correct time, so I created I stop script that I call on jail stop before shutting the jail down. This maybe is not required after the RC.order was corrected above. I will do some more testing.

3. The interface disappears to the host once the jail is shutdown. I created a script to that runs on jail poststop to detach and attach the interface if it is not visible in the host.

I will do some more testing and finally write a post on how I got it working reliably. My goal here is that the jail should be able to be restarted normally like any other jail and openvpn should connect to the VPN reliably using the same tun0 interface everytime.
 
I think I figured this out finally. There were 3 major issues I was facing:

1. OpenVPN was starting too early in the rc.order. It would oftentimes fail to create routes on startup. I created another rc.d script that openvpn requires before it will start. In that script it requires networking and has a small delay before it completes.

2. OpenVPN wasn't stopping at the correct time, so I created I stop script that I call on jail stop before shutting the jail down. This maybe is not required after the RC.order was corrected above. I will do some more testing.

3. The interface disappears to the host once the jail is shutdown. I created a script to that runs on jail poststop to detach and attach the interface if it is not visible in the host.

I will do some more testing and finally write a post on how I got it working reliably. My goal here is that the jail should be able to be restarted normally like any other jail and openvpn should connect to the VPN reliably using the same tun0 interface everytime.

Ah yes, now I understand what your issue was. Please see my earlier post, where I detail how I use the exec.prestop parameter in jail.conf to stop the OpenVPN service and remove the network interface from the jail before the jail is stopped.
 
Back
Top