Skip to main content

NQE - Cisco / Arista - Check for No Local Username

  • January 17, 2024
  • 1 reply
  • 111 views
  • Translate

RobertWelch
Employee
Forum|alt.badge.img

How does one access a Cisco or Arista device if TACACS+ or RADIUS is unavailable?  A local username is required.  What happens when your security clean up work goes a bit too far and removes all local usernames?  This quick NQE configuration check verifies that there is at least one local username.  (See the other NQE examples posted in this Community for configuration pattern matching).  

/**
 * @intent Find all Cisco and Arista devices where there is no local username configured.
 * @description Local usernames are used when TACACS+ or RADIUS is not accessible.  Find all Cisco and Arista devices where ** no ** "username" is configured.
 */

pattern = ```
username {user:string}
```;

foreach device in network.devices
where device.platform.vendor == Vendor.CISCO ||
      device.platform.vendor == Vendor.ARISTA ||
      device.platform.vendor == Vendor.HP
let platform = device.platform
where isPresent(platform.model)
select {
  violation: length(blockMatches(device.files.config, pattern)) < 1,
  name: device.name,
  vendor: platform.vendor,
  model: platform.model,
  os: platform.os,
  osVer: platform.osVersion
}

The following NQE, attributed to another Forward Networks engineer, allows one to list non-compliant usernames.  Search for username = admin.  If the username does not exist, indicate a violation and list the local username configured.  
 

/**
 * @intent Search Cisco Configuration for usernames that are not well known
 * @description To ensure consistent configuration, search the Cisco devices for usernames that are not well known, or outlyers. Report as Violations.
 */

Expected_Usernames = ["admin"];

// Check if the username exists
check_username(Device_Name) =
  foreach device in network.devices
  where device.name == Device_Name
  let configured_usernames = (foreach line in device.files.config
							  let configured_usernames = patternMatch(line.text, `username {username:string}`)
  							  where isPresent(configured_usernames) && isPresent(configured_usernames.username)
    						  select configured_usernames.username)

  let missing_users = configured_usernames - Expected_Usernames
  where length(missing_users) >0 
  select { Device_Name: Device_Name, User_List: missing_users, Info: "UNKNOWN_USER" };


// main
foreach device in network.devices
where device.platform.vendor == Vendor.CISCO || device.platform.vendor == Vendor.ARISTA
let non_compliant_devices = check_username(device.name)
foreach item in non_compliant_devices
select {violation: true, Device_Name: item.Device_Name, User_List: item.User_List, Info: item.Info}

 

Since we are on the topic of TACACS+ and checking configuration compliance, here is one more easy one.  Let’s check to see if TACACS+ is properly configured on Cisco or Arista devices.  Edit the IP addresses as appropriate for your environment.  A violation is true when the tacacServer string does not match the IP addresses in the list.  This NQE could be used for NQE Verification.  Meaning that this NQE is executed when the Snapshot is evaluated.  Flag the NQE as “Add to Verify” to execute during Snapshot processing. 

// Note that this NQE finds all lines.  Some systems use multiple lines to configure the TACACS+ server values. The result can be two or more lines of output.
// Documentation on Block Patterns:
// https://fwd.app/docs/nqe/guides/block-patterns/
// TACACS Configuration Example:
// https://fwd.app/docs/nqe/guides/data-extraction/
// ----------------------------------
tacacsPattern =
  ```
tacacs server {server:string}
  address ipv4 {serverIP:string}
```;

//
IOSTACACS =
  foreach device in network.devices
  /// Uncomment the next two lines to match only CISCO or ARISTA vendors.
  // where device.platform.vendor == Vendor.CISCO ||
  //      device.platform.vendor == Vendor.ARISTA
  foreach line in device.files.config
  let match = patternMatch(line.text, `tacacs-server host {serverIP:string}`)
  where isPresent(match)
  let platform = device.platform
  select {
    deviceName: device.name,
    tacacsServer: match.serverIP,
    vendor: platform.vendor,
    model: platform.model,
    os: platform.os,
    osVersion: platform.osVersion,
    managementIps: platform.managementIps
  };

// ----------------------------------
IOSXETACACS =
  foreach device in network.devices
  /// Uncomment the next two lines to match only CISCO or ARISTA vendors.
  // where device.platform.vendor == Vendor.CISCO ||
  //      device.platform.vendor == Vendor.ARISTA
  let matchData = blockMatches(device.files.config, tacacsPattern)
  foreach line in matchData
  let match = line.data
  where isPresent(match)
  let platform = device.platform
  select {
    deviceName: device.name,
    tacacsServer: match.serverIP,
    vendor: platform.vendor,
    model: platform.model,
    os: platform.os,
    osVersion: platform.osVersion,
    managementIps: platform.managementIps
  };

allTACACS = IOSTACACS + IOSXETACACS;

foreach x in allTACACS
select {
  violation: x.tacacsServer not in ["10.5.5.10", "10.5.10.10"],
  deviceName: x.deviceName,
  tacacsServer: x.tacacsServer,
  vendor: x.vendor,
  model: x.model,
  os: x.os,
  osVersion: x.osVersion,
  managementIps: x.managementIps
}

 

1 reply

Forum|alt.badge.img+1
  • Employee
  • 55 replies
  • January 18, 2024

One of the things you might consider is to leverage the set evaluation instead of using `||` and you can add matches to the output to see what `username` entries exist.
 

pattern = ```
username {user:string}
```;

foreach device in network.devices
let vendor = device.platform.vendor
where vendor in [Vendor.CISCO, Vendor.ARISTA, Vendor.HP]
let platform = device.platform
where isPresent(platform.model)
let matches = blockMatches(device.files.config, pattern)
select {
  violation: length(matches) == 0 ,
  name: device.name,
  vendor: platform.vendor,
  model: platform.model,
  os: platform.os,
  osVer: platform.osVersion,
  matches: foreach match in matches select match.data
}

 

Translate

Reply


Cookie policy

We use cookies to enhance and personalize your experience. If you accept you agree to our full cookie policy. Learn more about our cookies.

 
Cookie settings