VyOS Platform Blog

VyOS 1.4.0-rc1 release candidate

Written by Daniil Baturin | December 22, 2023 5:18:48 AM Z

Hello, community! Exactly two years ago we made the VyOS 1.3.0/Equuleus LTS release. Now, after two years of development, the upcoming 1.4.0/Sagitta release has taken its final shape and we are happy to announce the first release candidate. The 1.4.0-rc1 image is now available for everyone to download and test and will become the new LTS release when real-world testing proves that it's stable and free of unexpected regressions.

If you are using an older VyOS LTS release, please try it out in your staging environments and let us know if it works well for you. That includes both negative and positive findings: if a config fails to load or some feature doesn’t work, we need to know that, but it’s equally important to hear if it works well for you without any regressions or surprises.
If you are a community member, note that we are always happy to offer contributor subscriptions to everyone who tests release candidates and reports the findings, so grab the image and give it a try in your home lab.

If you are new to VyOS release naming, here's a refresher: after 1.2.0, we name our LTS releases after constellations, sorted by  area in square degrees. This release is named after Sagitta — a small constellation of the northern sky. Sagitta means an arrow in Latin, and you will see that in the updated boot screen artwork.

But, of course, the name is the least interesting part, so let's get to the features.

What’s new?

The new firewall

Since the earliest days of Vyatta (the predecessor of VyOS) and until VyOS 1.4, firewall rulesets were assigned to network interfaces and individual rules in a ruleset could not apply to different interfaces. That was a really strange design decision that was apparently made to make the CLI feel more familiar to network admins experienced with Cisco IOS and its look-alikes. There was no technical reason for that, since Linux firewalls never had that limitation and always allowed admins to either apply a rule to all interfaces or specify an interface — it was a limitation of the CLI.

Unfortunately, lifting that limitation wasn't easy because it required a complete redesign of the firewall CLI and a complete rewrite of its configuration scripts. However, we had to rewrite it anyway since we were migrating it from iptables to nftables, so we also used it as a chance to give it the necessary redesign. Now there is no need to assign a ruleset to multiple interfaces, since  rules of global policies (such as firewall ipv4 forward) apply to all interfaces by default. Moreover, it's now possible to make a rule apply to multiple interfaces at once by using interface groups. For example:

vyos@vyos# show firewall 
 group {
     interface-group WAN {
         interface eth1
         interface eth2
     }
 }
 ipv4 {
     forward {
         filter {
             rule 10 {
                 action jump
                 jump-target In
             }
         }
     }
     name In {
         rule 10 {
             action accept
             destination {
                 port 443
             }
             inbound-interface {
                 group WAN
             }
             protocol tcp
         }
     }
 }

We also made a strategic mistake during the  redesign process: initially, we removed the zone-based firewall. Originally, the zone-based firewall was introduced to make it easier to apply rulesets to network interfaces, since it allowed the admin to assign a ruleset only once to every zone pair and assign network interfaces to zones. Zone-based firewall configuration scripts were also a headache for us because they were separate from the rest of the firewall subsystem but still tightly coupled with it.

We assumed that when people could just apply a rule to all interfaces, there wouldn't be any need for zone-based firewall anymore, so we removed it and wrote a migration script that converted old zone-based firewalls to the "normal" firewalls instead. However, we were wrong: many people told us that they liked the mental model and the UI of the zone-based firewall, and that semantically equivalent auto-generated "zone-less" configs were often several times longer.

So we corrected our mistake and implemented a zone-based firewall CLI on top of the new nftables-based system. It works and looks like before, with a minor difference: it's under firewall zone rather than the top-level zone-policy node:

set firewall zone LAN from WAN firewall name WAN-In

We hope that you enjoy the capabilities of the new firewall system (such as support for transparent bridge firewalls, multiple new group types, and more) and that it works well for you. But if anything is wrong, don't hesitate to report a bug.

GraphQL API and PAM authentication

The original HTTPS API that we introduced in VyOS 1.2.x was pretty good for configuration automation, it has its limitations that made it unsuitable for making full-featured management tools, such as web UIs: it did not provide machine-readable operational mode command versions and could only authenticate clients by pre-configured API keys.

Now there is a new API that provides a GraphQL schema, offers a unified approach to configuration and operational mode commands, and, yes, supports authenticating users with their login names and passwords through PAM. Here is now to enable it:

set service https api graphql authentication type token 

Then you can authenticate with the login name and password of any system user, get a JWT token, and use it for queries. Here is a minimal example of a Python script for retrieving the machine-readable equivalent of show version:

#!/usr/bin/env python3

import json
from requests import request

VYOS_URL = 'https://vyos.example.com/graphql'

auth_query = """
  mutation {
    AuthToken (data: {username: "vyos", password: "vyos"}) {
      success
      errors
      data {
        result
      }
    }
  }
"""

version_query = """
  {
    ShowVersion (data: {}) {
      success
      errors
      op_mode_error {
        name
        message
        vyos_code
      }
      data {
        result
      }
    }
  }
"""


res = request('POST', VYOS_URL, verify=False, headers={}, json={'query': auth_query})
token = res.json()["data"]["AuthToken"]["data"]["result"]["token"]

res = request('POST', VYOS_URL, verify=False, headers={'Authorization': f'Bearer {token}'}, json={'query': version_query})

print(json.dumps(res.json(), indent=2))

The output will be like:

$ python3 ./api-client.py 
{
  "data": {
    "ShowVersion": {
      "success": true,
      "errors": null,
      "op_mode_error": null,
      "data": {
        "result": {
          "version": "1.4.0-rc1",
          "built_by": "Sentrium S.L.",
          "built_on": "Thu 21 Dec 2023 19:06 UTC",
          "build_uuid": "2463607a-ddc5-4942-8685-00d078350c68",
          "build_git": "81ec3de04eb291",
          "build_branch": "sagitta",
          "release_train": "sagitta",
          "lts_build": true,
          "build_comment": "",
          "system_arch": "x86_64",
          "system_type": "KVM guest",
          "boot_via": "livecd",
          "hardware_vendor": "innotek GmbH",
          "hardware_model": "VirtualBox",
          "hardware_serial": "0",
          "hardware_uuid": "33d93ca6-9d47-de4a-bb49-85ed5101970c"
        }
      }
    }
  }
}

We will eventually make official bindings for the API to make it even easier to use — at least for Python, and likely for more languages. We expect the new API to make it dramatically simpler to write integrations for any automation and management tools, too.

Live configuration rollback

The configuration rollback command that we inherited from Vyatta Core had a serious disadvantage: it would reboot the system before loading the older configuration version. That made it a command of last resort rather than a convenient way to revert changes that did not work as expected.

There were two main reasons for that. First, the old approach to default values inadvertently made it impossible to tell if an option was deleted or manually set to its built-in default value. Second, there was no way to construct a "reverse diff" between two config files. Now all command definitions are converted to the new XML-based format that does not have that issue, and we have a library for calculating config diffs, so we introduced an experimental live rollback command.

For now, the original rollback is still there, and the new implementation is called rollback-soft. Please try it out and let us know if it works well for you! Once it's fully stable, we will likely rename it to just "rollback" in the 1.5.0 release and rename the original command to "rollback-hard", or remove the old command and add a command to set the default config to a specific revision instead.

Load balancing with LVS

Now there is a CLI for configuring load balancing with LVS (Linux Virtual Server).

vyos@vyos# show high-availability virtual-server 
 virtual-server Test {
     address 203.0.113.1
     algorithm least-connection
     forward-method nat
     port 443
     protocol tcp
     real-server 10.0.0.1 {
         port 443
     }
real-server 10.0.0.2 {
port 443
}
}

OpenVPN site-to-site with TLS and peer fingerprint verification

For a long time, OpenVPN site-to-site mode with pre-shared keys served as a lightweight alternative to full-blown client-server setups with PKI and to VPN protocols that are less tolerant to weird networks, since it can work over TCP (alas, mostly tolerant to unintentionally weird networks — OpenVPN was proved vulnerable to blocking with DPI and there's no built-in obfuscation solution yet, but that's another story).

However, the pre-shared key support is due to be removed in upcoming OpenVPN versions. It's still supported in VyOS 1.4, but we are almost certain that we will be unable to support it in VyOS 1.5, so we encourage everyone to migrate to  site-to-site with self-signed certificates and peer fingerprint verification — a mode that is a lot more secure but only slightly more cumbersome to set up.

In short, you can generate a self-signed certificate — we recommend using elliptic curve ones:

vyos@left# run generate pki certificate self-signed install openvpn-local
Enter private key type: [rsa, dsa, ec] (Default: rsa) ec
Enter private key bits: (Default: 256)
Enter country code: (Default: GB)
Enter state: (Default: Some-State)
Enter locality: (Default: Some-City)
Enter organization name: (Default: VyOS)
Enter common name: (Default: vyos.io)
Do you want to configure Subject Alternative Names? [y/N]
Enter how many days certificate will be valid: (Default: 365)
Enter certificate type: (client, server) (Default: server)
Note: If you plan to use the generated key on this router, do not encrypt the private key.
Do you want to encrypt the private key with a passphrase? [y/N]
2 value(s) installed. Use "compare" to see the pending changes, and "commit" to apply.
[edit]

vyos@left# compare
[pki]
+ certificate openvpn-local {
+     certificate "MIICJTCCAcugAwIBAgIUMXLfRNJ5iOjk/    uAZqUe4phW8MdgwCgYIKoZIzj0EAwIwVzELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVnlPUzEQMA4GA1UEAwwHdnlvcy5pbzAeFw0yMzA5MDcyMTQzMTNaFw0yNDA5MDYyMTQzMTNaMFcxCzAJBgNVBAYTAkdCMRMwEQYDVQQIDApTb21lLVN0YXRlMRIwEAYDVQQHDAlTb21lLUNpdHkxDTALBgNVBAoMBFZ5T1MxEDAOBgNVBAMMB3Z5b3MuaW8wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASp7D0vE3SKSAWAzr/lw9Eq9Q89r247AJR6ec/GT26AIcVA1bsongV1YaWvRwzTPC/yi5pkzV/PcT/WU7JQIyMWo3UwczAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDATAdBgNVHQ4EFgQUBrAxRdFppdG/UBRdo7qNyHutaTQwHwYDVR0jBBgwFoAUBrAxRdFppdG/UBRdo7qNyHutaTQwCgYIKoZIzj0EAwIDSAAwRQIhAI2+8C92z9wTcTWkQ/goRxs10EBC+h78O+vgo9k97z5iAiBSeqfaVr5taQTS31+McGTAK3cYWNTg0DlOBI8aKO2oRg=="
+     private {
+         key "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgtOeEb0dMb5P/2Exi09WWvk6Cvz0oOBoDuP68ZimS2LShRANCAASp7D0vE3SKSAWAzr/lw9Eq9Q89r247AJR6ec/GT26AIcVA1bsongV1YaWvRwzTPC/yi5pkzV/PcT/WU7JQIyMW"
+     }
+ }

[edit]

vyos@left# commit

Then you can view its fingerprint using a new operational mode command:

vyos@left# run show pki certificate openvpn-local fingerprint sha256
5C:B8:09:64:8B:59:51:DC:F4:DF:2C:12:5C:B7:03:D1:68:94:D7:5B:62:C2:E1:83:79:F1:F0:68:B2:81:26:79

Finally, you can specify the fingerprint on the remote router:

vyos@right# set interfaces openvpn vtun1 tls peer-fingerprint '5C:B8:09:64:8B:59:51:DC:F4:DF:2C:12:5C:B7:03:D1:68:94:D7:5B:62:C2:E1:83:79:F1:F0:68:B2:81:26:79'

Then generate a certificate on the remote router and repeat the procedure on the local one.

Another thing to note: the DH file option is no longer required for OpenVPN — if it's not set, it defaults to the elliptic curve Diffie-Hellman algorithm, so a self-signed certificate for each side is literally all you need for the new site-to-site setup to work, and client-server setups can now use fewer options as well.

What else?

In the final 1.4.0 release post, we will include something about every new feature. Right now it's too early to give an exhaustive list because there still may be backports from the current branch. But here is a list of features you may want to test:

  • Support for the Babel routing protocol (set protocols babel)
  • HTTPS load balancing with HAProxy (set load-balancing reverse-proxy)
  • Built-in Zabbix agent (set service monitoring zabbix-agent)
  • SSTP client (set interfaces sstpc)

What's next?

Testing! A lot of testing... but we hope that no serious issues turn up and we can release the final 1.4.0 images early in the new year. Then the 1.4.0 LTS branch will be supported for at least three years. Since it's based on Debian 12/Bookworm, that gives us almost four years of security updates even before we turn to the extended LTS from Freexian. And there are big plans for the next 1.5 release, of course — stay tuned for development updates!