10. August 2018 · Comments Off on Cisco Firepower Management Center (FMC) bulk modifications of policy rules. · Categories: Cisco, Firewall, Linux, Linux Scripts, Networking · Tags: , , , , , , ,

As stated in my previous post, the Cisco Migration tools are very limited and Cisco has not shared any tools publicly that can perform bulk modifications. When migrating to a firepower unless all the previous access rules on the ASA had logging enabled any rule converted will continue to not have logging enabled. This is a major bummer for auditors, security teams or for anyone looking to eventually migrate L4 rules to L7 rules.

Just because I can, the following example script will bulk enable logBegin & sendEventsToFMC on all policy rules on a Firepower (FMC) appliance. This script is intended to be a reference, however modifying policy rules can broadly be broken down into the following steps:

  1. GET the accessrule
  2. Purge the metadata & link value trees from the json
  3. Change, add or remove whatever values you want
  4. PUT the accessrule
#!/bin/bash
## Example script that can bulk modify policy rules on a Cisco FMC.
## Requires: python:PyYAML,shyaml
## 2018 (v.01) - Script from www.davideaves.com
 
firepower="192.0.2.10"
username="apiuser"
password="api1234"
 
# Convert JSON to YAML
j2y() {
 python -c 'import sys, yaml, json; yaml.safe_dump(json.load(sys.stdin), sys.stdout, default_flow_style=False)' 2> /dev/null
}
 
# Convert YAML to JSON
y2j() {
 python -c 'import sys, yaml, json; y=yaml.load(sys.stdin.read()); print json.dumps(y)' 2> /dev/null
}
 
# Pop forbidden put values (metadata, links)
jpop() {
 python -c 'import sys, json; j=json.load(sys.stdin); j.pop("metadata"); j.pop("links"); print json.dumps(j)' 2> /dev/null
}
 
fmcauth() {
 ### Post credentials and eval header return data.
 
 if [[ -z "${auth_epoch}" || "${auth_epoch}" -lt "$(($(date +%s) - 1500))" ]]
  then
 
   if [ -z "${X_auth_access_toke}" ]
    then eval "auth_epoch=$(date +%s)"
        eval "$(curl -skX POST https://${firepower}/api/fmc_platform/v1/auth/generatetoken \
          -H "Authorization: Basic $(printf "${username}:${password}" | base64)" -D - |\
          awk '/(auth|DOMAIN|global)/{gsub(/[\r|:]/,""); gsub(/-/,"_",$1); print $1"=\""$2"\""}')"
    else eval "auth_epoch=$(date +%s)"
         eval "$(curl -skX POST https://${firepower}/api/fmc_platform/v1/auth/refreshtoken \
          -H "X-auth-access-token: ${X_auth_access_token}" \
          -H "X-auth-refresh-token: ${X_auth_refresh_token}" -D - |\
          awk '/(auth|DOMAIN|global)/{gsub(/[\r|:]/,""); gsub(/-/,"_",$1); print $1"=\""$2"\""}')"
   fi
 
 fi
}
 
# Get a list of all access control policies.
fmcauth && curl -skX GET https://${firepower}/api/fmc_config/v1/domain/${DOMAIN_UUID}/policy/accesspolicies \
 -H "X-auth-access-token: ${X_auth_access_token}" | j2y | shyaml get-value items | awk '/self:/{print $NF}' | while read POLICY
do fmcauth
 
   # Get a list of all access rules in each policy.
   curl -skX GET ${POLICY}/accessrules?limit=10000 -H "X-auth-access-token: ${X_auth_access_token}" |\
   j2y | shyaml get-value items 2> /dev/null | awk '/self:/{print $NF}' | while read RULE
   do
 
      # ID of the access rule.
      UUID="$(basename ${RULE})"
 
      # Collect the access rule resource and pop forbidden values.
      RESOURCE=`curl -skX GET ${RULE} -H "X-auth-access-token: ${X_auth_access_token}" | jpop`
 
      # Modify only if resource exists.
      if [[ ${RESOURCE} != *"Resource not found."* ]]
       then echo -ne "$(date) - PUT:${UUID} - MSG: "
 
       # Use sed to modify values in payload before putting back to FMC.
       curl -skX PUT ${RULE} \
        -H "Content-Type: application/json" \
        -H "X-auth-access-token: ${X_auth_access_token}" \
        -d "$(echo ${RESOURCE} | j2y | sed 's/logBegin: false/logBegin: true/;s/sendEventsToFMC: false/sendEventsToFMC: true/' | y2j)" && echo
      fi
 
   done
 
done

About a year ago I wrote two F5 scripts for mapping and filtering VIPs on an F5 BigIP. I use those scripts often and the f5filter script I continue to tweak/correct minor bugs; which I post in the comments. The following script is cut from same cloth as the previous 2 scripts, except this script search for an F5 VIP by Node; even if the VIP directs traffic to the node via a Policy or iRule.

f5node.sh: Bourne-Again shell script text executable

#!/bin/bash
## Reverse filter a single Node on a BigIP.
## 2017 (v1.0) - Script from www.davideaves.com
 
F5CONFIG="$1"
F5NODE="$2"
 
# Fix broken TERM variable if using screen.
[ "$TERM" == "screen-256color" ] && { export TERM="xterm-256color"; }
 
# FUNCTION: End Script if error.
DIE() {
 echo "ERROR: Validate \"$_\" is installed and working on your system."
 exit 0
}
 
# ANSI color variables.
esc="$(echo -e '\E')";  cCLS="${esc}[0m"
cfBLACK="${esc}[30m";   cbBLACK="${esc}[40m"
cfRED="${esc}[31m";     cbRED="${esc}[41m"
cfGREEN="${esc}[32m";   cbGREEN="${esc}[42m"
cfYELLOW="${esc}[33m";  cbYELLOW="${esc}[43m"
cfBLUE="${esc}[34m";    cbBLUE="${esc}[44m"
cfMAGENTA="${esc}[35m"; cbMAGENTA="${esc}[45m"
cfCYAN="${esc}[36m";    cbCYAN="${esc}[46m"
cfWHITE="${esc}[37m";   cbWHITE="${esc}[47m"
c1BOLD="${esc}[1m";     c0BOLD="${esc}[22m"
 
### Print Syntax if arguments are not provided. ###
if [ ! -e "$F5CONFIG" ] || [ -z "$F5NODE" ]
 then
 echo "Usage: $0 bigip.conf 10.1.1.10"
 exit 0;
fi
 
### Check to see if we are running this on an F5. ###
type -p tmsh > /dev/null && if [ "$(cat /var/prompt/ps1)" == "Active" ]
  then STATE=$cbGREEN
  else STATE=$cbRED
fi
 
### The function that does all the filtering. ###
F5FILTER() {
 if [[ "$(file "$F5CONFIG")" == *"ASCII"* ]]
  then cat "$F5CONFIG"
 elif [[ "$(file "$F5CONFIG")" == *"gzip compressed data"* ]]
  then tar -xOvf "$F5CONFIG" config/bigip.conf 2> /dev/null
 fi | sed -n -e '/^ltm '"$(echo $F5STANZA)"'.*/,/^}$/p' | sed 's/^}/}|/g' | tr -d '[:cntrl:]' |\
  sed 's/|/\n/g;s/ \{1,\}/ /g' | grep "$F5DIG"
}
 
### Display status icon back to the user. ###
STATUS() {
 if    [ "$STATUS" == "available enabled" ]; then printf -- "($c0BOLD$cfBLACK$cbGREEN@$cCLS) "
  elif [ "$STATUS" == "available disabled" ]; then printf -- "($c0BOLD$cfBLACK$cbBLUE@$cCLS) "
  elif [ "$STATUS" == "unknown enabled" ]; then printf -- "[$c0BOLD$cfBLACK$cbBLUE#$cCLS] "
  elif [ "$STATUS" == "offline disabled" ]; then printf -- "< $c0BOLD$cfBLACK$cbWHITE-$cCLS> "
  elif [ "$STATUS" == "offline enabled" ]; then printf -- "< $c0BOLD$cfBLACK$cbRED!$cCLS> "
  elif [ "$STATUS" == "available disabled-by-parent" ]; then printf -- "($c1BOLD$cfBLACK$cbWHITE@$cCLS) "
  elif [ "$STATUS" == "unknown disabled-by-parent" ]; then printf -- "[$c1BOLD$cfBLACK$cbWHITE#$cCLS] "
  elif [ "$STATUS" == "offline disabled-by-parent" ]; then printf -- "< $c1BOLD$cfBLACK$cbWHITE-$cCLS> "
  else printf "[ $STATUS ] "
 fi
}
 
### Display the final result back to the user. ###
PRINT() {
 if [ ! -z "$STATE" ]
  then ### We are running this script on the F5. ###
   printf "$STATE$cfBLACK$(cat /var/prompt/hostname)$cCLS "
 
   # Collect status of VIRTUAL and print.
   printf ">>> "
   STATUS="$(tmsh show ltm virtual $VIRTUAL 2> /dev/null | grep -e "Availability" -e "State" | awk -F':' '{printf $NF" "}' | sed 's/ \+/ /g;s/^[ \t]*//;s/[ \t]*$//')"
   STATUS
   printf "$c1BOLD$VIRTUAL$cCLS < << $cf$POLICY$cCLS\n  "
 
   # Collect status of POOL and print.
   STATUS="$(tmsh show ltm pool $POOL 2> /dev/null | grep -e "Availability" -e "State" | awk -F':' '{printf $NF" "}' | sed 's/ \+/ /g;s/^[ \t]*//;s/[ \t]*$//')"
   STATUS
   printf "$cfCYAN$POOL$cCLS\n"
 
   # Collect status of Nodes and print.
   for NODE in $NODES;
    do STATUS="$(tmsh show ltm node $NODE 2> /dev/null | grep -e "Availability" -e "State" | awk -F':' '{printf $NF" "}' | sed 's/ \+/ /g;s/^[ \t]*//;s/[ \t]*$//')"
       STATUS && printf "$NODE "
   done | sed 's/\] /\]_/g' | xargs -n4 | column -t | awk '{print "  ",$0}' |\
   sed 's/'"$F5NODE"'/'"$cfYELLOW$F5NODE$cCLS "'/g;s/\]_/\] /g'
 
 else ### We are not Running on an F5 ###
   printf ">>> $c1BOLD$VIRTUAL$cCLS < << $cf$POLICY$cCLS\n  $cfCYAN$POOL$cCLS\n"
   printf "$NODES\n" | xargs -n4 | column -t | awk '{print "  ",$0}' |\
   sed 's/'"$F5NODE"'/'"$cfYELLOW$F5NODE$cCLS "'/g'
 fi; echo
}
 
# Main Loop.
F5STANZA="pool"
F5DIG="$F5NODE "
 
for i in $(F5FILTER)
 do
  if [ "$i" == "pool" ]
   then echo; FLAG="$i"
  elif [ "$i" == "address" ]
   then FLAG="$i"
  elif [ ! -z "${FLAG}" ]
   then printf "$i " | sed 's/^\/.*\///'
   unset FLAG
  fi
done | grep -v ^$ | while read POOL NODES
 do
 
  # Dig polices and iRules binding the pool to VIPs.
  for F5STANZA in policy profile rule
   do F5DIG="$POOL"
 
      if [ "$F5STANZA" == "policy" ]
       then COLOR="$c1BOLD$cfGREEN"
      elif [ "$F5STANZA" == "rule" ]
       then COLOR="$c0BOLD$cfGREEN"
      else
       COLOR="$c0BOLD$cfMAGENTA"
      fi
 
      F5FILTER | sed 's/profile httpclass/httpclass/g' | awk '{print $3}' | sed 's/\/.*\///g' | while read POLICY;
       do F5STANZA="virtual "
          F5DIG="$POLICY"
          F5FILTER | awk '{print $3}' | sed 's/\/.*\///g' | while read VIRTUAL
            do POLICY="$COLOR$POLICY"
               PRINT
          done
      done
  done
 
  # Dig VIPs that are bound to the pool.
  F5STANZA="virtual "
  F5DIG="$POOL"
  F5FILTER | awk '{print $3}' | sed 's/\/.*\///g' | while read VIRTUAL
   do PRINT
  done
 
done