I did not want to “pollute” my setup which is why I tried to do it declaratively from the start (though it appears to forget the entire namespace configuration on boot anyway). And from all the searching I have done, it appears that the way to create a network namespace declaratively is through a systemd service.
The LLM-generated answer on how to do that is based on this page. The page uses the 2-stage systemd service method and adding multiple ip commands into the ExecStart.
The first service to create the namespace works fine, as long as I make sure it is actually started. The second one to create the virtual interfaces works as well.
I concluded the next step was to follow the fun with network namespaces link you posted earlier, in which the next step is to assign one virtual interface to the namespace, then set up static IP’s and a NAT route out of it. It would not be that complicated to translate the ifState examples into ip commands if needed. I figured once I knew how to make it execute multiple ip commands in sequence (as in the first link in this post) I could figure out the basic networking and the only step remaining would be to configure the firewall.
And then figure out if Plasma allows me to create a shortcut for an application to start in the namespace, but that’s a luxury problem.
So yes, I did add the second line to the ExecStart. My Linux skills are rather specialized. Some things I am really good at, others really not. This is still new to me.
EDIT:
Been doing some reading to figure out what that script option ends up doing. Does it create a shell script which it then executes in the service? In that case, can it even be used together with ExecStart?
Also, by reading the man-page it appears that when using oneshot I can not do multi-lines but I can do multiple single lines. And using a combination of ExecStart and ExecStartPre could theoretically work. Though using the NixOS-supplied script would be more NixOS conform, so I could try both.
man page excerpt
ExecStart=
Commands that are executed when this service is started.
Unless Type= is oneshot, exactly one command must be given. When Type=oneshot is used, this setting may be used multiple times to define multiple commands to execute. If the empty string is assigned to this option, the list of commands to start is reset, prior assignments of this option will have no effect. If no ExecStart= is specified, then the service must have RemainAfterExit=yes and at least one ExecStop= line set. (Services lacking both ExecStart= and ExecStop= are not valid.)
If more than one command is configured, the commands are invoked sequentially in the order they appear in the unit file. If one of the commands fails (and is not prefixed with “-”), other lines are not executed, and the unit is considered failed.
Unless Type=forking is set, the process started via this command line will be considered the main process of the daemon.
ExecStartPre=, ExecStartPost=
Additional commands that are executed before or after the command in ExecStart=, respectively. Syntax is the same as for ExecStart=. Multiple command lines are allowed, regardless of the service type (i.e. Type=), and the commands are executed one after the other, serially.
If any of those commands (not prefixed with “-”) fail, the rest are not executed and the unit is considered failed.
ExecStart= commands are only run after all ExecStartPre= commands that were not prefixed with a “-” exit successfully.
ExecStartPost= commands are only run after the commands specified in ExecStart= have been invoked successfully, as determined by Type= (i.e. the process has been started for Type=simple or Type=idle, the last ExecStart= process exited successfully for Type=oneshot, the initial process exited successfully for Type=forking, “READY=1” is sent for Type=notify/Type=notify-reload, or the BusName= has been taken for Type=dbus).
Note that ExecStartPre= may not be used to start long-running processes. All processes forked off by processes invoked via ExecStartPre= will be killed before the next service process is run.
Note that if any of the commands specified in ExecStartPre=, ExecStart=, or ExecStartPost= fail (and are not prefixed with “-”, see above) or time out before the service is fully up, execution continues with commands specified in ExecStopPost=, the commands in ExecStop= are skipped.
Note that the execution of ExecStartPost= is taken into account for the purpose of Before=/After= ordering constraints.
By the way, it might not even be possible to execute ip commands in the namespace from outside the namespace, so I may end up having to do it by hand after all. At least, I have so far not found any way (except ifState) to configure the inside of a namespace.
In which case, 2 network interfaces and assigning 1 exclusively to the namespace would be the only solution which could be automated.