Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

YANG Config Guide

tacacsrs-config provides TACACS+ configuration support using the RFC 7951 JSON encoding of the ietf-system-tacacs-plus YANG module. The crate owns parsing, generated model types, validation, local credential bundle enumeration, and helper APIs used by tacon and tacacsrs-agentd.

Use this guide when you need to author, validate, or consume TACACS+ server configuration from JSON.

What the configuration crate provides

  • Generated Rust types that mirror the expanded TACACS+ YANG tree.
  • RFC 7951 JSON parsing into the generated TACACS+ model.
  • Validation for required server data, unique addresses, TLS choice constraints, SNI requirements, key format identities, base64-encoded key material, and config-local credential references.
  • Bundle enumeration helpers that inline local client-credentials and server-credentials references onto server entries.
  • A TacacsPlusServerBuilder for constructing valid server definitions in Rust.
  • A project-owned TACACS+/TLS augmentation for TLS 1.3 PSK-DHE key exchange group selection.

External secret providers are intentionally kept outside this crate. Parse and enumerate configuration first, then pass the resulting server values to a runtime or provider layer that can materialize external secrets.

Basic JSON shape

Both tacon and tacacsrs-agentd can load TACACS+ server definitions from an RFC 7951 JSON document:

{
  "ietf-system-tacacs-plus:tacacs-plus": {
    "server": [
      {
        "name": "primary",
        "server-type": "authentication authorization accounting",
        "address": "192.0.2.2",
        "port": 49,
        "shared-secret": "example-shared-secret",
        "timeout": 10
      }
    ]
  }
}

Use --config <file> with tacon or tacacsrs-agentd to load the configuration.

Parsing API

For simple application code, parse the JSON and enumerate runtime server entries:

#![allow(unused)]
fn main() {
use tacacsrs_config::{enumerate_servers, parse_yang_json};

let config = parse_yang_json(json_str)?;
let servers = enumerate_servers(&config)?;
anyhow::Ok::<()>(())
}

The primary entry points are:

FunctionPurpose
parse_yang_json(&str)Parse and structurally validate a JSON string without mutating credential references.
parse_yang_json_file(&Path)Parse a JSON file from disk.
validate_credential_references(&TacacsPlus)Validate config-local credential bundle references.
enumerate_servers(&TacacsPlus)Inline shared credential bundles onto every server.
enumerate_server(&TacacsPlus, &str)Inline shared credential bundles for one named server.

The module-oriented API is also available for callers that want grouped imports:

  • builders - programmatic construction helpers.
  • model - generated YANG model types and namespaces.
  • extensions - helper traits layered over generated model types.
  • pipeline - step-by-step parsing and processing.
  • runtime - bundle enumeration helpers.
  • stats - runtime statistics types.

Three-layer processing model

The crate separates parsing and runtime preparation into three layers.

Raw YANG model

Use the raw pipeline when you need the submitted YANG model preserved exactly for round-tripping or reporting:

#![allow(unused)]
fn main() {
use tacacsrs_config::pipeline;

let raw_config = pipeline::parse_root_json(json_str)?;
anyhow::Ok::<()>(())
}

Config-local bundle enumeration

Credential references may point to bundles in the same configuration. Validate those references, then enumerate the selected server:

#![allow(unused)]
fn main() {
use tacacsrs_config::{enumerate_server, parse_yang_json, validate_credential_references};

let config = parse_yang_json(json_str)?;
validate_credential_references(&config)?;

let server = enumerate_server(&config, "primary")?;
anyhow::Ok::<()>(())
}

External secret resolution

External secret material should be resolved after enumeration by a separate runtime or provider layer. That keeps the generated configuration model safe for logging, reporting, and round-tripping while runtime code receives normalized server values.

Programmatic server construction

Use TacacsPlusServerBuilder when code needs to construct server definitions directly instead of parsing RFC 7951 JSON:

#![allow(unused)]
fn main() {
use tacacsrs_config::{TacacsPlusServerBuilder, TacacsPlusServerExt, TacacsPlusServerType};

let server = TacacsPlusServerBuilder::new(
    "primary",
    TacacsPlusServerType::ACCOUNTING,
    "192.0.2.10",
    49,
)
.with_timeout(10)
.with_shared_secret("example-shared-secret")
.build();

assert_eq!(server.socket_address(), "192.0.2.10:49");
assert!(server.is_obfuscation());
anyhow::Ok::<(), anyhow::Error>(())
}

The builder is for runtime construction of valid server shapes. It is not a replacement for schema validation of submitted YANG JSON.

Inline key format identities

TLS inline key material uses YANG identityref fields from ietf-crypto-types. RFC 7951 JSON represents those values as module-qualified strings.

Private key format

JSON valueMeaning
ietf-crypto-types:rsa-private-key-formatDER-encoded RSAPrivateKey.
ietf-crypto-types:ec-private-key-formatDER-encoded ECPrivateKey.
ietf-crypto-types:one-asymmetric-key-formatDER-encoded CMS OneAsymmetricKey.

Public key format

JSON valueMeaning
ietf-crypto-types:subject-public-key-info-formatDER-encoded SubjectPublicKeyInfo.
ietf-crypto-types:ssh-public-key-formatSSH public key format.

The TACACS+ YANG model constrains TLS client identity and server authentication paths to subject-public-key-info-format.

Symmetric key format

JSON valueMeaning
ietf-crypto-types:octet-string-key-formatRaw octet string.
ietf-crypto-types:one-symmetric-key-formatDER-encoded CMS OneSymmetricKey.

TLS with inline certificate material

{
  "ietf-system-tacacs-plus:tacacs-plus": {
    "server": [
      {
        "name": "tls-inline",
        "server-type": "authentication",
        "address": "192.0.2.1",
        "port": 49,
        "domain-name": "tacacs.example.com",
        "sni-enabled": true,
        "client-identity": {
          "certificate": {
            "inline-definition": {
              "public-key-format": "ietf-crypto-types:subject-public-key-info-format",
              "public-key": "BASE64VALUE=",
              "private-key-format": "ietf-crypto-types:rsa-private-key-format",
              "cleartext-private-key": "BASE64VALUE=",
              "cert-data": "BASE64VALUE="
            }
          }
        },
        "server-authentication": {
          "ca-certs": {
            "inline-definition": {
              "certificate": [
                {
                  "name": "CA-1",
                  "cert-data": "BASE64VALUE="
                }
              ]
            }
          }
        }
      }
    ]
  }
}

TACACS+/TLS PSK-DHE augmentation

The repository includes a local tacacsrs YANG module that augments TACACS+ TLS 1.3 EPSK configuration with psk-dhe-ke-groups. RFC 7951 JSON uses the module-qualified key tacacsrs:psk-dhe-ke-groups:

{
  "ietf-system-tacacs-plus:tacacs-plus": {
    "server": [
      {
        "name": "tls-psk-dhe",
        "server-type": "accounting",
        "address": "192.0.2.10",
        "port": 49,
        "client-identity": {
          "tls13-epsk": {
            "inline-definition": {
              "cleartext-symmetric-key": "BASE64VALUE="
            },
            "external-identity": "client@example.com",
            "tacacsrs:psk-dhe-ke-groups": [
              "x25519",
              "secp256r1",
              "ffdhe3072"
            ]
          }
        }
      }
    ]
  }
}

Supported group values are x25519, secp256r1, secp384r1, secp521r1, ffdhe2048, ffdhe3072, ffdhe4096, ffdhe6144, and ffdhe8192. Unknown values fail during JSON deserialization.

Code generation workflow

The generated Rust types come from the checked-in YANG tooling under libraries/tacacsrs_config/yang/:

  • plugins/yang2rust.py emits Rust structs, enums, bitflags, and helper methods.
  • expand_yang_tree.py refreshes the expanded tree reference and generated Rust output.
  • modules/ contains project-owned YANG modules passed to pyang alongside the upstream TACACS+ model.
  • generated_types.rs is copied into src/generated.rs after regeneration.

Regenerate after YANG module updates:

python -m pip install -r requirements.txt

cd libraries/tacacsrs_config/yang
python expand_yang_tree.py --list-features
python expand_yang_tree.py --list-features --list-features-format ini > feature-flags.ini
python expand_yang_tree.py --features-ini feature-flags.ini > expanded-tree.txt
python expand_yang_tree.py \
  -f rust \
  --features-ini feature-flags.ini \
  -o generated_types.rs
cp generated_types.rs ../src/generated.rs

Run formatting, clippy, build, and tests after regeneration.