mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-24 09:43:02 +02:00
Merge pull request #1379 from kernelkit/copilot/add-infix-keystore-documentation
Keystore refactor and documentation Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
+2
-2
@@ -218,8 +218,8 @@
|
||||
"symmetric-key": [
|
||||
{
|
||||
"name": "wifi",
|
||||
"infix-keystore:symmetric-key": "infixinfix",
|
||||
"infix-keystore:key-format": "infix-crypto-types:wifi-preshared-key-format"
|
||||
"cleartext-symmetric-key": "aW5maXhpbmZpeA==",
|
||||
"key-format": "infix-crypto-types:passphrase-key-format"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -107,8 +107,10 @@ To configure WiFi as a client, first store your WiFi password in the keystore:
|
||||
```
|
||||
admin@infix:/> configure
|
||||
admin@infix:/config/> edit keystore symmetric-key mywifi
|
||||
admin@infix:/config/keystore/…/mywifi/> set key-format wifi-preshared-key-format
|
||||
admin@infix:/config/keystore/…/mywifi/> set symmetric-key YourWiFiPassword
|
||||
admin@infix:/config/keystore/…/mywifi/> set key-format passphrase-key-format
|
||||
admin@infix:/config/keystore/…/mywifi/> change cleartext-symmetric-key
|
||||
Passphrase: ************
|
||||
Retype passphrase: ************
|
||||
admin@infix:/config/keystore/…/mywifi/> leave
|
||||
```
|
||||
|
||||
|
||||
@@ -1,23 +1,49 @@
|
||||
#!/bin/sh
|
||||
# Prompt for a secret with confirmation, then encode and output it.
|
||||
#
|
||||
# Default mode: hash with mkpasswd (for system passwords)
|
||||
# askpass [OUTPUT]
|
||||
#
|
||||
# Base64 mode (-b): base64-encode (for keystore passphrases)
|
||||
# askpass -b [OUTPUT]
|
||||
#
|
||||
# If OUTPUT is given, result is written to the file.
|
||||
# If omitted, result is written to stdout.
|
||||
# shellcheck disable=SC3045
|
||||
|
||||
LABEL="New password"
|
||||
MODE=hash
|
||||
if [ "$1" = "-b" ]; then
|
||||
LABEL="Passphrase"
|
||||
MODE=base64
|
||||
shift
|
||||
fi
|
||||
OUTPUT=$1
|
||||
|
||||
read -r -s -p "New password: " password
|
||||
read -r -s -p "$LABEL: " secret
|
||||
>&2 echo
|
||||
read -r -s -p "Retype password: " password_again
|
||||
read -r -s -p "Retype $LABEL: " secret_again
|
||||
>&2 echo
|
||||
|
||||
if [ "$password" != "$password_again" ]; then
|
||||
echo "Passwords do not match, try again."
|
||||
if [ "$secret" != "$secret_again" ]; then
|
||||
echo "${LABEL}s do not match, try again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$OUTPUT" ]; then
|
||||
echo "$password"
|
||||
exit 0
|
||||
if [ -z "$secret" ]; then
|
||||
echo "Empty $LABEL, try again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$MODE" = "base64" ]; then
|
||||
encoded=$(printf '%s' "$secret" | base64 -w 0)
|
||||
else
|
||||
encoded=$(printf '%s\n' "$secret" | mkpasswd -s)
|
||||
fi
|
||||
|
||||
umask 0177
|
||||
echo "$password" | mkpasswd -s > "$OUTPUT"
|
||||
exit 0
|
||||
if [ -z "$OUTPUT" ]; then
|
||||
echo "$encoded"
|
||||
else
|
||||
printf '%s' "$encoded" > "$OUTPUT"
|
||||
fi
|
||||
|
||||
+5
-2
@@ -3,7 +3,7 @@ Change Log
|
||||
|
||||
All notable changes to the project are documented in this file.
|
||||
|
||||
[v26.01.0][UNRELEASED]
|
||||
[v26.01.0][] - 2026-02-03
|
||||
-------------------------
|
||||
|
||||
> [!IMPORTANT]
|
||||
@@ -23,7 +23,7 @@ All notable changes to the project are documented in this file.
|
||||
|
||||
Noteworthy changes and additions in this release are marked below in bold text.
|
||||
|
||||
- Upgrade Linux kernel to 6.18.8 (LTS)
|
||||
- **Upgrade Linux kernel from 6.12.65 to 6.18.8 (LTS)**
|
||||
- Upgrade Buildroot to 2025.02.10 (LTS)
|
||||
- Upgrade libyang to 4.2.2
|
||||
- Upgrade sysrepo to 4.2.10
|
||||
@@ -53,6 +53,9 @@ Noteworthy changes and additions in this release are marked below in bold text.
|
||||
removed during upgrade (for the rest of the configuration to apply) and you
|
||||
need to reconfigure them again. See the [WiFi][] documentation for details
|
||||
- Add support for **WireGuard VPN tunnels**.
|
||||
- Updated CLI change command to support `cleartext-symmetric-key` (type binary).
|
||||
Used by both WireGuard and WiFi, with application-specific `key-format` for
|
||||
keys and passphrases
|
||||
- New default NACM privilege levels (user levels) in `factory-config`:
|
||||
`operator` (network & container manager) and `guest` (read-only). For
|
||||
details, see the updated system configuration documentation, as well as a
|
||||
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
# Keystore
|
||||
|
||||
The Infix keystore is a centralized storage system for cryptographic keys
|
||||
used throughout the system. It is based on the IETF standards [RFC 9641][1]
|
||||
(Keystore) and [RFC 9640][2] (Cryptographic Types), with Infix extensions
|
||||
for WiFi and WireGuard key formats.
|
||||
|
||||
## Overview
|
||||
|
||||
The keystore supports two types of cryptographic keys:
|
||||
|
||||
1. **Asymmetric Keys** — public/private key pairs used for:
|
||||
- SSH host authentication (RSA keys)
|
||||
- WireGuard VPN tunnels (X25519 keys)
|
||||
|
||||
2. **Symmetric Keys** — shared secrets used for:
|
||||
- WiFi authentication (WPA2/WPA3 pre-shared keys)
|
||||
- WireGuard VPN pre-shared keys
|
||||
|
||||
All keys are stored under the `ietf-keystore` configuration path and can be
|
||||
managed via CLI, NETCONF, or RESTCONF.
|
||||
|
||||
### Supported Formats
|
||||
|
||||
| **Asymmetric Key Format** | **Use Case** | **Key Type** |
|
||||
|----------------------------------------------------------|---------------|--------------|
|
||||
| `rsa-private-key-format` / `ssh-public-key-format` | SSH host keys | RSA |
|
||||
| `x25519-private-key-format` / `x25519-public-key-format` | WireGuard VPN | Curve25519 |
|
||||
|
||||
| **Symmetric Key Format** | **Use Case** |
|
||||
|-----------------------------|-----------------------------------|
|
||||
| `passphrase-key-format` | Human-readable passphrases (WiFi) |
|
||||
| `octet-string-key-format` | Raw symmetric keys (WireGuard) |
|
||||
|
||||
## Asymmetric Keys
|
||||
|
||||
Asymmetric keys consist of a public/private key pair. The public key can be
|
||||
shared freely, while the private key must be kept secure.
|
||||
|
||||
### SSH Host Keys
|
||||
|
||||
SSH host keys identify the system during SSH and NETCONF connections. The
|
||||
default host key is automatically generated on first boot and stored in the
|
||||
keystore with the name `genkey`.
|
||||
|
||||
See [SSH Management](management.md) for details on generating and importing
|
||||
custom SSH host keys.
|
||||
|
||||
### WireGuard Keys
|
||||
|
||||
WireGuard uses X25519 elliptic curve cryptography for key exchange. Each
|
||||
WireGuard interface requires a public/private key pair stored as an asymmetric
|
||||
key in the keystore. Key pairs can be generated directly from the CLI:
|
||||
|
||||
<pre class="cli"><code>admin@example:/> <b>wireguard genkey</b>
|
||||
Private: aMqBvZqkSP5JrqBvZqkSP5JrqBvZqkSP5JrqBvZqkSP=
|
||||
Public: bN1CwZ1lTP6KsrCwZ1lTP6KsrCwZ1lTP6KsrCwZ1lTP=
|
||||
</code></pre>
|
||||
|
||||
See [WireGuard VPN](vpn-wireguard.md) for key generation and configuration
|
||||
examples.
|
||||
|
||||
## Symmetric Keys
|
||||
|
||||
Symmetric keys are shared secrets where the same key must be configured on
|
||||
all systems that need to communicate.
|
||||
|
||||
### WiFi Pre-Shared Keys
|
||||
|
||||
WiFi networks secured with WPA2 or WPA3 use pre-shared keys stored as
|
||||
symmetric keys in the keystore with `passphrase-key-format`. The
|
||||
passphrase must be 8-63 printable ASCII characters.
|
||||
|
||||
Since symmetric keys are stored as binary (base64-encoded), the CLI
|
||||
provides the `change` command to enter passphrases interactively:
|
||||
|
||||
<pre class="cli"><code>admin@example:/config/keystore/…/my-wifi-key/> <b>change cleartext-symmetric-key</b>
|
||||
Passphrase: ************
|
||||
Retype passphrase: ************
|
||||
</code></pre>
|
||||
|
||||
See [WiFi](wifi.md) for complete configuration examples.
|
||||
|
||||
### WireGuard Pre-Shared Keys
|
||||
|
||||
WireGuard supports optional pre-shared keys (PSK) that add a layer of
|
||||
symmetric encryption alongside Curve25519. PSKs use the standard IETF
|
||||
`octet-string-key-format` (32 random bytes). This provides defense-in-depth
|
||||
against future quantum computers that might break elliptic curve cryptography.
|
||||
Note, however, that WireGuard’s authentication and initial key agreement
|
||||
remain Curve25519-based, so PSKs only protect the session encryption,
|
||||
not the handshake itself.
|
||||
|
||||
PSKs can be generated directly from the CLI:
|
||||
|
||||
<pre class="cli"><code>admin@example:/> <b>wireguard genpsk</b>
|
||||
cO2DxZ2mUQ7LtsrDxZ2mUQ7LtsrDxZ2mUQ7LtsrDxZ2m=
|
||||
</code></pre>
|
||||
|
||||
See [WireGuard VPN](vpn-wireguard.md) for PSK generation and usage examples.
|
||||
|
||||
## Viewing Keys
|
||||
|
||||
The `show keystore` command in admin-exec mode gives an overview of all
|
||||
keys in the keystore. Passphrases (WiFi passwords) are decoded and shown
|
||||
in cleartext, while binary keys (WireGuard PSKs) are shown as base64:
|
||||
|
||||
<pre class="cli"><code>admin@example:/> <b>show keystore</b>
|
||||
────────────────────────────────────────────────────────────────────────
|
||||
<span class="title">Symmetric Keys</span>
|
||||
<span class="header">NAME FORMAT VALUE </span>
|
||||
my-wifi-key passphrase MySecretPassword
|
||||
wg-psk octet-string zYr83O4Ykj9i1gN+/aaosJxQx...
|
||||
|
||||
────────────────────────────────────────────────────────────────────────
|
||||
<span class="title">Asymmetric Keys</span>
|
||||
<span class="header">NAME TYPE PUBLIC KEY </span>
|
||||
genkey rsa MIIBCgKCAQEAnj0YinjhYDgYbEGuh7...
|
||||
wg-tunnel x25519 bN1CwZ1lTP6KsrCwZ1lTP6KsrCwZ1...
|
||||
</code></pre>
|
||||
|
||||
To see the full (untruncated) details of a specific key, use the
|
||||
`symmetric` or `asymmetric` qualifier with the key name:
|
||||
|
||||
<pre class="cli"><code>admin@example:/> <b>show keystore symmetric my-wifi-key</b>
|
||||
name : my-wifi-key
|
||||
format : passphrase
|
||||
value : MySecretPassword
|
||||
|
||||
admin@example:/> <b>show keystore asymmetric genkey</b>
|
||||
name : genkey
|
||||
algorithm : rsa
|
||||
public key format : ssh-public-key
|
||||
public key : MIIBCgKCAQEAnj0YinjhY...full key...IDAQAB
|
||||
</code></pre>
|
||||
|
||||
> [!NOTE]
|
||||
> The `show keystore` command is protected by NACM. Only users in the
|
||||
> `admin` group can view keystore data. Operator-level users will see a
|
||||
> message indicating that no keystore data is available.
|
||||
|
||||
The full configuration-mode view (including private keys) is still
|
||||
available via `configure` and then `show keystore`:
|
||||
|
||||
<pre class="cli"><code>admin@example:/config/> <b>show keystore</b>
|
||||
</code></pre>
|
||||
|
||||
> [!WARNING]
|
||||
> The configuration-mode `show keystore` displays private keys in
|
||||
> cleartext. Be careful when viewing keys on shared screens or in
|
||||
> logged sessions. The admin-exec `show keystore` command never
|
||||
> displays private keys.
|
||||
|
||||
## Deleting Keys
|
||||
|
||||
<pre class="cli"><code>admin@example:/> <b>configure</b>
|
||||
admin@example:/config/> <b>delete keystore asymmetric-key mykey</b>
|
||||
admin@example:/config/> <b>leave</b>
|
||||
</code></pre>
|
||||
|
||||
> [!CAUTION]
|
||||
> Deleting a key that is referenced by a service (SSH, WireGuard, WiFi) will
|
||||
> cause that service to fail. Verify the key is not in use before deletion.
|
||||
|
||||
## Security Considerations
|
||||
|
||||
The keystore is protected by NACM (Network Access Control Model) rules.
|
||||
Only users in the `admin` group can view or modify cryptographic keys.
|
||||
See [NACM](nacm.md) for details on access control.
|
||||
|
||||
Private keys are stored in cleartext in the configuration database.
|
||||
Configuration files and backups containing the keystore should be treated
|
||||
as sensitive and protected accordingly.
|
||||
|
||||
### Key Validation
|
||||
|
||||
Symmetric key values are stored as binary (base64-encoded). The system
|
||||
validates them based on their declared format:
|
||||
|
||||
- `passphrase-key-format`: Used by WiFi, must decode to 8-63 ASCII characters
|
||||
- `octet-string-key-format`: Used by Wireguard, must decode to exactly 32 bytes (256 bits)
|
||||
|
||||
## References
|
||||
|
||||
- [RFC 9641 - A YANG Data Model for a Keystore][1]
|
||||
- [RFC 9640 - YANG Data Types and Groupings for Cryptography][2]
|
||||
- [WiFi Documentation](wifi.md)
|
||||
- [WireGuard VPN Documentation](vpn-wireguard.md)
|
||||
- [SSH Management](management.md)
|
||||
- [NACM Access Control](nacm.md)
|
||||
|
||||
[1]: https://datatracker.ietf.org/doc/html/rfc9641
|
||||
[2]: https://datatracker.ietf.org/doc/html/rfc9640
|
||||
@@ -47,6 +47,11 @@ RSA for now, thus the private key must be
|
||||
`ietf-crypto-types:rsa-private-key-format` and the public key
|
||||
`ietf-crypto-types:ssh-public-key-format`
|
||||
|
||||
> [!TIP]
|
||||
> For comprehensive information about the keystore, including key management,
|
||||
> security considerations, and examples for different key types, see the
|
||||
> [Keystore documentation](keystore.md).
|
||||
|
||||
### Use your own SSH hostkeys
|
||||
|
||||
Hostkeys can be generated with OpenSSL:
|
||||
|
||||
+17
-28
@@ -26,31 +26,25 @@ Key features of WireGuard:
|
||||
WireGuard uses public-key cryptography similar to SSH. Each WireGuard interface
|
||||
requires a private key, and each peer is identified by its public key.
|
||||
|
||||
**Generate a WireGuard key pair using the `wg` command:**
|
||||
|
||||
```bash
|
||||
admin@example:~$ wg genkey | tee privatekey | wg pubkey > publickey
|
||||
admin@example:~$ cat privatekey
|
||||
aMqBvZqkSP5JrqBvZqkSP5JrqBvZqkSP5JrqBvZqkSP=
|
||||
admin@example:~$ cat publickey
|
||||
bN1CwZ1lTP6KsrCwZ1lTP6KsrCwZ1lTP6KsrCwZ1lTP=
|
||||
```
|
||||
|
||||
This generates a private key, saves it to `privatekey`, derives the public key,
|
||||
and saves it to `publickey`.
|
||||
|
||||
**Import the private key into the keystore:**
|
||||
**Import the key pair into the keystore:**
|
||||
|
||||
<pre class="cli"><code>admin@example:/> <b>configure</b>
|
||||
admin@example:/config/> <b>edit keystore asymmetric-key wg-site-a</b>
|
||||
admin@example:/config/keystore/asymmetric-key/wg-site-a/> <b>do wireguard genkey</b>
|
||||
Private: aMqBvZqkSP5JrqBvZqkSP5JrqBvZqkSP5JrqBvZqkSP=
|
||||
Public: bN1CwZ1lTP6KsrCwZ1lTP6KsrCwZ1lTP6KsrCwZ1lTP=
|
||||
admin@example:/config/keystore/asymmetric-key/wg-site-a/> <b>set public-key-format x25519-public-key-format</b>
|
||||
admin@example:/config/keystore/asymmetric-key/wg-site-a/> <b>set private-key-format x25519-private-key-format</b>
|
||||
admin@example:/config/keystore/asymmetric-key/wg-site-a/> <b>set public-key bN1CwZ1lTP6KsrCwZ1lTP6KsrCwZ1lTP6KsrCwZ1lTP=</b>
|
||||
admin@example:/config/keystore/asymmetric-key/wg-site-a/> <b>set private-key aMqBvZqkSP5JrqBvZqkSP5JrqBvZqkSP5JrqBvZqkSP=</b>
|
||||
admin@example:/config/keystore/asymmetric-key/wg-site-a/> <b>set cleartext-private-key aMqBvZqkSP5JrqBvZqkSP5JrqBvZqkSP5JrqBvZqkSP=</b>
|
||||
admin@example:/config/keystore/asymmetric-key/wg-site-a/> <b>leave</b>
|
||||
admin@example:/>
|
||||
</code></pre>
|
||||
|
||||
> [!TIP]
|
||||
> The `do` prefix allows running admin-exec commands from configure context.
|
||||
> Use the base for `wireguard genkey` when in admin-exec context.
|
||||
|
||||
**Import peer public keys into the truststore:**
|
||||
|
||||
<pre class="cli"><code>admin@example:/> <b>configure</b>
|
||||
@@ -63,8 +57,7 @@ admin@example:/>
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Keep private keys secure! Never share your private key. Only exchange
|
||||
> public keys with peers. Delete the `privatekey` file after importing it
|
||||
> into the keystore.
|
||||
> public keys with peers.
|
||||
|
||||
## Point-to-Point Configuration
|
||||
|
||||
@@ -394,20 +387,17 @@ recording traffic today would still need the PSK even if they break Curve25519
|
||||
later. However, peer authentication still relies on Curve25519, so PSKs don't
|
||||
provide complete post-quantum security.
|
||||
|
||||
**Generate a preshared key using `wg genpsk`:**
|
||||
**Generate a preshared key:**
|
||||
|
||||
```bash
|
||||
admin@example:~$ wg genpsk > preshared.key
|
||||
admin@example:~$ cat preshared.key
|
||||
<pre class="cli"><code>admin@example:/config/> <b>do wireguard genpsk</b>
|
||||
cO2DxZ2mUQ7LtsrDxZ2mUQ7LtsrDxZ2mUQ7LtsrDxZ2m=
|
||||
```
|
||||
</code></pre>
|
||||
|
||||
**Import the preshared key into the keystore:**
|
||||
|
||||
<pre class="cli"><code>admin@example:/> <b>configure</b>
|
||||
admin@example:/config/> <b>edit keystore symmetric-key wg-psk</b>
|
||||
admin@example:/config/keystore/symmetric-key/wg-psk/> <b>set key-format wireguard-symmetric-key-format</b>
|
||||
admin@example:/config/keystore/symmetric-key/wg-psk/> <b>set key cO2DxZ2mUQ7LtsrDxZ2mUQ7LtsrDxZ2mUQ7LtsrDxZ2m=</b>
|
||||
<pre class="cli"><code>admin@example:/config/> <b>edit keystore symmetric-key wg-psk</b>
|
||||
admin@example:/config/keystore/symmetric-key/wg-psk/> <b>set key-format octet-string-key-format</b>
|
||||
admin@example:/config/keystore/symmetric-key/wg-psk/> <b>set cleartext-symmetric-key cO2DxZ2mUQ7LtsrDxZ2mUQ7LtsrDxZ2mUQ7LtsrDxZ2m=</b>
|
||||
admin@example:/config/keystore/symmetric-key/wg-psk/> <b>end</b>
|
||||
admin@example:/config/interface/wg0/> <b>edit wireguard peers wg-peers peer remote</b>
|
||||
admin@example:/config/interface/…/wg-peers/peer/remote/> <b>set preshared-key wg-psk</b>
|
||||
@@ -420,5 +410,4 @@ on both sides.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Preshared keys must be kept secret and exchanged through a secure channel,
|
||||
> just like passwords. Delete the `preshared.key` file after importing it
|
||||
> into both peer keystores.
|
||||
> just like passwords.
|
||||
|
||||
+45
-14
@@ -241,6 +241,23 @@ In the CLI, signal strength is reported as: excellent, good, fair or bad.
|
||||
For precise signal strength values in dBm, use NETCONF or RESTCONF to access
|
||||
the `signal-strength` leaf in the operational datastore.
|
||||
|
||||
## Passphrase Requirements
|
||||
|
||||
To ensure your connection is secure and compatible with all network
|
||||
hardware, your passphrase must meet the following criteria:
|
||||
|
||||
- Length: Between 8 and 63 characters
|
||||
- Characters: Use only standard English keyboard characters
|
||||
- Allowed: Letters (A-Z, a-z), numbers (0-9), and common symbols (e.g., ! @ # $ % ^ & * ( ) _ + - = [ ] { } | ; : ' " , . < > / ? ~)
|
||||
- Spaces: Spaces are allowed, but not at the very beginning or very end of the passphrase
|
||||
- Prohibited: Emojis, accented characters (like á or ñ), and special "control" characters
|
||||
|
||||
> [!TIP] Why the limit?
|
||||
> Standard WiFi security (WPA2/WPA3) requires a minimum of 8 characters to
|
||||
> prevent "brute-force" hacking. The character limit ensures your password
|
||||
> works on older routers and various operating systems.
|
||||
> Tips for password strength, see [XKCD #936](https://xkcd.com/936/).
|
||||
|
||||
## Station Mode (Client)
|
||||
|
||||
Station mode connects to an existing Wi-Fi network. Before configuring station
|
||||
@@ -253,11 +270,16 @@ Create a keystore entry for your WiFi password (8-63 characters):
|
||||
|
||||
<pre class="cli"><code>admin@example:/> <b>configure</b>
|
||||
admin@example:/config/> <b>edit keystore symmetric-key my-wifi-key</b>
|
||||
admin@example:/config/keystore/…/my-wifi-key/> <b>set key-format wifi-preshared-key-format</b>
|
||||
admin@example:/config/keystore/…/my-wifi-key/> <b>set symmetric-key MyPassword123</b>
|
||||
admin@example:/config/keystore/…/my-wifi-key/> <b>set key-format passphrase-key-format</b>
|
||||
admin@example:/config/keystore/…/my-wifi-key/> <b>change cleartext-symmetric-key</b>
|
||||
Passphrase: ************
|
||||
Retype passphrase: ************
|
||||
admin@example:/config/keystore/…/my-wifi-key/> <b>leave</b>
|
||||
</code></pre>
|
||||
|
||||
The `change` command prompts for the passphrase interactively and
|
||||
handles the base64 encoding required by the keystore automatically.
|
||||
|
||||
### Step 2: Connect to Network
|
||||
|
||||
Configure station mode with the SSID and password to connect:
|
||||
@@ -276,8 +298,9 @@ name : wifi0
|
||||
type : wifi
|
||||
operational status : up
|
||||
physical address : f0:09:0d:36:5f:86
|
||||
SSID : MyHomeNetwork
|
||||
Signal : excellent
|
||||
mode : station
|
||||
ssid : MyHomeNetwork
|
||||
signal : -52 dBm (good)
|
||||
</code></pre>
|
||||
|
||||
**Station configuration parameters:**
|
||||
@@ -310,8 +333,10 @@ create a keystore entry for your WiFi password and configure the AP interface:
|
||||
|
||||
<pre class="cli"><code>admin@example:/> <b>configure</b>
|
||||
admin@example:/config/> <b>edit keystore symmetric-key my-wifi-secret</b>
|
||||
admin@example:/config/keystore/…/my-wifi-secret/> <b>set key-format wifi-preshared-key-format</b>
|
||||
admin@example:/config/keystore/…/my-wifi-secret/> <b>set symmetric-key MySecurePassword123</b>
|
||||
admin@example:/config/keystore/…/my-wifi-secret/> <b>set key-format passphrase-key-format</b>
|
||||
admin@example:/config/keystore/…/my-wifi-secret/> <b>change cleartext-symmetric-key</b>
|
||||
Passphrase: ************
|
||||
Retype passphrase: ************
|
||||
admin@example:/config/keystore/…/my-wifi-secret/> <b>end</b>
|
||||
</code></pre>
|
||||
|
||||
@@ -371,14 +396,20 @@ admin@example:/config/hardware/component/radio0/wifi-radio/> <b>leave</b>
|
||||
|
||||
<pre class="cli"><code>admin@example:/> <b>configure</b>
|
||||
admin@example:/config/> <b>edit keystore symmetric-key main-secret</b>
|
||||
admin@example:/config/keystore/…/main-secret/> <b>set key-format wifi-preshared-key-format</b>
|
||||
admin@example:/config/keystore/…/main-secret/> <b>set symmetric-key MyMainPassword</b>
|
||||
admin@example:/config/keystore/…/main-secret/> <b>set key-format passphrase-key-format</b>
|
||||
admin@example:/config/keystore/…/main-secret/> <b>change cleartext-symmetric-key</b>
|
||||
Passphrase: ************
|
||||
Retype passphrase: ************
|
||||
admin@example:/config/> <b>edit keystore symmetric-key guest-secret</b>
|
||||
admin@example:/config/keystore/…/guest-secret/> <b>set key-format wifi-preshared-key-format</b>
|
||||
admin@example:/config/keystore/…/guest-secret/> <b>set symmetric-key GuestPassword123</b>
|
||||
admin@example:/config/keystore/…/guest-secret/> <b>set key-format passphrase-key-format</b>
|
||||
admin@example:/config/keystore/…/guest-secret/> <b>change cleartext-symmetric-key</b>
|
||||
Passphrase: ************
|
||||
Retype passphrase: ************
|
||||
admin@example:/config/> <b>edit keystore symmetric-key iot-secret</b>
|
||||
admin@example:/config/keystore/…/iot-secret/> <b>set key-format wifi-preshared-key-format</b>
|
||||
admin@example:/config/keystore/…/iot-secret/> <b>set symmetric-key IoTDevices2025</b>
|
||||
admin@example:/config/keystore/…/iot-secret/> <b>set key-format passphrase-key-format</b>
|
||||
admin@example:/config/keystore/…/iot-secret/> <b>change cleartext-symmetric-key</b>
|
||||
Passphrase: ************
|
||||
Retype passphrase: ************
|
||||
admin@example:/config/keystore/…/iot-secret/> <b>leave</b>
|
||||
</code></pre>
|
||||
|
||||
@@ -466,8 +497,8 @@ If issues arise, try the following troubleshooting steps:
|
||||
|
||||
1. **Verify signal strength**: Check that the target network shows
|
||||
"good" or "excellent" signal in scan results
|
||||
2. **Check credentials**: Verify the preshared key in the keystore
|
||||
matches the network password
|
||||
2. **Check credentials**: Use `show keystore symmetric <name>` to verify
|
||||
the passphrase matches the network password
|
||||
3. **Review logs**: Check system logs with `show log` for Wi-Fi related
|
||||
errors
|
||||
4. **Regulatory compliance**: Ensure the country-code on the radio
|
||||
|
||||
@@ -53,6 +53,7 @@ nav:
|
||||
- Access Control (NACM): nacm.md
|
||||
- Hardware Info & Status: hardware.md
|
||||
- Management: management.md
|
||||
- Keystore: keystore.md
|
||||
- Syslog Support: syslog.md
|
||||
- Support Data: support.md
|
||||
- Upgrade: upgrade.md
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Locally calculated
|
||||
sha256 9d9d33b873917ca5d0bdcc47a36d2fd385971ab0c045d1472fcadf95ee5bcf5b LICENCE
|
||||
sha256 6e098390be2a78a56cf94eecb9d28b11c1791dd2c364e59602bbd664b508fd57 klish-plugin-sysrepo-a4b1fae697b51614dc75989e8f4fc8d277689d16-git4.tar.gz
|
||||
sha256 7bfdaef838ee8bd3995140c40144abe79ff1c6391c7b48445e6728cd4e7e56a8 klish-plugin-sysrepo-2f14503c7ea6eb24c8adaf0cf2cf7a511114b09e-git4.tar.gz
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#
|
||||
################################################################################
|
||||
|
||||
KLISH_PLUGIN_SYSREPO_VERSION = a4b1fae697b51614dc75989e8f4fc8d277689d16
|
||||
KLISH_PLUGIN_SYSREPO_VERSION = 2f14503c7ea6eb24c8adaf0cf2cf7a511114b09e
|
||||
KLISH_PLUGIN_SYSREPO_SITE = https://github.com/kernelkit/klish-plugin-sysrepo.git
|
||||
#KLISH_PLUGIN_SYSREPO_VERSION = cdd3eb51a7f7ee0ed5bd925fa636061d3b1b85fb
|
||||
#KLISH_PLUGIN_SYSREPO_SITE = https://src.libcode.org/pkun/klish-plugin-sysrepo.git
|
||||
|
||||
@@ -190,7 +190,7 @@ def interface(args: List[str]) -> None:
|
||||
elif len(args) == 1:
|
||||
iface = args[0]
|
||||
if is_valid_interface_name(iface):
|
||||
cli_pretty(data, f"show-interfaces", "-n", iface)
|
||||
cli_pretty(data, "show-interfaces", "-n", iface)
|
||||
else:
|
||||
print(f"Invalid interface name: {iface}")
|
||||
else:
|
||||
@@ -659,6 +659,25 @@ def nacm(args: List[str]) -> None:
|
||||
print(f"Unknown NACM subcommand: {subcommand}")
|
||||
|
||||
|
||||
def keystore(args: List[str]) -> None:
|
||||
data = get_json("/ietf-keystore:keystore", "running", quiet=True)
|
||||
if not data:
|
||||
user = os.environ.get('USER', 'unknown')
|
||||
print(f'No keystore data available (check NACM permissions for "{user}").')
|
||||
return
|
||||
|
||||
if RAW_OUTPUT:
|
||||
print(json.dumps(data, indent=2))
|
||||
return
|
||||
|
||||
if len(args) == 0 or not args[0]:
|
||||
cli_pretty(data, "show-keystore")
|
||||
elif len(args) >= 2 and args[0] in ("symmetric", "asymmetric"):
|
||||
cli_pretty(data, "show-keystore", "-t", args[0], "-n", args[1])
|
||||
else:
|
||||
print("Usage: show keystore [symmetric <name> | asymmetric <name>]")
|
||||
|
||||
|
||||
def execute_command(command: str, args: List[str]):
|
||||
command_mapping = {
|
||||
'bfd': bfd,
|
||||
@@ -667,6 +686,7 @@ def execute_command(command: str, args: List[str]):
|
||||
'dhcp': dhcp,
|
||||
'hardware': hardware,
|
||||
'interface': interface,
|
||||
'keystore': keystore,
|
||||
'lldp': lldp,
|
||||
'nacm': nacm,
|
||||
'ntp': ntp,
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
#!/bin/sh
|
||||
# Rename cleartext-key to symmetric-key
|
||||
# Migrate symmetric keys from v1.6 (v25.11) to IETF standard format.
|
||||
#
|
||||
# v1.6 had WiFi keys stored as:
|
||||
# - infix-keystore:cleartext-key (type string, plaintext)
|
||||
# - infix-keystore:key-format wifi-preshared-key-format
|
||||
#
|
||||
# v1.7 uses the IETF standard leaf and updated format names:
|
||||
# - cleartext-symmetric-key (type binary, base64-encoded)
|
||||
# - key-format passphrase-key-format
|
||||
|
||||
file=$1
|
||||
temp=${file}.tmp
|
||||
@@ -7,12 +15,17 @@ temp=${file}.tmp
|
||||
jq '
|
||||
if .["ietf-keystore:keystore"]?."symmetric-keys"?."symmetric-key" then
|
||||
.["ietf-keystore:keystore"]."symmetric-keys"."symmetric-key" |= map(
|
||||
if ."infix-keystore:key-format" then
|
||||
del(."infix-keystore:key-format") |
|
||||
. + { "key-format": "infix-crypto-types:passphrase-key-format" }
|
||||
else
|
||||
.
|
||||
end |
|
||||
|
||||
if ."infix-keystore:cleartext-key" then
|
||||
# Rename cleartext-key to symmetric-key
|
||||
."infix-keystore:cleartext-key" as $key_value |
|
||||
del(."infix-keystore:cleartext-key") | . + {
|
||||
"infix-keystore:symmetric-key": $key_value
|
||||
}
|
||||
."infix-keystore:cleartext-key" as $val |
|
||||
del(."infix-keystore:cleartext-key") |
|
||||
. + { "cleartext-symmetric-key": ($val | @base64) }
|
||||
else
|
||||
.
|
||||
end
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "core.h"
|
||||
#include "interfaces.h"
|
||||
#include "dagger.h"
|
||||
#include "base64.h"
|
||||
|
||||
#define XPATH_BASE_ "/ietf-hardware:hardware"
|
||||
#define HOSTAPD_CONF "/etc/hostapd-%s.conf"
|
||||
@@ -235,8 +236,9 @@ static int wifi_find_radio_aps(struct lyd_node *cifs, const char *radio_name,
|
||||
/* Helper: Write SSID and security configuration (shared between primary and BSS) */
|
||||
static void wifi_gen_ssid_config(FILE *hostapd, struct lyd_node *cif, struct lyd_node *config, bool is_bss)
|
||||
{
|
||||
const char *ssid, *hidden, *security_mode, *secret_name, *secret;
|
||||
const char *ssid, *hidden, *security_mode, *secret_name;
|
||||
struct lyd_node *wifi, *ap, *security, *secret_node;
|
||||
unsigned char *secret = NULL;
|
||||
const char *ifname;
|
||||
char bssid[18];
|
||||
|
||||
@@ -282,15 +284,19 @@ static void wifi_gen_ssid_config(FILE *hostapd, struct lyd_node *cif, struct lyd
|
||||
security_mode = "open";
|
||||
|
||||
/* Get secret from keystore if needed */
|
||||
secret = NULL;
|
||||
if (strcmp(security_mode, "open") != 0) {
|
||||
secret_name = lydx_get_cattr(security, "secret");
|
||||
if (secret_name) {
|
||||
const char *b64;
|
||||
|
||||
secret_node = lydx_get_xpathf(config,
|
||||
"/keystore/symmetric-keys/symmetric-key[name='%s']/symmetric-key",
|
||||
"/keystore/symmetric-keys/symmetric-key[name='%s']/cleartext-symmetric-key",
|
||||
secret_name);
|
||||
if (secret_node)
|
||||
secret = lyd_get_value(secret_node);
|
||||
if (secret_node) {
|
||||
b64 = lyd_get_value(secret_node);
|
||||
if (b64)
|
||||
secret = base64_decode((const unsigned char *)b64, strlen(b64), NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,6 +335,8 @@ static void wifi_gen_ssid_config(FILE *hostapd, struct lyd_node *cif, struct lyd
|
||||
/* ieee80211w=1: MFP capable but optional, for WPA2 client compatibility */
|
||||
fprintf(hostapd, "ieee80211w=1\n");
|
||||
}
|
||||
|
||||
free(secret);
|
||||
}
|
||||
|
||||
/* Helper: Write radio-specific configuration */
|
||||
|
||||
+76
-8
@@ -7,14 +7,76 @@
|
||||
* configuration (hostapd) is handled by hardware.c.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include <srx/lyx.h>
|
||||
#include <srx/srx_val.h>
|
||||
|
||||
#include "interfaces.h"
|
||||
#include "base64.h"
|
||||
|
||||
#define WPA_SUPPLICANT_CONF "/etc/wpa_supplicant-%s.conf"
|
||||
|
||||
|
||||
int wifi_validate_secret(sr_session_ctx_t *session, struct lyd_node *cif)
|
||||
{
|
||||
struct lyd_node *wifi, *station, *security, *secret_node;
|
||||
const char *ifname, *secret_name, *security_mode, *b64;
|
||||
unsigned char *decoded;
|
||||
size_t len;
|
||||
|
||||
ifname = lydx_get_cattr(cif, "name");
|
||||
wifi = lydx_get_child(cif, "wifi");
|
||||
if (!wifi)
|
||||
return SR_ERR_OK;
|
||||
|
||||
station = lydx_get_child(wifi, "station");
|
||||
if (!station)
|
||||
return SR_ERR_OK;
|
||||
|
||||
security = lydx_get_child(station, "security");
|
||||
security_mode = lydx_get_cattr(security, "mode");
|
||||
secret_name = lydx_get_cattr(security, "secret");
|
||||
|
||||
if (!secret_name || !strcmp(security_mode, "disabled"))
|
||||
return SR_ERR_OK;
|
||||
|
||||
secret_node = lydx_get_xpathf(cif,
|
||||
"../../keystore/symmetric-keys/symmetric-key[name='%s']",
|
||||
secret_name);
|
||||
b64 = lydx_get_cattr(secret_node, "cleartext-symmetric-key");
|
||||
if (!b64 || !*b64)
|
||||
return SR_ERR_OK;
|
||||
|
||||
decoded = base64_decode((const unsigned char *)b64, strlen(b64), &len);
|
||||
if (!decoded)
|
||||
return SR_ERR_OK;
|
||||
|
||||
if (len < 8 || len > 63) {
|
||||
if (session)
|
||||
sr_session_set_error_message(session,
|
||||
"%s: WiFi passphrase must be 8-63 characters, got %zu",
|
||||
ifname, len);
|
||||
free(decoded);
|
||||
return SR_ERR_VALIDATION_FAILED;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (!isprint((unsigned char)decoded[i])) {
|
||||
if (session)
|
||||
sr_session_set_error_message(session,
|
||||
"%s: WiFi passphrase contains non-printable "
|
||||
"character at position %zu",
|
||||
ifname, i + 1);
|
||||
free(decoded);
|
||||
return SR_ERR_VALIDATION_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
free(decoded);
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
wifi_mode_t wifi_get_mode(struct lyd_node *iface)
|
||||
{
|
||||
struct lyd_node *ap, *wifi;
|
||||
@@ -59,8 +121,9 @@ int wifi_mode_changed(struct lyd_node *wifi)
|
||||
*/
|
||||
int wifi_gen_station(struct lyd_node *cif)
|
||||
{
|
||||
const char *ifname, *ssid, *secret_name, *secret, *security_mode, *radio;
|
||||
const char *ifname, *ssid, *secret_name, *security_mode, *radio;
|
||||
struct lyd_node *security, *secret_node, *radio_node, *station, *wifi;
|
||||
unsigned char *secret = NULL;
|
||||
FILE *wpa_supplicant = NULL;
|
||||
char *security_str = NULL;
|
||||
const char *country;
|
||||
@@ -91,12 +154,14 @@ int wifi_gen_station(struct lyd_node *cif)
|
||||
country = lydx_get_cattr(radio_node, "country-code");
|
||||
|
||||
if (secret_name && strcmp(security_mode, "disabled") != 0) {
|
||||
const char *b64;
|
||||
|
||||
secret_node = lydx_get_xpathf(cif,
|
||||
"../../keystore/symmetric-keys/symmetric-key[name='%s']",
|
||||
secret_name);
|
||||
secret = lydx_get_cattr(secret_node, "symmetric-key");
|
||||
} else {
|
||||
secret = NULL;
|
||||
b64 = lydx_get_cattr(secret_node, "cleartext-symmetric-key");
|
||||
if (b64)
|
||||
secret = base64_decode((const unsigned char *)b64, strlen(b64), NULL);
|
||||
}
|
||||
|
||||
oldmask = umask(0077);
|
||||
@@ -121,11 +186,13 @@ int wifi_gen_station(struct lyd_node *cif)
|
||||
/* If SSID is present, create network block. Otherwise, scan-only mode */
|
||||
if (ssid) {
|
||||
/* Station mode with network configured */
|
||||
if (!strcmp(security_mode, "disabled")) {
|
||||
if (!strcmp(security_mode, "disabled"))
|
||||
asprintf(&security_str, "key_mgmt=NONE");
|
||||
} else if (secret) {
|
||||
asprintf(&security_str, "key_mgmt=SAE WPA-PSK\npsk=\"%s\"", secret);
|
||||
}
|
||||
else if (secret)
|
||||
asprintf(&security_str,
|
||||
"key_mgmt=SAE WPA-PSK\n"
|
||||
" psk=\"%s\"", secret);
|
||||
|
||||
fprintf(wpa_supplicant,
|
||||
"network={\n"
|
||||
" bgscan=\"simple: 30:-45:300\"\n"
|
||||
@@ -152,6 +219,7 @@ int wifi_gen_station(struct lyd_node *cif)
|
||||
}
|
||||
|
||||
out:
|
||||
free(secret);
|
||||
if (wpa_supplicant)
|
||||
fclose(wpa_supplicant);
|
||||
umask(oldmask);
|
||||
|
||||
@@ -2,9 +2,81 @@
|
||||
#include <srx/lyx.h>
|
||||
|
||||
#include "interfaces.h"
|
||||
#include "base64.h"
|
||||
|
||||
#define WIREGUARD_CONFIG "/run/wireguard-%s.conf"
|
||||
|
||||
static int wireguard_validate_psk(sr_session_ctx_t *session, struct lyd_node *cif,
|
||||
const char *ifname, const char *psk_ref)
|
||||
{
|
||||
struct lyd_node *psk_node;
|
||||
const char *psk_data;
|
||||
unsigned char *decoded;
|
||||
size_t len;
|
||||
|
||||
psk_node = lydx_get_xpathf(cif,
|
||||
"../../keystore/symmetric-keys/symmetric-key[name='%s']",
|
||||
psk_ref);
|
||||
if (!psk_node)
|
||||
return SR_ERR_OK;
|
||||
|
||||
psk_data = lydx_get_cattr(psk_node, "cleartext-symmetric-key");
|
||||
if (!psk_data || !*psk_data)
|
||||
return SR_ERR_OK;
|
||||
|
||||
decoded = base64_decode((const unsigned char *)psk_data, strlen(psk_data), &len);
|
||||
if (!decoded)
|
||||
return SR_ERR_OK;
|
||||
|
||||
if (len != 32) {
|
||||
if (session)
|
||||
sr_session_set_error_message(session,
|
||||
"%s: WireGuard preshared key '%s' must be "
|
||||
"exactly 32 bytes, got %zu",
|
||||
ifname, psk_ref, len);
|
||||
free(decoded);
|
||||
return SR_ERR_VALIDATION_FAILED;
|
||||
}
|
||||
|
||||
free(decoded);
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
int wireguard_validate_peers(sr_session_ctx_t *session, struct lyd_node *cif)
|
||||
{
|
||||
const char *ifname = lydx_get_cattr(cif, "name");
|
||||
struct lyd_node *wg, *bag_peer, *peer;
|
||||
|
||||
wg = lydx_get_child(cif, "wireguard");
|
||||
if (!wg)
|
||||
return SR_ERR_OK;
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(wg), bag_peer, "peers") {
|
||||
const char *psk_ref;
|
||||
int rc;
|
||||
|
||||
/* Validate bag-level PSK */
|
||||
psk_ref = lydx_get_cattr(bag_peer, "preshared-key");
|
||||
if (psk_ref) {
|
||||
rc = wireguard_validate_psk(session, cif, ifname, psk_ref);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Validate per-peer PSK overrides */
|
||||
LYX_LIST_FOR_EACH(lyd_child(bag_peer), peer, "peer") {
|
||||
psk_ref = lydx_get_cattr(peer, "preshared-key");
|
||||
if (psk_ref) {
|
||||
rc = wireguard_validate_psk(session, cif, ifname, psk_ref);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
/* Helper to get a peer setting with override logic:
|
||||
* 1. Check peer-specific override
|
||||
* 2. Fall back to key-bag level default
|
||||
|
||||
@@ -391,9 +391,11 @@ static int netdag_gen_afspec_add(sr_session_ctx_t *session, struct dagger *net,
|
||||
case IFT_VXLAN:
|
||||
return vxlan_gen(NULL, cif, ip);
|
||||
case IFT_WIFI:
|
||||
return wifi_add_iface(cif, net);
|
||||
return wifi_validate_secret(session, cif)
|
||||
? : wifi_add_iface(cif, net);
|
||||
case IFT_WIREGUARD:
|
||||
return wireguard_gen(NULL, cif, ip, net);
|
||||
return wireguard_validate_peers(session, cif)
|
||||
? : wireguard_gen(NULL, cif, ip, net);
|
||||
case IFT_ETH:
|
||||
return netdag_gen_ethtool(net, cif, dif);
|
||||
case IFT_LO:
|
||||
@@ -424,7 +426,8 @@ static int netdag_gen_afspec_set(sr_session_ctx_t *session, struct dagger *net,
|
||||
return netdag_gen_ethtool(net, cif, dif);
|
||||
case IFT_WIFI:
|
||||
if (wifi_get_mode(cif) == wifi_station)
|
||||
return wifi_gen_station(cif);
|
||||
return wifi_validate_secret(session, cif)
|
||||
? : wifi_gen_station(cif);
|
||||
return 0;
|
||||
case IFT_DUMMY:
|
||||
case IFT_GRE:
|
||||
@@ -819,6 +822,39 @@ err_out:
|
||||
return err;
|
||||
}
|
||||
|
||||
int interfaces_validate_keys(sr_session_ctx_t *session, struct lyd_node *config)
|
||||
{
|
||||
struct lyd_node *ifaces, *iface;
|
||||
int rc;
|
||||
|
||||
ifaces = lydx_get_descendant(config, "interfaces", "interface", NULL);
|
||||
LYX_LIST_FOR_EACH(ifaces, iface, "interface") {
|
||||
const char *ifname = lydx_get_cattr(iface, "name");
|
||||
|
||||
switch (iftype_from_iface(iface)) {
|
||||
case IFT_WIFI:
|
||||
rc = wifi_validate_secret(session, iface);
|
||||
break;
|
||||
case IFT_WIREGUARD:
|
||||
rc = wireguard_validate_peers(session, iface);
|
||||
break;
|
||||
default:
|
||||
rc = SR_ERR_OK;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!rc)
|
||||
continue;
|
||||
|
||||
if (session)
|
||||
return rc;
|
||||
|
||||
ERROR("%s: key fails validation, check keystore", ifname);
|
||||
}
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
int interfaces_get_all_l3(const struct lyd_node *tree, char ***ifaces)
|
||||
{
|
||||
struct lyd_node *interfaces, *cif;
|
||||
|
||||
@@ -105,6 +105,7 @@ const char *get_chassis_addr(void);
|
||||
int interface_get_phys_addr(struct lyd_node *cif, char *mac);
|
||||
int link_gen_address(struct lyd_node *cif, FILE *ip);
|
||||
int interfaces_get_all_l3(const struct lyd_node *tree, char ***ifaces);
|
||||
int interfaces_validate_keys(sr_session_ctx_t *session, struct lyd_node *config);
|
||||
|
||||
/* ip.c */
|
||||
int netdag_gen_ipv6_autoconf(struct dagger *net, struct lyd_node *cif,
|
||||
@@ -130,6 +131,7 @@ typedef enum wifi_mode_t {
|
||||
wifi_unknown
|
||||
} wifi_mode_t;
|
||||
|
||||
int wifi_validate_secret(sr_session_ctx_t *session, struct lyd_node *cif);
|
||||
int wifi_add_iface(struct lyd_node *cif, struct dagger *net);
|
||||
int wifi_del_iface(struct lyd_node *dif, struct dagger *net);
|
||||
int wifi_mode_changed(struct lyd_node *wifi);
|
||||
@@ -162,6 +164,7 @@ int ifchange_cand_infer_dhcp(sr_session_ctx_t *session, const char *path);
|
||||
int vxlan_gen(struct lyd_node *dif, struct lyd_node *cif, FILE *ip);
|
||||
|
||||
/* infix-if-wireguard */
|
||||
int wireguard_validate_peers(sr_session_ctx_t *session, struct lyd_node *cif);
|
||||
int wireguard_gen(struct lyd_node *dif, struct lyd_node *cif, FILE *ip, struct dagger *net);
|
||||
|
||||
#endif /* CONFD_INTERFACES_H_ */
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
|
||||
#include "base64.h"
|
||||
#include "core.h"
|
||||
#include "interfaces.h"
|
||||
|
||||
#define XPATH_KEYSTORE_ "/ietf-keystore:keystore/asymmetric-keys"
|
||||
#define XPATH_KEYSTORE_ASYM "/ietf-keystore:keystore/asymmetric-keys"
|
||||
#define XPATH_KEYSTORE_SYM "/ietf-keystore:keystore/symmetric-keys"
|
||||
#define SSH_PRIVATE_KEY "/tmp/ssh.key"
|
||||
#define SSH_PUBLIC_KEY "/tmp/ssh.pub"
|
||||
|
||||
@@ -166,7 +168,8 @@ int keystore_change(sr_session_ctx_t *session, struct lyd_node *config, struct l
|
||||
struct lyd_node *changes, *change;
|
||||
int rc = SR_ERR_OK;
|
||||
|
||||
if (diff && !lydx_find_xpathf(diff, XPATH_KEYSTORE_))
|
||||
if (diff && !lydx_find_xpathf(diff, XPATH_KEYSTORE_ASYM)
|
||||
&& !lydx_find_xpathf(diff, XPATH_KEYSTORE_SYM))
|
||||
return SR_ERR_OK;
|
||||
|
||||
switch (event) {
|
||||
@@ -174,6 +177,9 @@ int keystore_change(sr_session_ctx_t *session, struct lyd_node *config, struct l
|
||||
rc = keystore_update(session, config, diff);
|
||||
break;
|
||||
case SR_EV_CHANGE:
|
||||
if (diff && lydx_find_xpathf(diff, XPATH_KEYSTORE_SYM))
|
||||
rc = interfaces_validate_keys(session, config);
|
||||
break;
|
||||
case SR_EV_ENABLED:
|
||||
break;
|
||||
case SR_EV_ABORT:
|
||||
@@ -186,6 +192,8 @@ int keystore_change(sr_session_ctx_t *session, struct lyd_node *config, struct l
|
||||
if (rename(SSH_HOSTKEYS_NEXT, SSH_HOSTKEYS))
|
||||
ERRNO("Failed switching to new %s", SSH_HOSTKEYS);
|
||||
}
|
||||
if (diff && lydx_find_xpathf(diff, XPATH_KEYSTORE_SYM))
|
||||
interfaces_validate_keys(NULL, config);
|
||||
return SR_ERR_OK;
|
||||
default:
|
||||
return SR_ERR_OK;
|
||||
|
||||
@@ -15,32 +15,46 @@ module infix-crypto-types {
|
||||
revision 2025-02-04 {
|
||||
description "Initial";
|
||||
}
|
||||
|
||||
identity private-key-format {
|
||||
base ct:private-key-format;
|
||||
description
|
||||
"Base key-format identity for private keys.";
|
||||
"Base for Infix private key format extensions.";
|
||||
}
|
||||
|
||||
identity public-key-format {
|
||||
base ct:public-key-format;
|
||||
description
|
||||
"Base key-format identity for public keys.";
|
||||
"Base for Infix public key format extensions.";
|
||||
}
|
||||
|
||||
identity rsa-private-key-format {
|
||||
base private-key-format;
|
||||
base ct:rsa-private-key-format;
|
||||
description
|
||||
"RSA private key in PKCS#1 format (RFC 8017).
|
||||
Used for SSH host keys.";
|
||||
}
|
||||
|
||||
identity ssh-public-key-format {
|
||||
base public-key-format;
|
||||
base ct:ssh-public-key-format;
|
||||
description
|
||||
"SSH public key format (RFC 4253, Section 6.6).
|
||||
Used for SSH host keys.";
|
||||
}
|
||||
|
||||
identity symmetric-key-format {
|
||||
base ct:symmetric-key-format;
|
||||
description
|
||||
"Base for symmetric key format";
|
||||
"Base for Infix symmetric key format extensions.";
|
||||
}
|
||||
identity wifi-preshared-key-format {
|
||||
|
||||
identity passphrase-key-format {
|
||||
base ct:symmetric-key-format;
|
||||
base symmetric-key-format;
|
||||
description
|
||||
"WiFi secret key";
|
||||
"Human-readable passphrase, e.g., WiFi WPA2/WPA3 passwords.";
|
||||
}
|
||||
|
||||
identity x25519-public-key-format {
|
||||
@@ -48,7 +62,7 @@ module infix-crypto-types {
|
||||
base ct:public-key-format;
|
||||
description
|
||||
"X25519 (Curve25519) public key format for Diffie-Hellman key exchange.
|
||||
This is the format used by WireGuard.";
|
||||
This format is used by network protocols such as WireGuard VPN.";
|
||||
}
|
||||
|
||||
identity x25519-private-key-format {
|
||||
@@ -56,15 +70,6 @@ module infix-crypto-types {
|
||||
base ct:private-key-format;
|
||||
description
|
||||
"X25519 (Curve25519) private key format for Diffie-Hellman key exchange.
|
||||
This is the format used by WireGuard.";
|
||||
}
|
||||
|
||||
identity wireguard-symmetric-key-format {
|
||||
base ct:symmetric-key-format;
|
||||
base symmetric-key-format;
|
||||
description
|
||||
"WireGuard pre-shared key format.
|
||||
32-byte base64-encoded key used as an optional additional layer
|
||||
of symmetric encryption for post-quantum resistance.";
|
||||
This format is used by network protocols such as WireGuard VPN.";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ submodule infix-if-wireguard {
|
||||
import ietf-inet-types {
|
||||
prefix inet;
|
||||
}
|
||||
import ietf-crypto-types {
|
||||
prefix ct;
|
||||
}
|
||||
import infix-crypto-types {
|
||||
prefix ixct;
|
||||
}
|
||||
@@ -75,8 +78,8 @@ submodule infix-if-wireguard {
|
||||
|
||||
This provides post-quantum resistance as an attacker would need
|
||||
to break both the Curve25519 key exchange and this symmetric key.";
|
||||
must "derived-from-or-self(deref(.)/../infix-ks:key-format, 'ixct:wireguard-symmetric-key-format')" {
|
||||
error-message "Preshared key must be in wireguard-symmetric-key-format";
|
||||
must "derived-from-or-self(deref(.)/../ks:key-format, 'ct:octet-string-key-format')" {
|
||||
error-message "Preshared key must be in octet-string-key-format";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,15 +5,12 @@ module infix-keystore {
|
||||
import ietf-keystore {
|
||||
prefix ks;
|
||||
}
|
||||
import ietf-crypto-types {
|
||||
prefix ct;
|
||||
}
|
||||
import infix-crypto-types {
|
||||
prefix infix-ct;
|
||||
}
|
||||
|
||||
revision 2025-12-17 {
|
||||
description "Add WireGuard support";
|
||||
description "Add WireGuard support, see infix-crypto-types.yang";
|
||||
}
|
||||
revision 2025-12-10 {
|
||||
description "Adapt to changes in final version of ietf-keystore";
|
||||
@@ -24,59 +21,4 @@ module infix-keystore {
|
||||
revision 2025-02-04 {
|
||||
description "Initial";
|
||||
}
|
||||
deviation "/ks:keystore/ks:asymmetric-keys/ks:asymmetric-key/ks:public-key-format" {
|
||||
deviate replace {
|
||||
type identityref {
|
||||
base infix-ct:public-key-format;
|
||||
}
|
||||
}
|
||||
}
|
||||
deviation "/ks:keystore/ks:asymmetric-keys/ks:asymmetric-key/ks:private-key-format" {
|
||||
deviate replace {
|
||||
type identityref {
|
||||
base infix-ct:private-key-format;
|
||||
}
|
||||
}
|
||||
}
|
||||
deviation "/ks:keystore/ks:symmetric-keys/ks:symmetric-key/ks:key-format" {
|
||||
deviate not-supported;
|
||||
}
|
||||
augment "/ks:keystore/ks:symmetric-keys/ks:symmetric-key" {
|
||||
leaf key-format {
|
||||
type identityref {
|
||||
base infix-ct:symmetric-key-format;
|
||||
}
|
||||
description
|
||||
"Identifies the symmetric key's format
|
||||
|
||||
Valid symmetric key formats are:
|
||||
wifi-preshared-key-format - WiFi preshared key
|
||||
wireguard-symmetric-key-format - WireGuard preshared key";
|
||||
}
|
||||
}
|
||||
deviation "/ks:keystore/ks:symmetric-keys/ks:symmetric-key/ks:key-type/ks:cleartext-symmetric-key" {
|
||||
deviate not-supported;
|
||||
}
|
||||
augment "/ks:keystore/ks:symmetric-keys/ks:symmetric-key/ks:key-type" {
|
||||
case cleartext-symmetric-key {
|
||||
leaf symmetric-key {
|
||||
type string;
|
||||
must "../infix-ks:key-format != 'infix-ct:wifi-preshared-key-format' or " +
|
||||
"(string-length(.) >= 8 and string-length(.) <= 63)" {
|
||||
error-message "WiFi pre-shared key must be 8-63 characters long";
|
||||
}
|
||||
must "../infix-ks:key-format != 'infix-ct:wireguard-symmetric-key-format' or " +
|
||||
"string-length(.) = 44" {
|
||||
error-message "WireGuard pre-shared key must be 44 characters (32-byte base64-encoded)";
|
||||
}
|
||||
description
|
||||
"Cleartext symmetric key value.
|
||||
|
||||
Format depends on key-format:
|
||||
- WiFi pre-shared key: 8-63 printable ASCII characters
|
||||
- WireGuard pre-shared key: 32-byte base64-encoded key (44 chars with padding)";
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,6 +413,20 @@ int infix_groups(kcontext_t *ctx)
|
||||
"| jq -r '.\"ietf-netconf-acm:nacm\".groups.group[].name'");
|
||||
}
|
||||
|
||||
int infix_sym_keys(kcontext_t *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
return shellf("copy running -x /ietf-keystore:keystore/symmetric-keys "
|
||||
"| jq -r '.\"ietf-keystore:keystore\".\"symmetric-keys\".\"symmetric-key\"[].name'");
|
||||
}
|
||||
|
||||
int infix_asym_keys(kcontext_t *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
return shellf("copy running -x /ietf-keystore:keystore/asymmetric-keys "
|
||||
"| jq -r '.\"ietf-keystore:keystore\".\"asymmetric-keys\".\"asymmetric-key\"[].name'");
|
||||
}
|
||||
|
||||
int kplugin_infix_fini(kcontext_t *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
@@ -432,6 +446,8 @@ int kplugin_infix_init(kcontext_t *ctx)
|
||||
kplugin_add_syms(plugin, ksym_new("ifaces", infix_ifaces));
|
||||
kplugin_add_syms(plugin, ksym_new("users", infix_users));
|
||||
kplugin_add_syms(plugin, ksym_new("groups", infix_groups));
|
||||
kplugin_add_syms(plugin, ksym_new("sym_keys", infix_sym_keys));
|
||||
kplugin_add_syms(plugin, ksym_new("asym_keys", infix_asym_keys));
|
||||
kplugin_add_syms(plugin, ksym_new("firewall_zones", infix_firewall_zones));
|
||||
kplugin_add_syms(plugin, ksym_new("firewall_policies", infix_firewall_policies));
|
||||
kplugin_add_syms(plugin, ksym_new("firewall_services", infix_firewall_services));
|
||||
|
||||
@@ -155,6 +155,20 @@
|
||||
<ACTION sym="STRING"/>
|
||||
</PTYPE>
|
||||
|
||||
<PTYPE name="SYM_KEYS">
|
||||
<COMPL>
|
||||
<ACTION sym="sym_keys@infix"/>
|
||||
</COMPL>
|
||||
<ACTION sym="STRING"/>
|
||||
</PTYPE>
|
||||
|
||||
<PTYPE name="ASYM_KEYS">
|
||||
<COMPL>
|
||||
<ACTION sym="asym_keys@infix"/>
|
||||
</COMPL>
|
||||
<ACTION sym="STRING"/>
|
||||
</PTYPE>
|
||||
|
||||
<VIEW name="main">
|
||||
<HOTKEY key="^D" cmd="exit"/>
|
||||
|
||||
@@ -259,6 +273,21 @@
|
||||
</COMMAND>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="wireguard" help="WireGuard VPN key tools" mode="switch">
|
||||
<COMMAND name="genkey" help="Generate a WireGuard private/public key pair">
|
||||
<ACTION sym="script">
|
||||
priv=$(wg genkey)
|
||||
pub=$(printf '%s' "$priv" | wg pubkey)
|
||||
echo "Private: $priv"
|
||||
echo "Public: $pub"
|
||||
</ACTION>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="genpsk" help="Generate a WireGuard pre-shared key">
|
||||
<ACTION sym="script">wg genpsk</ACTION>
|
||||
</COMMAND>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="set" help="Set operations, e.g., current date/time" mode="switch">
|
||||
<COMMAND name="datetime" help="Set current date and time, ISO-8601 format">
|
||||
<PARAM name="current-datetime" ptype="/STRING" help="yyyy-mm-ddThh:mm:ss(Z|+/-hh:mm)"/>
|
||||
@@ -338,6 +367,20 @@
|
||||
</ACTION>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="keystore" help="Show keystore keys">
|
||||
<SWITCH name="subcommands" min="0">
|
||||
<COMMAND name="symmetric" help="Show details for a symmetric key">
|
||||
<PARAM name="name" ptype="/SYM_KEYS" help="Key name"/>
|
||||
<ACTION sym="script" in="tty" out="tty" interrupt="true">show keystore symmetric $KLISH_PARAM_name</ACTION>
|
||||
</COMMAND>
|
||||
<COMMAND name="asymmetric" help="Show details for an asymmetric key">
|
||||
<PARAM name="name" ptype="/ASYM_KEYS" help="Key name"/>
|
||||
<ACTION sym="script" in="tty" out="tty" interrupt="true">show keystore asymmetric $KLISH_PARAM_name</ACTION>
|
||||
</COMMAND>
|
||||
</SWITCH>
|
||||
<ACTION sym="script" in="tty" out="tty" interrupt="true">show keystore</ACTION>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="nacm" help="Show users and NACM status and groups">
|
||||
<SWITCH name="subcommands" min="0">
|
||||
<COMMAND name="group" help="Show details for a specific NACM group">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
import base64
|
||||
import json
|
||||
import argparse
|
||||
import sys
|
||||
@@ -1030,6 +1031,7 @@ class DhcpServer:
|
||||
class Iface:
|
||||
# Class variable to hold routing-enabled interfaces for the current display session
|
||||
_routing_ifaces = set()
|
||||
_keystore = {}
|
||||
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
@@ -3381,6 +3383,131 @@ def show_nacm_user(json):
|
||||
print("For detailed rules, use: show nacm group <name>")
|
||||
|
||||
|
||||
def _keystore_format_name(key_format):
|
||||
"""Simplify key-format to a short display name."""
|
||||
fmt = key_format.split(':')[-1] if ':' in key_format else key_format
|
||||
return fmt.replace('-key-format', '')
|
||||
|
||||
|
||||
def _keystore_decode_symmetric(key):
|
||||
"""Decode a symmetric key value for display."""
|
||||
key_format = key.get('key-format', '')
|
||||
fmt = _keystore_format_name(key_format)
|
||||
b64val = key.get('cleartext-symmetric-key', '')
|
||||
|
||||
if fmt == 'passphrase' and b64val:
|
||||
try:
|
||||
return base64.b64decode(b64val).decode('utf-8')
|
||||
except Exception:
|
||||
return b64val
|
||||
return b64val if b64val else '-'
|
||||
|
||||
|
||||
def _keystore_find_key(keystore, kind, name):
|
||||
"""Find a key by type and name in the keystore."""
|
||||
if kind == 'symmetric':
|
||||
keys = keystore.get('symmetric-keys', {}).get('symmetric-key', [])
|
||||
else:
|
||||
keys = keystore.get('asymmetric-keys', {}).get('asymmetric-key', [])
|
||||
for key in keys:
|
||||
if key.get('name') == name:
|
||||
return key
|
||||
return None
|
||||
|
||||
|
||||
def _keystore_asym_type(key):
|
||||
"""Derive asymmetric key algorithm from key format fields."""
|
||||
for field in ('private-key-format', 'public-key-format'):
|
||||
fmt = key.get(field, '')
|
||||
name = fmt.split(':')[-1] if ':' in fmt else fmt
|
||||
name = name.replace('-private-key-format', '').replace('-public-key-format', '')
|
||||
if name:
|
||||
return name
|
||||
return ''
|
||||
|
||||
|
||||
def show_keystore_detail(keystore, kind, name):
|
||||
"""Display detailed information about a specific key."""
|
||||
key = _keystore_find_key(keystore, kind, name)
|
||||
if not key:
|
||||
print(f'{kind.capitalize()} key "{name}" not found.')
|
||||
return
|
||||
|
||||
print(f"{'name':<{20}}: {name}")
|
||||
if kind == 'symmetric':
|
||||
fmt = _keystore_format_name(key.get('key-format', ''))
|
||||
value = _keystore_decode_symmetric(key)
|
||||
print(f"{'format':<{20}}: {fmt}")
|
||||
print(f"{'value':<{20}}: {value}")
|
||||
else:
|
||||
ktype = _keystore_asym_type(key)
|
||||
if ktype:
|
||||
print(f"{'algorithm':<{20}}: {ktype}")
|
||||
pub_fmt = _keystore_format_name(key.get('public-key-format', ''))
|
||||
if pub_fmt:
|
||||
print(f"{'public key format':<{20}}: {pub_fmt}")
|
||||
pub_key = key.get('public-key', '')
|
||||
if pub_key:
|
||||
print(f"{'public key':<{20}}: {pub_key}")
|
||||
|
||||
|
||||
def show_keystore(json, kind=None, name=None):
|
||||
"""Display keystore keys overview or detail for a specific key."""
|
||||
keystore = json.get("ietf-keystore:keystore", {})
|
||||
if not keystore:
|
||||
print("Keystore is empty.")
|
||||
return
|
||||
|
||||
if kind and name:
|
||||
show_keystore_detail(keystore, kind, name)
|
||||
return
|
||||
|
||||
TABLE_WIDTH = 72
|
||||
|
||||
# Symmetric keys
|
||||
sym_keys_data = keystore.get('symmetric-keys', {}).get('symmetric-key', [])
|
||||
if sym_keys_data:
|
||||
Decore.title("Symmetric Keys", TABLE_WIDTH)
|
||||
table = SimpleTable([
|
||||
Column('NAME', flexible=True),
|
||||
Column('FORMAT'),
|
||||
Column('VALUE', flexible=True)
|
||||
], min_width=TABLE_WIDTH)
|
||||
|
||||
for key in sym_keys_data:
|
||||
name = key.get('name', '')
|
||||
fmt = _keystore_format_name(key.get('key-format', ''))
|
||||
value = _keystore_decode_symmetric(key)
|
||||
table.row(name, fmt, value)
|
||||
|
||||
table.print()
|
||||
|
||||
# Asymmetric keys
|
||||
asym_keys_data = keystore.get('asymmetric-keys', {}).get('asymmetric-key', [])
|
||||
if asym_keys_data:
|
||||
Decore.title("Asymmetric Keys", TABLE_WIDTH)
|
||||
table = SimpleTable([
|
||||
Column('NAME', flexible=True),
|
||||
Column('TYPE'),
|
||||
Column('PUBLIC KEY', flexible=True)
|
||||
], min_width=TABLE_WIDTH)
|
||||
|
||||
for key in asym_keys_data:
|
||||
name = key.get('name', '')
|
||||
ktype = _keystore_asym_type(key)
|
||||
|
||||
pub_key = key.get('public-key', '')
|
||||
if len(pub_key) > 40:
|
||||
pub_key = pub_key[:37] + '...'
|
||||
|
||||
table.row(name, ktype, pub_key)
|
||||
|
||||
table.print()
|
||||
|
||||
if not sym_keys_data and not asym_keys_data:
|
||||
print("Keystore is empty.")
|
||||
|
||||
|
||||
def show_system(json):
|
||||
"""System information overivew"""
|
||||
if not json.get("ietf-system:system-state"):
|
||||
@@ -5369,6 +5496,10 @@ def main():
|
||||
subparsers.add_parser('show-nacm-group', help='Show NACM group details')
|
||||
subparsers.add_parser('show-nacm-user', help='Show NACM user details')
|
||||
|
||||
ks_parser = subparsers.add_parser('show-keystore', help='Show keystore keys')
|
||||
ks_parser.add_argument('-t', '--type', help='Key type (symmetric or asymmetric)')
|
||||
ks_parser.add_argument('-n', '--name', help='Key name')
|
||||
|
||||
subparsers.add_parser('show-ntp', help='Show NTP status') \
|
||||
.add_argument('-a', '--address', help='Show details for specific address')
|
||||
subparsers.add_parser('show-ntp-tracking', help='Show NTP tracking status')
|
||||
@@ -5438,6 +5569,8 @@ def main():
|
||||
show_nacm_group(json_data)
|
||||
elif args.command == "show-nacm-user":
|
||||
show_nacm_user(json_data)
|
||||
elif args.command == "show-keystore":
|
||||
show_keystore(json_data, getattr(args, 'type', None), args.name)
|
||||
elif args.command == "show-ntp":
|
||||
show_ntp(json_data, args.address)
|
||||
elif args.command == "show-ntp-tracking":
|
||||
|
||||
@@ -75,12 +75,12 @@ def configure_server(dut):
|
||||
"symmetric-keys": {
|
||||
"symmetric-key": [{
|
||||
"name": "psk-client1",
|
||||
"infix-keystore:symmetric-key": psk_client1,
|
||||
"infix-keystore:key-format": "infix-crypto-types:wireguard-symmetric-key-format"
|
||||
"cleartext-symmetric-key": psk_client1,
|
||||
"key-format": "ietf-crypto-types:octet-string-key-format"
|
||||
}, {
|
||||
"name": "psk-client2",
|
||||
"infix-keystore:symmetric-key": psk_client2,
|
||||
"infix-keystore:key-format": "infix-crypto-types:wireguard-symmetric-key-format"
|
||||
"cleartext-symmetric-key": psk_client2,
|
||||
"key-format": "ietf-crypto-types:octet-string-key-format"
|
||||
}]
|
||||
}
|
||||
}
|
||||
@@ -226,8 +226,8 @@ def configure_client1(dut):
|
||||
"symmetric-keys": {
|
||||
"symmetric-key": [{
|
||||
"name": "psk-server",
|
||||
"infix-keystore:symmetric-key": psk_client1,
|
||||
"infix-keystore:key-format": "infix-crypto-types:wireguard-symmetric-key-format"
|
||||
"cleartext-symmetric-key": psk_client1,
|
||||
"key-format": "ietf-crypto-types:octet-string-key-format"
|
||||
}]
|
||||
}
|
||||
}
|
||||
@@ -360,8 +360,8 @@ def configure_client2(dut):
|
||||
"symmetric-keys": {
|
||||
"symmetric-key": [{
|
||||
"name": "psk-server",
|
||||
"infix-keystore:symmetric-key": psk_client2,
|
||||
"infix-keystore:key-format": "infix-crypto-types:wireguard-symmetric-key-format"
|
||||
"cleartext-symmetric-key": psk_client2,
|
||||
"key-format": "ietf-crypto-types:octet-string-key-format"
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,12 @@ may be supplied in places where a hostname is otherwise expected.
|
||||
|
||||
Commands:
|
||||
|
||||
scan [-a] [<iface>]
|
||||
Discover Infix devices on the LAN using mDNS-SD (avahi-browse).
|
||||
Use -a to show all mDNS devices, not just Infix. Falls back to
|
||||
IPv6 link-local multicast ping if avahi-browse is not installed
|
||||
and <iface> is specified.
|
||||
|
||||
peer <iface>
|
||||
Return the address of the first IPv6 neighbor to respond on
|
||||
<iface>'s local LAN.
|
||||
@@ -75,6 +81,9 @@ case "$cmd" in
|
||||
help)
|
||||
usage && exit 0
|
||||
;;
|
||||
scan)
|
||||
llscan "$@"
|
||||
;;
|
||||
peer)
|
||||
llpeer "$@"
|
||||
;;
|
||||
|
||||
+152
@@ -144,3 +144,155 @@ llscp()
|
||||
|
||||
$sshpasscmd scp $LLSCP_OPTS "$src" "$dst"
|
||||
}
|
||||
|
||||
# Usage: llscan [-a] [<iface>]
|
||||
#
|
||||
# Discover Infix devices (or all mDNS devices with -a) on the LAN.
|
||||
# Prefers mDNS-SD via avahi-browse; falls back to IPv6 link-local
|
||||
# multicast ping if avahi-browse is not available and an interface
|
||||
# is specified.
|
||||
llscan()
|
||||
{
|
||||
local all=0
|
||||
local iface=
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-a)
|
||||
all=1
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
iface="$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$iface" ] && [ ! -d "/sys/class/net/$iface" ]; then
|
||||
echo "Error: Interface \"$iface\" does not exist." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if command -v avahi-browse >/dev/null 2>&1; then
|
||||
llscan_mdns "$all"
|
||||
elif [ "$iface" ]; then
|
||||
echo "Note: avahi-browse not found, falling back to link-local scan." >&2
|
||||
llscan_ll "$iface"
|
||||
else
|
||||
cat >&2 <<-EOF
|
||||
Error: avahi-browse not found.
|
||||
|
||||
Install avahi-utils to scan for Infix devices via mDNS:
|
||||
sudo apt install avahi-utils
|
||||
|
||||
Or specify an interface to use IPv6 link-local fallback:
|
||||
$(basename $0) scan <iface>
|
||||
EOF
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
llscan_mdns()
|
||||
{
|
||||
local all="$1"
|
||||
local flags="-tarp"
|
||||
|
||||
if avahi-browse --help 2>&1 | grep -q -- '-k'; then
|
||||
flags="-tarpk"
|
||||
fi
|
||||
|
||||
avahi-browse $flags | awk -F';' -v show_all="$all" '
|
||||
$1 == "=" {
|
||||
host = $7
|
||||
proto = $3
|
||||
addr = $8
|
||||
txt = $10
|
||||
|
||||
on = ""; ov = ""; product = ""; serial = ""; devid = ""
|
||||
n = split(txt, parts, "\" \"")
|
||||
for (i = 1; i <= n; i++) {
|
||||
gsub(/"/, "", parts[i])
|
||||
if (parts[i] ~ /^on=/) { split(parts[i], kv, "="); on = kv[2] }
|
||||
else if (parts[i] ~ /^ov=/) { split(parts[i], kv, "="); ov = kv[2] }
|
||||
else if (parts[i] ~ /^product=/) { split(parts[i], kv, "="); product = kv[2] }
|
||||
else if (parts[i] ~ /^serial=/) { split(parts[i], kv, "="); serial = kv[2] }
|
||||
else if (parts[i] ~ /^deviceid=/) { split(parts[i], kv, "="); devid = kv[2] }
|
||||
}
|
||||
|
||||
if (!show_all && on != "Infix") next
|
||||
|
||||
# Use deviceid (MAC) as unique key; fall back to hostname
|
||||
key = devid ? devid : host
|
||||
|
||||
if (!product) product = on ? on : "-"
|
||||
if (!ov) ov = "-"
|
||||
if (!serial || serial == "null") serial = "-"
|
||||
|
||||
# Prefer IPv4 for display address
|
||||
if (proto == "IPv4") {
|
||||
ipv4[key] = addr
|
||||
} else if (proto == "IPv6" && !ipv6[key]) {
|
||||
ipv6[key] = addr
|
||||
}
|
||||
|
||||
if (!seen[key]++) {
|
||||
keys[++ndevs] = key
|
||||
hosts[key] = host
|
||||
products[key] = product
|
||||
versions[key] = ov
|
||||
serials[key] = serial
|
||||
} else if (length(host) > length(hosts[key])) {
|
||||
# Prefer the unique hostname (e.g., infix-c0-ff-ee.local)
|
||||
# over the generic one (e.g., infix.local)
|
||||
hosts[key] = host
|
||||
}
|
||||
}
|
||||
|
||||
END {
|
||||
if (ndevs == 0) {
|
||||
if (show_all)
|
||||
print "No mDNS devices found." | "cat >&2"
|
||||
else
|
||||
print "No Infix devices found. Use -a to show all mDNS devices." | "cat >&2"
|
||||
exit 1
|
||||
}
|
||||
|
||||
fmt = "%-26s %-18s %-24s %-22s %s\n"
|
||||
hdr = sprintf(fmt, "HOSTNAME", "ADDRESS", "PRODUCT", "VERSION", "SERIAL")
|
||||
sub(/\n$/, "", hdr)
|
||||
printf "\033[7m%s\033[0m\n", hdr
|
||||
|
||||
for (i = 1; i <= ndevs; i++) {
|
||||
k = keys[i]
|
||||
a = (ipv4[k] ? ipv4[k] : (ipv6[k] ? ipv6[k] : "-"))
|
||||
p = products[k]; if (length(p) > 23) p = substr(p, 1, 22) "~"
|
||||
v = versions[k]; if (length(v) > 21) v = substr(v, 1, 20) "~"
|
||||
printf fmt, hosts[k], a, p, v, serials[k]
|
||||
}
|
||||
|
||||
printf "\n%d device(s) found.\n", ndevs
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
llscan_ll()
|
||||
{
|
||||
local iface="$1"
|
||||
|
||||
printf "Scanning %s for IPv6 link-local neighbors ...\n\n" "$iface"
|
||||
printf "\033[7m%-6s %s\033[0m\n" "#" "ADDRESS"
|
||||
|
||||
llping "$iface" -c3 -w3 2>/dev/null | awk '
|
||||
/bytes from/ {
|
||||
sub(/:$/, "", $4)
|
||||
addr = $4
|
||||
if (!seen[addr]++) {
|
||||
printf "%-6d %s\n", ++n, addr
|
||||
}
|
||||
}
|
||||
END {
|
||||
printf "\n%d neighbor(s) found on '"$iface"'.\n", n+0
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user