| Age | Commit message (Collapse) | Author |
|
Address the round-2 adversarial-review finding: warnings.catch_warnings()
mutates the process-global filter list for the duration of the block (still
the case on Python 3.13), so scoping suppression inside the library was not
actually isolated.
- http_api_client no longer touches warning filters at all. The
_suppress_insecure_warning helper is removed and post() calls
session.post directly again. The unused warnings, contextmanager and
urllib3 imports are dropped. The class docstring now states that callers
disabling verification own InsecureRequestWarning handling. The OSError
wrapping and verify_tls validation are unchanged.
- config_sync ignores InsecureRequestWarning in its __main__ block only,
before opmode.run(). That one-shot op-mode process deliberately connects
without verification (verify_tls=False) until T9396. Importing the module
adds no filter.
🤖 Generated by [robots](https://vyos.io)
|
|
Address the adversarial-review findings on the TLS verification change:
- Finding 1: validate ApiClientConfig.verify_tls in __post_init__. Only a
bool or a non-empty CA bundle path is accepted; anything else (including
"", None and ints) raises ValueError. requests treats "" as falsy and
would silently disable verification. Strings are never coerced: "false"
is a path, so callers must pass a real bool.
- Finding 2: stop calling urllib3.disable_warnings() process-wide from
ApiClient.__init__. InsecureRequestWarning is now ignored only around the
request itself, via warnings.catch_warnings(), and only when verify_tls
is False. post() is the only request path in the client.
- Finding 3: wrap OSError (raised by requests' cert_verify for a missing
CA bundle path) in ApiTransportError. The handler sits after the
RequestException handlers, since those subclass OSError.
- Finding 4: config-sync hard-codes verify_tls = False instead of reading
an unmapped 'verify_tls' key from the runtime config, whose leaf values
are strings. Mapping a CLI option to a bool or CA path is T9396.
🤖 Generated by [robots](https://vyos.io)
|
|
ApiClientConfig.verify_tls defaulted to False and ApiClient unconditionally
silenced urllib3's InsecureRequestWarning, so TLS peer verification was off by
default. The sole consumer, src/op_mode/config_sync.py, builds the client
without passing verify_tls and therefore connected to a *remote* secondary VyOS
node over HTTPS without verifying the certificate -- a MITM exposure on the
channel that carries the API key and the full configuration.
- ApiClientConfig.verify_tls now defaults to True and accepts bool | str;
a string is forwarded to requests as a CA bundle path (verify=<path>).
- InsecureRequestWarning is suppressed only when verification is explicitly
disabled (verify_tls is False), not when a CA path or True is set.
- config_sync reads an optional verify_tls from the secondary runtime settings,
defaulting to False to preserve current behaviour for self-signed secondaries.
The insecurity is now explicit at the call site instead of hidden in the
library default. Exposing a CA certificate (PKI reference) and peer
fingerprint pinning under service config-sync secondary is follow-up T9396.
🤖 Generated by [robots](https://vyos.io)
|
|
verify() looked up each prefix-list rule in a list of the rules seen
so far, which is quadratic in the size of the list and runs on every
policy commit. verify() alone, run from the source on a workstation,
took 41.8 s for a 40,000-rule list; on a 1.5.1 VM the check cost 0.08 s
at 2,000 rules.
Keep a set of the rules' sorted items instead. Every value under a
prefix-list rule is a single leaf, so the key is hashable, and two keys
are equal exactly when the rule dicts are: the same rules are flagged,
with the same message. The workstation benchmark took 0.11 s.
|
|
T9364: openvpn: default the MTU to 1420 when DCO is enabled
|
|
wwan: T7487: identify modem by port ownership, not interface number
|
|
The ovpn Kernel module does no MSS clamping, so with a 1500 bytes MTU
full sized packets get fragmented on the underlay.
Add an "mtu" node, rendered as "tun-mtu". With "offload dco" it
defaults to 1420, unless a raw tun-mtu/link-mtu/udp-mtu option is
present. Reject "mtu" together with such an option, below 100, and
below 1280 when IPv6 runs inside the tunnel.
|
|
http-api: T8989: Hotfix to enforce audience validation if OIDC is configured
|
|
"failure-count" counts missed intervals, so the timeout it describes is
interval * failure-count. Server mode renders and verifies it that way;
site-to-site passed the bare count to "ping-restart", where it lands as
seconds - 60s instead of 600s with the defaults.
Render the product. That puts a timeout past the 86400 "ping-restart"
accepts within reach, so verify() rejects it and the 5-to-6 migration
clamps configurations already beyond it.
This changes behaviour on upgrade: site-to-site tunnels now tolerate a
longer silence, 600 seconds instead of 60 with the defaults.
|
|
openvpn: T8999: improve shared-secret reference errors
|
|
modem_index() in python/vyos/utils/wwan.py derived the ModemManager modem index straight from the interface name (wwan0 -> 0). The kernel-assigned WWAN interface number and ModemManager's own modem index are two independently enumerated values and are not guaranteed to match - on a box with more than one modem, they can enumerate in different orders, so modem index 0 does not necessarily own wwan0. Every function in this module funnels through modem_index() (start_modem_manager, modem_disconnect, modem_connect, and both admin-disconnect marker helpers), and is_wwan_connected() in vyos.utils.network had its own separate copy of the same assumption - so a single reversed enumeration order silently commands the wrong physical modem for every one of them.
modem_index() now lists every modem ModemManager currently knows about and returns whichever one actually has ifname in its own port list, instead of assuming the correspondence. A modem that has not shown up on the bus yet looks the same as one that will never exist, so this waits up to the same modem_wait_timeout budget start_modem_manager() already gives a modem to become dialable, then returns None if nothing claimed the interface. is_wwan_connected() now calls the same resolver instead of keeping its own copy of the old assumption.
None is a real, expected return now, not just a theoretical one - the three callers that previously assumed modem_index() always produced *a* string (right or wrong) are updated: start_modem_manager() bails out early (matching the tolerance its own caller's w.exists() check already has for hardware that isn't detected yet), modem_disconnect() and modem_connect() treat it as nothing to disconnect / a failed dial, matching each function's own existing return contract. show_wwan.py's modem_info() raises a clear, specific error instead, which its own top-level exception handler already reports cleanly. set_admin_disconnected()/clear_admin_disconnected() only ever used modem_index() to validate the interface name looks like a WWAN interface in the first place, so they're unaffected.
|
|
wwan: T9350: possible dead interface after boot, disconnect does not stick
|
|
|
|
op-mode: T9354: replace "reset connection" with "reconnect interface"
|
|
pim: T9362: prevent killing an already dead/non-existing PID
|
|
|
|
T8977: Fix regression with is_addr_assigned and is_listen_port_bind_service
|
|
The daemon is not guaranteed to be running when PIM is removed - it may have
never started, or already died.
|
|
create_private_key() took one "size" for every key type, but the number
means entirely different things per type: for RSA and DSA it is a modulus
size, for EC it selects a curve. CodeQL is not path sensitive across the
key_type comparison, so it follows the 256 an EC caller passes into the
RSA and DSA branches that caller can never reach, and reports pki.py as
generating 256 bit RSA and DSA keys.
Build elliptic curve keys in create_ec_private_key() instead and route the
op mode generator and the OpenVPN smoketest there, so no curve size ever
enters the function that also builds RSA and DSA keys.
create_private_key() now refuses a key type it does not build rather than
returning None, so a caller cannot drift back into the old shape
unnoticed.
|
|
ipsec: T9303: use ipsec-acceleration for VPP algo check
|
|
firewall: T9205: fix ruff findings in conf_mode/op_mode firewall.py
|
|
cli: T9077: insert literal '?' inside quoted values instead of triggering help
|
|
Commit a9e9c4acfa ("pki: openvpn: T3642: Migrate OpenVPN to PKI and refactor")
was the last caller of verify_diffie_hellman_length() despite the build time
testcases.
Unused code is dead code, drop it.
|
|
The Diffie-Hellman length check stringified the requested minimum keysize
into a variable it never used, then converted the original argument again
for the comparison. A non-numeric value therefore escaped as an uncaught
ValueError from the comparison rather than being reported where it was
passed in.
The argument is now typed and validated up front: a non-integer raises
TypeError, booleans included, and a non-positive integer raises
ValueError. Both indicate a logic error in the calling script and should
fail loudly instead of silently reporting an undersized key. The only
callers today are the unit tests, so the contract is free to change.
A file that exists but holds no readable parameters is user input rather
than a logic error, and now fails the check instead of propagating the
error from openssl.
The bit-count pattern becomes a raw string, and the tests pass integer
keysizes and cover the new paths.
|
|
Whether an interface belongs to a VRF was only established when a VRF was
named explicitly. A lookup for the default VRF - what most callers ask for -
thus reported an address living in an arbitrary VRF, or on a VRF device
itself, as locally assigned, and accepted a configuration the daemon cannot
bind later on. Naming the VRF device remains the way to ask for the
addresses it carries.
Probing the next address family after a failed bind was unreachable, as the
argument is validated as a literal beforehand and then resolves to exactly
one family. The early return is restored.
An interface address carries host bits and must stay valid, but the range
variants classify untrusted input handed over to nftables, which only
accepts a prefix in canonical form. Those reject host bits again.
All of it is covered by tests.
|
|
A listen-address had to be present on an interface at commit time, which
refused a perfectly valid configuration: such an address is commonly
supplied by a failover mechanism only after the commit, and the kernel
permits binding it as soon as the non-local bind sysctl is set. That is
accepted now.
Co-authored-by: Christian Breunig <christian@breunig.cc>
|
|
Whether an address may be bound is not the same question as whether it is
currently configured somewhere: the kernel permits binding a foreign address
once the non-local bind sysctl is set, which is how a failover address is
commonly handed over after the commit. Callers can now opt into that, the
default is unchanged.
A lookup no longer skips the VRF device itself, which carries the addresses
of its own L3 domain, and an interface name can be retrieved for an address
without the caller asking a yes/no question in the first place.
Address and range validation is split into separate checks, so a caller no
longer has to accept ranges where only an address is meaningful. The two
duplicated version checks are dropped in favour of the existing ones.
Co-authored-by: Christian Breunig <christian@breunig.cc>
|
|
"connect interface" and "disconnect interface" cannot be combined to restart a
dialer interface when the CLI session runs over that very interface, yet the
command which does cover it was filed under an unrelated top-level node and
was hard to find.
It is now "reconnect interface <ifname>", mirroring the existing connect and
disconnect commands. "reset connection" is removed, no alias is kept.
The removed command was reachable through the REST API as a "reset" path. A
dedicated "/reconnect" endpoint takes its place, so the operation stays
available to API consumers.
|
|
A reconnect is usually issued via the very interface being reconnected. Once
the link drops, the calling session dies and the reconnect was killed by
SIGHUP - or by SIGPIPE on the next write to the dead terminal - before the
interface could be dialed again, leaving the system unreachable.
The reconnect now runs in a transient systemd unit and survives the loss of
the session. Progress is logged to the journal.
PPPoE and SSTP restart the ppp service in a single operation instead of
stopping and starting it, so there is no window in which an interrupted
command leaves the link down. WWAN still disconnects the modem before dialing
again.
|
|
The f-string-without-placeholder cleanup on these 3 raise ConfigError()
calls forced darker/black to wrap them onto 3 lines each, since the
lines were already over ruff.toml's 88-char limit before this PR. Per
review feedback, that reformatting adds more backport friction than it
resolves, so leave these 3 pre-existing long lines exactly as they were
(f-string prefix included). They're untouched, not part of this diff,
so CI's diff-scoped ruff/darker checks don't flag them either.
|
|
cmdl() decodes subprocess output as utf-8 and can raise
UnicodeDecodeError (not an OSError subclass) if that fails, in
addition to OSError on command failure. Catch both, closing a gap
left by the exact-exception narrowing in the previous commit.
|
|
Address copilot-pull-request-reviewer feedback on PR #5398: cmdl()
only raises OSError and json.loads() only raises json.JSONDecodeError
here, so catching bare Exception hid unrelated bugs. Narrowed all four
call sites accordingly (three flagged, one identical pattern found by
inspection).
|
|
Bare except:, extraneous f-string prefixes on ConfigError()/text calls
without placeholders, and 3 unused test locals in test_firewall.py.
No behavior change.
Split out of PR #5374 (T9160) at sever-sever's request: that PR bundled
this cleanup with the apply-path feature because it happened to touch
the same files, but it isn't related to apply-path itself.
|
|
vpp: T9146: Enable DPDK no-multi-seg when a full frame fits the buffer
|
|
T9274: Add more telegraf configuration
|
|
A modem is on the bus while still probing, so a dial could start too early and
leave the interface without a session until something re-dialled it.
|
|
t-shiro/fix/T9345-vrf-address-family-config-validation
bgp: T9345: restore named-VRF AFI validation
|
|
vxlan: T9319: add VXLAN-GBP support
|
|
Added the ability to make several agent values configurable instead of hardcoded.
|
|
frr: T9344: remove dead code path when vyos-configd is not running
|
|
ipsec: T9320: Add deprecation warning for IKEv1 key-exchange
|
|
show_wwan.py drove qmicli against /dev/cdc-wdmN, which assumes the modem
exposes a QMI control port. An MBIM modem does not, so every subcommand of
"show interfaces wwan wwanN" failed on one - after waiting out a five second
transaction timeout, twice over for "signal":
$ show interfaces wwan wwan0 signal
Command not supported by Modem (10.6s)
$ qmicli --device=/dev/cdc-wdm0 --device-open-proxy --nas-get-signal-info
error: couldn't create client for the 'nas' service: CID allocation failed
in the CTL client: Transaction timed out
ports: cdc-wdm0 (mbim), ttyUSB0 (at), wwan0 (net)
ModemManager has already probed the modem and speaks whichever protocol it
exposes - QMI, MBIM or plain AT. It is a hard dependency of "interfaces wwan"
anyway, as conf_mode cannot dial without it, and it answers from its own cache
over D-Bus without touching the device:
$ show interfaces wwan wwan0 signal
Access technology: lte (0.2s)
Signal quality: 58%
Operator: Telekom.de
Registration: home
Packet service: attached
State: connected
|
|
Two things kept an operator disconnect from taking effect.
vyos-netlinkd re-dials every configured WWAN interface that has lost its
session. It cannot tell a session lost to RF signal loss from one that was
taken down on purpose, so the disconnect was undone on the next reconcile
pass, at most 30 seconds later:
$ disconnect interface wwan0
08:53:33 t+10s state=registered
08:53:43 t+20s state=registered
08:53:53 t+30s state=connected <- re-dialled
08:54:03 t+40s state=connected
vyos-netlinkd: WWAN interface wwan0 is not connected, re-dialling...
vyos-netlinkd: Re-dialled WWAN interface wwan0
Record the intent instead of inferring it from the session state. op-mode
"disconnect" drops a marker in /run/vyos-wwan, which _reconcile_wwan() honours
and "connect" removes again. conf_mode removes it too, so a commit keeps
re-asserting the configured state - the marker only survives for as long as
nobody says otherwise, and never across a reboot.
|
|
/etc/cron.d/vyos-wwan re-ran the conf_mode script every 5 minutes via
op-mode "connect interface wwanN" to re-establish a session lost to RF
signal loss. Re-entering conf_mode from cron is unsafe: the cron job knows
nothing about the boot commit or about a commit in flight, so when a tick
lands before the running config is populated, get_config() returns nothing
but {'ifname', 'deleted'} and apply() takes the teardown branch instead of
dialling. It then stops the DHCP clients, stops ModemManager and unlinks its
own cron file, leaving wwan0 down with no way to recover until the next
commit:
CRON[1869]: (root) CMD (/usr/libexec/vyos/vyos-check-wwan.py)
systemd[1]: Stopping DHCP client on wwan0...
systemd[1]: Stopping Modem Manager...
This is timing dependent - it only bites when a */5 tick coincides with
boot - which is why it presents as an intermittently dead WWAN interface.
Move the re-dial into vyos-netlinkd, which already tracks these interfaces
for DHCP and already gates all of its work behind boot_configuration_complete()
and commit_in_progress2(). A re-dial can therefore no longer land on a
half-applied configuration. It also dials ModemManager directly rather than
re-entering conf_mode, so the teardown branch is no longer reachable from
the recovery path at all.
|
|
Harden the quote-aware '?' handler: quote readline buffer expansions and
write the DSR query to /dev/tty so help still works when stdout is redirected.
|
|
The '?' key is a raw readline binding (possible-completions), so a
literal question mark can never be typed into a value: readline
intercepts the keystroke before bash parsing begins and quoting cannot
protect it. Values like remote-group URLs with query strings get
silently corrupted ('.../blacklist?key=x' is stored as
'.../blacklistkey=x') with no error at any stage.
Rebind '?' to a bind -x handler that scans the line left of the cursor
for an open single/double quote or a pending backslash escape. Inside a
quote or escape the key self-inserts; otherwise it triggers the
standard help via a DSR round-trip (printf '\e[5n' with the terminal's
reply ESC [ 0 n bound to possible-completions), keeping the help path
and output identical to the previous behavior.
'set terminal key query-help enable' now restores the quote-aware
handler instead of the raw binding; 'disable' keeps its existing
self-insert override, which takes precedence at login as before.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
T8262 renamed "vpp settings ipsec" to "vpp settings ipsec-acceleration".
get_config() still probed the old path, so vpp_ipsec_exists was always
False and incompatible IKE/ESP algorithms were accepted.
Check the current CLI node and add a smoketest that 3des (ESP) and
serpent128 (IKE) fail commit when VPP IPsec acceleration is enabled.
|
|
|
|
Expose the Group Policy extension as a valueless gbp CLI node and pass
it to iproute2 when creating VXLAN links. Recreate links when GBP
changes and reject configurations combining GBP with GPE.
Reject conflicting GBP settings on a shared UDP port, accounting for
address families and underlay VRF socket bindings. Warn that external
mode carries GBP in tunnel metadata instead of the packet mark.
Add smoketest coverage for GBP creation, removal and re-enabling,
retained interface settings, rejection without changing the running
tunnel, shared ports, separate address families, and external mode.
|
|
vyos-configd used to be optional, so every FRR related conf_mode script
carried a fallback that instantiated FRRender() and rendered/applied the
configuration on its own whenever the service was not running. The service
is now the only thing driving configuration mode, so that branch can never
be taken.
Drop the is_systemd_service_running('vyos-configd.service') fallbacks along
with the now unused FRRender imports. For bonding and ethernet interfaces
this also removes collecting the full frrender dict on an "evpn" node change,
which only existed to feed that dead path.
|