To me the best practice is to NEVER mix production and management traffic.
That is if you use eth0 for mgmt then ONLY use management here as in remote access + logging and ntp (so the VyOS box have correct time) etc.
Other than that Im using vrfs (virtual routing and forwarding) to have separate routingtables between MGMT and PROD.
In VyOS there is also netns (network namespaces) that can be configured but they currently dont seem to have any usage yet.
Compared to others (lets say Arista) when you create a vrf there what happens in the backend is that they will create a netns for full isolation of the interfaces along with a vrf as in dedicated routingtable.
So what VyOS currently supports is whats often refered to as “vrf-lite” (just separate routingtables and not fully separated interfaces).
Here is some good reading on this topic:
And here is a config-example:
set interfaces ethernet eth0 address '10.99.0.100/24'
set interfaces ethernet eth0 hw-id '<REMOVED>'
set interfaces ethernet eth0 ip arp-cache-timeout '240'
set interfaces ethernet eth0 ip source-validation 'strict'
set interfaces ethernet eth0 ipv6 disable-forwarding
set interfaces ethernet eth0 ipv6 source-validation 'strict'
set interfaces ethernet eth0 offload gro
set interfaces ethernet eth0 offload gso
set interfaces ethernet eth0 offload rfs
set interfaces ethernet eth0 offload rps
set interfaces ethernet eth0 offload sg
set interfaces ethernet eth0 offload tso
set interfaces ethernet eth0 ring-buffer rx '1024'
set interfaces ethernet eth0 ring-buffer tx '256'
set interfaces ethernet eth0 vrf 'MGMT'
set interfaces ethernet eth1 address '192.168.1.20/24'
set interfaces ethernet eth1 hw-id '<REMOVED>'
set interfaces ethernet eth1 ip arp-cache-timeout '240'
set interfaces ethernet eth1 ip source-validation 'strict'
set interfaces ethernet eth1 ipv6 disable-forwarding
set interfaces ethernet eth1 ipv6 source-validation 'strict'
set interfaces ethernet eth1 offload gro
set interfaces ethernet eth1 offload gso
set interfaces ethernet eth1 offload rfs
set interfaces ethernet eth1 offload rps
set interfaces ethernet eth1 offload sg
set interfaces ethernet eth1 offload tso
set interfaces ethernet eth1 ring-buffer rx '1024'
set interfaces ethernet eth1 ring-buffer tx '256'
set interfaces ethernet eth1 vrf 'PROD'
set interfaces loopback lo ip source-validation 'strict'
set netns name MGMT
set netns name PROD
set vrf bind-to-all
set vrf name MGMT ipv6 disable-forwarding
set vrf name MGMT protocols static route 0.0.0.0/0 next-hop 10.99.0.254 interface 'eth0'
set vrf name MGMT table '100'
set vrf name MGMT vni '10000100'
set vrf name PROD ipv6 disable-forwarding
set vrf name PROD protocols static route 0.0.0.0/0 next-hop 192.168.1.254 interface 'eth1'
set vrf name PROD table '101'
set vrf name PROD vni '10000101'
In above case I have disabled IPv6 and as you can see defined which interface belongs to which vrf.
And then each vrf can have its own default routes and whatelse.
Note that any service not setup/configured for vrf will use the default vrf “local” so with above setup these services will not function any longer.
You can see how the kernel currently see things (routingwise) by doing something like:
cat /proc/1/task/1/net/fib_trie
To fix this you need to define vrf for each service aswell something like this:
set service ntp server 10.99.0.10 prefer
set service ntp vrf 'MGMT'
set service ssh access-control allow user 'vyos'
set service ssh cipher 'aes256-gcm@openssh.com'
set service ssh disable-host-validation
set service ssh dynamic-protection allow-from '10.99.0.0/24'
set service ssh dynamic-protection block-time '60'
set service ssh dynamic-protection detect-time '3600'
set service ssh dynamic-protection threshold '10'
set service ssh listen-address '10.99.0.100'
set service ssh mac 'hmac-sha2-512'
set service ssh rekey data '1024'
set service ssh rekey time '60'
set service ssh vrf 'MGMT'
In above example the local NTP service will sync VyOS against 10.99.0.10 located on vrf MGMT.
And similar for SSH where I define both listenaddress and which vrf to be used along with an “allow-from” to filter who will be able to handshake with the service.
Also dont forget if possible to also setup proper filtering in the firewall section (aka layered security). But I leave that exercise to the reader 