<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic Re: Scheduled backup of ASA multicontext configuration in Network Security</title>
    <link>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234753#M1118082</link>
    <description>&lt;P&gt;hm, strange, OK. I think I need just install rconfig, because match every&amp;nbsp;&amp;nbsp;SSH algorithms netmiko with different asa software it's a little inconvinient&lt;/P&gt;</description>
    <pubDate>Tue, 10 Dec 2024 09:08:10 GMT</pubDate>
    <dc:creator>dijix1990</dc:creator>
    <dc:date>2024-12-10T09:08:10Z</dc:date>
    <item>
      <title>Scheduled backup of ASA multicontext configuration</title>
      <link>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5126993#M1113404</link>
      <description>&lt;P&gt;Hi,&lt;/P&gt;&lt;P&gt;I wrote the Python script shown below to backup multiple ASA firewalls in single or multiple context mode, with and without failover. If ASA is in failover mode, then also config from standby unit is backed up. Normally this should be identical, but sometimes Cisco developers screw up their code ...&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="python"&gt;#!/usr/bin/env python3.11
# -*- coding: utf-8 -*-

# ----------------------------------------------------------------------------
# Backup of Cisco ASA firewalls with multiple contexts.
# ----------------------------------------------------------------------------
#
#
# Requires a backup user with the following minimal privileges on the
# firewall admin and system contexts:
#
#     changeto context admin
#     enable password ********** level 6
#     username backup privilege 6
#     username backup attributes
#      ssh authentication publickey ThEeNcRyPtEdSsHpUbLiCkEy==
#
#     changeto admin
#     privilege show level 6 command mode
#     privilege show level 6 command version
#     privilege show level 6 command context
#     privilege show level 6 command interface
#     privilege cmd level 6 command changeto
#
#     changeto system
#     privilege show level 6 mode exec command tech-support
#     privilege cmd level 6 command backup
#     privilege cmd level 6 command delete
#     privilege cmd level 6 mode exec command copy
#
# The SSH authentication public key is from the backup user on backup host.
# Before running the backup command on the firewall, first run a
# a normal copy command to the scp destination to add the SSH public host key.


# ----------------------------------------------------------------------------
# Constants
# ----------------------------------------------------------------------------

CFG_DEFAULTFILE = "~/.asa_backup.yaml"



# ----------------------------------------------------------------------------
# Import Libraries
# ----------------------------------------------------------------------------
# Netmiko: https://github.com/ktbyers/netmiko

import argparse
import yaml
import os
import re
import socket
import subprocess
import sys

from datetime import datetime
from netmiko import ConnectHandler
from pprint import pprint



# ----------------------------------------------------------------------------
# is_resolvable
# ----------------------------------------------------------------------------
# Returns True if the hostname is resolvable via DNS. False otherwise.
#
def is_resolvable(host):
    try:
        socket.gethostbyname(host)
        return True
    except socket.error:
        return False



# ----------------------------------------------------------------------------
# is_host_reachable
# ----------------------------------------------------------------------------
# Check if a host is reachable.
# Returns: True if the host is reachable, False otherwise
#
def is_host_reachable(host):
    result = subprocess.run(['ping', '-c', '1', host],
        stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    return result.returncode == 0



# ----------------------------------------------------------------------------
# read_yamlfile
# ----------------------------------------------------------------------------
# Reads YAML file given as argument. Returns dict with the data.
#
def read_yamlfile(file_path):
    try:
        with open(file_path, 'r') as file:
            data = yaml.safe_load(file)
        return data
    except yaml.YAMLError as e:
        print(f"YAML format error: {e}")
        sys.exit(1)



# ----------------------------------------------------------------------------
# read_configfile
# ----------------------------------------------------------------------------
# Read configuration file in YAML format and set default values if not
# defined. Simplifies further code and error checking. :-)
# Returns a dict with the configuration parameters.
#
def read_configfile(file_path):
    if not file_path:
        file_path = os.path.expanduser(CFG_DEFAULTFILE)
    if not os.path.exists(file_path):
        sys.exit(f"Config file {file_path} does not exist!")
    if not os.access(file_path, os.R_OK):
        sys.exit(f"Config file {file_path} is not readable!")
    cfg = read_yamlfile(file_path)
    for fw in cfg["firewalls"]:
        for key in cfg["defaults"]:
            if key not in cfg["firewalls"][fw]:
                cfg["firewalls"][fw][key] = cfg["defaults"][key]
    return cfg



# ----------------------------------------------------------------------------
# validate_firewalls
# ----------------------------------------------------------------------------
# Validate commandline argument firewalls. If empty returns full list of
# firewalls defined in configuration file. Aborts if firewalls given are not
# listed in config file. Returns list of firewalls.
#
def validate_firewalls(cfg, args_firewalls):
    firewalls = []
    if not args_firewalls:
        firewalls = cfg["firewalls"].keys()
    elif set(args_firewalls).issubset(set(cfg["firewalls"].keys())):
        firewalls = args_firewalls
    elif len(args_firewalls) == 1 and args_firewalls[0] == "all":
        firewalls = cfg["firewalls"].keys()
    else:
        missing = set(args_firewalls) - set(cfg["firewalls"].keys())
        sys.exit(f"Firewalls not allowed: {missing}")
    return list(set(firewalls))



# ----------------------------------------------------------------------------
# get_retention_slot
# ----------------------------------------------------------------------------
# Define retention filename slot for simple retention algorithm:
# Daily up to 7 per week
# Monthly up to 12 per year on 1st day of month.
# Yearly up to forever on 1st of January
# Run script daily after midnight.
#
def get_retention_slot():
    dt = datetime.now()
    if dt.day == 1 and dt.month == 1:
        slot = "yearly_{}".format(dt.year)
    elif dt.day == 1:
        slot = "monthly_{}".format(dt.month)
    else:
        slot = "daily_{}".format(dt.weekday())
    return(slot)



# ----------------------------------------------------------------------------
# get_version
# ----------------------------------------------------------------------------
# Returns the ASA software version as dict with major, minor, maintenance
# and interim.
# Example: Cisco Adaptive Security Appliance Software Version 9.16(3)23
# major = 9, minor = 16, maintenance = 3, interim = 23
#
def get_version(conn):
    output = conn.send_command("show version | include ^Cisco.*Appliance.*Version")
    digits = re.findall(r'\d+', output)
    version = {
        "major":       int(digits[0]),
        "minor":       int(digits[1]),
        "maintenance": int(digits[2]),
        "interim":     int(digits[3])
    }
    return(version)



# ----------------------------------------------------------------------------
# get_context_mode
# ----------------------------------------------------------------------------
# Queries context mode of the ASA firewall. Change to system context if ASA
# has multiple contexts. Returns context mode (single, multiple).
#
def get_context_mode(conn):
    pattern = re.compile(r'Security context mode: (single|multiple)')
    output = conn.send_command("show mode")
    match = pattern.search(output)
    return(match.group(1))



# ----------------------------------------------------------------------------
# get_failover_units
# ----------------------------------------------------------------------------
#
def get_failover_units(conn):
    units = [ "active" ]
    output = conn.send_command("show failover | include ^Failover (On|Off)")
    if re.match(r'^Failover On', output):
        units.append("standby")
    return(units)



# ----------------------------------------------------------------------------
# get_contexts
# ----------------------------------------------------------------------------
# Show contexts, parse output, append context names into list contexts.
# Returns list of context names, starting with system.
#
def get_contexts(conn):
    pattern = re.compile(r'^[ \*]([A-Za-z0-9\-]+)')
    contexts = [ "system" ]
    output = conn.send_command("show context")
    for line in output.split('\n'):
        if match := pattern.search(line):
            contexts.append(match.group(1))
    return(contexts)



# ----------------------------------------------------------------------------
# get_interface_hack
# ----------------------------------------------------------------------------
# In single context mode, when accessing ASA through a VPN tunnel and doing a
# copy command, the ASA uses the public interface IP as source address. The
# copy command then fails because traffic is not encrypted through the VPN
# tunnel. Appending the option ";int=inside" is an undocumented hack to use
# the inside IP address instead. Works only for the copy command, not for
# the backup command.
#
def get_interface_hack(conn):
    ihack = ""
    output = conn.send_command("show interface inside | include ^Interface")
    if re.match(r'^Interface.*inside.*is up', output):
        ihack = ";int=inside"
    return(ihack)



# ----------------------------------------------------------------------------
# run_batch_commands
# ----------------------------------------------------------------------------
# Run array of commands on active ASA (default) or on standby unit.
#
def run_batch_commands(conn, commands, unit="active"):
    for command in commands:
        if unit == "standby":
            command = f"failover exec {unit} {command}"
        output = conn.send_command(command)
        print(output)



# ----------------------------------------------------------------------------
# copy_tech_support
# ----------------------------------------------------------------------------
# Sending tech-support directly to scp path fails. Copy first to flash disk,
# then copy, then delete.
#
def copy_tech_support(conn, unit, backup_url, ihack):
    print(f"Collecting tech-support on {unit} unit  ...")

    file = f"tech-support_{unit}.txt"
    commands = [
        f"show tech-support file flash:/{file}",
        f"copy /noconfirm flash:/{file} {backup_url}/{file}{ihack}",
        f"delete /noconfirm flash:/{file}"
    ]
    run_batch_commands(conn, commands, unit)
    return



# ----------------------------------------------------------------------------
# copy_config
# ----------------------------------------------------------------------------
# Copy running-config and startup-config to backup_url. In single mode it is
# the entire configuration in multiple context mode it is only the system
# context.
#
def copy_config(conn, unit, backup_url, ihack, contexts):
    print(f"Collecting config on {unit} unit ...")

    commands = [
      f"copy /noconfirm running-config {backup_url}/running-config_{unit}.cfg{ihack}",
      f"copy /noconfirm startup-config {backup_url}/startup-config_{unit}.cfg{ihack}"
    ]

    # We have contexts. Also backup the context configs individually.
    if isinstance(contexts, list) and len(contexts) &amp;gt; 0:
        pattern = re.compile(r'^\s*config-url\s+(\S+)')
        for context in contexts:
            output = conn.send_command(f"show run context {context} | include config-url")
            if match := pattern.search(output):
                srcfile = match.group(1)
                commands.append(f"copy /noconfirm {srcfile} {backup_url}/context_{context}_{unit}.cfg{ihack}")

    run_batch_commands(conn, commands, unit)
    return



# ----------------------------------------------------------------------------
# run_backup
# ----------------------------------------------------------------------------
# The backup command was added with ASA version 9.3(2).
# It contains the running-config, startup-config, certificates and if there
# was no bug with multiple contexts, also webvpn data such as anyconnect
# packages, but backup of WebVPN data fails in multiple contexts.
# First run a backup to flash disk and then copy it via scp due to Cisco bug
# CSCvh02142.
#
def run_backup(conn, unit, backup_url, ihack, contexts, passphrase):
    ver = get_version(conn)
    if not (ver["major"] &amp;gt;= 9 and ver["minor"] &amp;gt;= 3 and ver["maintenance"] &amp;gt;= 2):
        print("Backup command not invented yet.")
        return

    if not contexts:
        print(f"Backing up single context on {unit} unit ...")
        file = f"backup_{unit}.tar.gz"
        commands = [
            f"backup /noconfirm passphrase {passphrase} location flash:/{file}",
            f"copy /noconfirm flash:/{file} {backup_url}/{file}{ihack}",
            f"delete /noconfirm flash:/{file}"
        ]
        run_batch_commands(conn, commands, unit)

    elif isinstance(contexts, list) and len(contexts) &amp;gt; 0:
        for context in contexts:
            print(f"Backing up context {context} on {unit} unit ...")
            file = f"backup_{context}_{unit}.tar.gz"
            commands = [
                f"backup /noconfirm context {context} passphrase {passphrase} location flash:/{file}",
                f"copy /noconfirm flash:/{file} {backup_url}/{file}{ihack}",
                f"delete /noconfirm flash:/{file}"
            ]
            run_batch_commands(conn, commands, unit)
    return



# ----------------------------------------------------------------------------
# backup_firewall
# ----------------------------------------------------------------------------
def backup_firewall(cfg, fw):
    hostname = cfg["firewalls"][fw]["hostname"]

    if not is_resolvable(hostname):
        print(f"ERROR: Host {hostname} is not resolvable!")
        return

    if not is_host_reachable(hostname):
        print(f"ERROR: Host {hostname} is not reachable!")
        return

    slot = get_retention_slot()
    destdir = "/".join([cfg["firewalls"][fw]["backup-dir"], fw, slot])

    print("")
    print("=" * 80)
    print("Firewall name   : {}".format(fw))
    print("Firewall host   : {}".format(hostname))
    print("Backup host     : {}".format(cfg["firewalls"][fw]["backup-host"]))
    print("Backup directory: {}".format(destdir))
    print("=" * 80)
    print("")

    backup_url = "scp://{}:{}@{}/{}".format(
        cfg["firewalls"][fw]["backup-username"],
        cfg["firewalls"][fw]["backup-password"],
        cfg["firewalls"][fw]["backup-host"], destdir
    )

    if not os.path.exists(destdir):
        os.makedirs(destdir)

    cisco_asa = {
        "host":                  hostname,
        "device_type":           "cisco_asa",
        "username":              cfg["firewalls"][fw]["username"],
        "password":              cfg["firewalls"][fw]["password"],
        "secret":                cfg["firewalls"][fw]["enable-secret"],
        "read_timeout_override": cfg["firewalls"][fw]["read-timeout"],
        "conn_timeout":          cfg["firewalls"][fw]["conn-timeout"],
        "session_log":           destdir + "/" + "session.log",
        "use_keys":              True,
        "key_file":              cfg["firewalls"][fw]["ssh-key"],
        "disable_sha2_fix":      True,
        "verbose":               True,
    }

    try:
        with ConnectHandler(**cisco_asa) as conn:
            context_mode = get_context_mode(conn)
            failover_units = get_failover_units(conn)
            passphrase = cfg["firewalls"][fw]["password"]

            if context_mode == "single":
                ihack = get_interface_hack(conn)
                contexts = False
            elif context_mode == "multiple":
                ihack = ""
                conn.send_command("changeto system")
                contexts = get_contexts(conn)

            for unit in failover_units:
                copy_tech_support(conn, unit, backup_url, ihack)
                copy_config(conn, unit, backup_url, ihack, contexts)
                run_backup(conn, unit, backup_url, ihack, contexts, passphrase)

    except Exception as e:
        print(f"SSH to {hostname} failed: {e}")



# -----------------------------------------------------------------------------
# get_arguments
# -----------------------------------------------------------------------------
# Read commandline arguments. Returns a Namespace object with all the given
# arguments.
#
def get_arguments():
    parser = argparse.ArgumentParser(
        description="Update object-groups on the Cisco firewalls.")
    parser.add_argument('-c', '--config', required=False,
        metavar="FILENAME", help="Configuration file in YAML format.")
    parser.add_argument('-f', '--firewalls', required=True, nargs='+',
        metavar="NAME", help="""Select firewalls (HA pairs) to be updated as
        listed in the YAML config file. If not used or set to 'all', all
        configured firewalls are backed up.""")
    args = parser.parse_args()
    return args



# ----------------------------------------------------------------------------
# MAIN
# ----------------------------------------------------------------------------
# Read commandline arguments, configuration file and iterate over the fire-
# walls given as argument or in configuration file.
#
if __name__ == "__main__":
    args = get_arguments()
    cfg = read_configfile(args.config)
    firewalls = validate_firewalls(cfg, args.firewalls)

    for fw in firewalls:
        backup_firewall(cfg, fw)&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Configuration file:&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="markup"&gt;---
# ----------------------------------------------------------------------------
# Cisco ASA FIREWALLS BACKUP CONFIGURATION
# ----------------------------------------------------------------------------
#
# Use yamllint &amp;lt;thisfile&amp;gt; for syntax checking after editing this file.
#
# Defaults to be used for all the firewalls defined further below. They can
# be overwritten when needed. The device type is from Python netmiko library
# See https://github.com/ktbyers/netmiko for more.

defaults:
  device-type: cisco_asa
  conn-timeout: 30
  read-timeout: 1800
  username: backup
  password: **********
  ssh-key: ~/.ssh/id_rsa
  enable-level: 6
  backup-host: 10.0.x.y
  backup-username: ciscobackup
  backup-password: **********
  backup-dir: /mnt/backup/cisco/asa


# Cisco ASA firewalls and contexts to be processed.
# Default parameters from above can be overwritten if needed.

firewalls:
  asa1:
    hostname: asa1-admin.example.com
    enable-secret: **********
  asa2:
    hostname: asa2-admin.example.com
    enable-secret: **********&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Code uses daily, monthly and yearly retention slots to store backup.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="markup"&gt;asa1
├── daily_0
├── daily_1
├── daily_2
├── daily_3
├── daily_4
├── daily_5
├── daily_6
├── monthly_02
├── monthly_03
├── monthly_04
├── monthly_05
├── monthly_06
├── monthly_07
├── monthly_08
├── monthly_09
├── monthly_10
├── monthly_11
├── monthly_12
├── yearly_2021
├── yearly_2022
├── yearly_2023
└── yearly_2024&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Fri, 07 Jun 2024 09:25:21 GMT</pubDate>
      <guid>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5126993#M1113404</guid>
      <dc:creator>Network Diver</dc:creator>
      <dc:date>2024-06-07T09:25:21Z</dc:date>
    </item>
    <item>
      <title>Re: Scheduled backup of ASA multicontext configuration</title>
      <link>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5233696#M1118032</link>
      <description>&lt;P&gt;Hi, can you provide instructions how can I run it? I found it on the&amp;nbsp;&lt;A href="https://github.com/nies-ch/asa-backup/tree/master" target="_blank"&gt;https://github.com/nies-ch/asa-backup/tree/master&lt;/A&gt;&amp;nbsp;copied it, change config inside file&amp;nbsp;&lt;A class="Link--primary" title="asa_backup.py" href="https://github.com/nies-ch/asa-backup/blob/master/asa_backup.py" aria-label="asa_backup.py, (File)" target="_blank"&gt;asa_backup.py&lt;/A&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="markup"&gt;defaults:
  device-type: cisco_asa
  conn-timeout: 30
  read-timeout: 1800
  username: admin
  password: admin
  ssh-key: ~/.ssh/id_rsa
  backup-host: 192.168.100.98
  backup-username: backup
  backup-password: testpass
  backup-dir: /mnt/backup

firewalls:
  ASAv:
    hostname: 192.168.100.99
    enable-secret: ciscocisco
&lt;/LI-CODE&gt;
&lt;P&gt;moved it to&amp;nbsp;/usr/local/bin and tried to run, but I'm getting this error&lt;/P&gt;
&lt;P&gt;venv) root@debian:/usr/local/bin# python3 asa_backup.py -f asa1&lt;BR /&gt;ERROR: Host asa1-admin.example.com is not resolvable!&lt;/P&gt;</description>
      <pubDate>Sat, 07 Dec 2024 11:13:19 GMT</pubDate>
      <guid>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5233696#M1118032</guid>
      <dc:creator>dijix1990</dc:creator>
      <dc:date>2024-12-07T11:13:19Z</dc:date>
    </item>
    <item>
      <title>Re: Scheduled backup of ASA multicontext configuration</title>
      <link>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234121#M1118050</link>
      <description>&lt;P&gt;No need to change config inside Python script. That's just a template.&amp;nbsp;&lt;SPAN&gt;Run script once. It creates a template YAML formatted config file at ~/.asa_backup.yaml. Update that created file according your environment.&lt;/SPAN&gt;&lt;/P&gt;</description>
      <pubDate>Mon, 09 Dec 2024 05:43:56 GMT</pubDate>
      <guid>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234121#M1118050</guid>
      <dc:creator>Network Diver</dc:creator>
      <dc:date>2024-12-09T05:43:56Z</dc:date>
    </item>
    <item>
      <title>Re: Scheduled backup of ASA multicontext configuration</title>
      <link>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234638#M1118065</link>
      <description>&lt;P&gt;I think I'm doing something wrong) I copied it again and just try to run it&lt;/P&gt;
&lt;LI-CODE lang="markup"&gt;root@debian:/usr/local/bin# python3 asa_backup.py
usage: asa_backup.py [-h] [-c FILENAME] -f NAME [NAME ...]
asa_backup.py: error: the following arguments are required: -f/--firewalls
root@debian:/usr/local/bin# python3 asa_backup.py -f asa1
ERROR: Host asa1-admin.example.com is not resolvable!
&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Tue, 10 Dec 2024 01:29:30 GMT</pubDate>
      <guid>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234638#M1118065</guid>
      <dc:creator>dijix1990</dc:creator>
      <dc:date>2024-12-10T01:29:30Z</dc:date>
    </item>
    <item>
      <title>Re: Scheduled backup of ASA multicontext configuration</title>
      <link>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234673#M1118066</link>
      <description>&lt;P&gt;It reads the config from file&amp;nbsp;&lt;SPAN&gt;~/.asa_backup.yaml.&lt;/SPAN&gt;&lt;/P&gt;</description>
      <pubDate>Tue, 10 Dec 2024 05:25:44 GMT</pubDate>
      <guid>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234673#M1118066</guid>
      <dc:creator>Network Diver</dc:creator>
      <dc:date>2024-12-10T05:25:44Z</dc:date>
    </item>
    <item>
      <title>Re: Scheduled backup of ASA multicontext configuration</title>
      <link>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234676#M1118067</link>
      <description>&lt;P&gt;Yes, but I don't have this file I tried to run&amp;nbsp;&lt;/P&gt;
&lt;PRE class="lia-code-sample  language-markup"&gt;&lt;CODE&gt;asa_backup.py&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;but it didn't create file&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;~/.asa_backup.yaml maybe I need to create it myself?&lt;/SPAN&gt;&lt;/P&gt;</description>
      <pubDate>Tue, 10 Dec 2024 05:36:05 GMT</pubDate>
      <guid>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234676#M1118067</guid>
      <dc:creator>dijix1990</dc:creator>
      <dc:date>2024-12-10T05:36:05Z</dc:date>
    </item>
    <item>
      <title>Re: Scheduled backup of ASA multicontext configuration</title>
      <link>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234678#M1118068</link>
      <description>&lt;P&gt;"~/" means it expands to user's home directory or whatever is in $HOME shell environment variable. If you run it as root, it should be at /root/.asa_backup.yaml.&lt;/P&gt;</description>
      <pubDate>Tue, 10 Dec 2024 05:42:49 GMT</pubDate>
      <guid>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234678#M1118068</guid>
      <dc:creator>Network Diver</dc:creator>
      <dc:date>2024-12-10T05:42:49Z</dc:date>
    </item>
    <item>
      <title>Re: Scheduled backup of ASA multicontext configuration</title>
      <link>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234689#M1118069</link>
      <description>&lt;P&gt;oh, yes, don't know why I didn't see it&lt;/P&gt;
&lt;P&gt;Does it works with ASAv single context mode and without failover?&lt;/P&gt;
&lt;LI-CODE lang="markup"&gt;root@debian:/usr/local/bin# python3 asa_backup.py -f asa1

================================================================================
Firewall name   : asa1
Firewall host   : 192.168.100.99
Backup host     : 192.168.100.98
Backup directory: /mnt/backup/cisco/asa/asa1/daily_1
Backup date/time: 2024-12-10 13:29:44
================================================================================

ERROR: Backing up 192.168.100.99 failed: BaseConnection.__init__() got an unexpected keyword argument 'read_timeout_override'
Traceback (most recent call last):
  File "/usr/local/bin/asa_backup.py", line 567, in &amp;lt;module&amp;gt;
    backup_firewall(cfg, fw)
  File "/usr/local/bin/asa_backup.py", line 530, in backup_firewall
    verify_backup(destdir, failover_units, contexts)
                           ^^^^^^^^^^^^^^
UnboundLocalError: cannot access local variable 'failover_units' where it is not associated with a value
&lt;/LI-CODE&gt;</description>
      <pubDate>Tue, 10 Dec 2024 06:36:47 GMT</pubDate>
      <guid>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234689#M1118069</guid>
      <dc:creator>dijix1990</dc:creator>
      <dc:date>2024-12-10T06:36:47Z</dc:date>
    </item>
    <item>
      <title>Re: Scheduled backup of ASA multicontext configuration</title>
      <link>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234698#M1118070</link>
      <description>&lt;P&gt;Yes, it works with single context and no failover license. But I guess the problem here is the Netmiko library:&lt;/P&gt;&lt;LI-CODE lang="markup"&gt;BaseConnection.__init__() got an unexpected keyword argument 'read_timeout_override'&lt;/LI-CODE&gt;&lt;P&gt;I use Python 3.11 and Netmiko 4.3.0. Python compatibitility is highly volatile. Best you create a virtual Python environment when updating libraries with pip that are managed by OS package manager. It could break other system tools using Python.&lt;/P&gt;&lt;P&gt;For example create a python envioronment in /usr/local and use /usr/local/bin/python3.11 in the script's shebang.&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="markup"&gt;python3.11 -m venv /usr/local/
/usr/local/bin/python3.11 -m pip install netmiko&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Tue, 10 Dec 2024 07:11:48 GMT</pubDate>
      <guid>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234698#M1118070</guid>
      <dc:creator>Network Diver</dc:creator>
      <dc:date>2024-12-10T07:11:48Z</dc:date>
    </item>
    <item>
      <title>Re: Scheduled backup of ASA multicontext configuration</title>
      <link>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234709#M1118073</link>
      <description>&lt;P&gt;Yes,&amp;nbsp;&lt;SPAN&gt;Netmiko library was too old, I upgraded it to 4.5.0, but strange I think there is something else&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;I will try to reinstall python3 and netmiko&lt;/P&gt;
&lt;P&gt;Can I just use Login and password instead of ssh key? (because of tacacs),&amp;nbsp;use_keys need to be false?&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="markup"&gt;(venv) root@debian:/usr/local/bin# python3.11 asa_backup.py -f asa1

================================================================================
Firewall name   : asa1
Firewall host   : 192.168.100.99
Backup host     : 192.168.100.98
Backup directory: /mnt/backup/cisco/asa/asa1/daily_1
Backup date/time: 2024-12-10 14:28:51
================================================================================

Unknown exception: q must be exactly 160, 224, or 256 bits long
Traceback (most recent call last):
  File "/usr/local/bin/venv/lib/python3.11/site-packages/paramiko/transport.py", line 2262, in run
    handler(m)
  File "/usr/local/bin/venv/lib/python3.11/site-packages/paramiko/auth_handler.py", line 404, in _parse_service_accept
    sig = self.private_key.sign_ssh_data(blob, algorithm)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/bin/venv/lib/python3.11/site-packages/paramiko/dsskey.py", line 120, in sign_ssh_data
    ).private_key(backend=default_backend())
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: q must be exactly 160, 224, or 256 bits long

ERROR: Backing up 192.168.100.99 failed: q must be exactly 160, 224, or 256 bits long
Traceback (most recent call last):
  File "/usr/local/bin/asa_backup.py", line 567, in &amp;lt;module&amp;gt;
    backup_firewall(cfg, fw)
  File "/usr/local/bin/asa_backup.py", line 530, in backup_firewall
    verify_backup(destdir, failover_units, contexts)
                           ^^^^^^^^^^^^^^
UnboundLocalError: cannot access local variable 'failover_units' where it is not associated with a value
&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Tue, 10 Dec 2024 07:45:01 GMT</pubDate>
      <guid>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234709#M1118073</guid>
      <dc:creator>dijix1990</dc:creator>
      <dc:date>2024-12-10T07:45:01Z</dc:date>
    </item>
    <item>
      <title>Re: Scheduled backup of ASA multicontext configuration</title>
      <link>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234723#M1118075</link>
      <description>&lt;P&gt;Yes. Netmiko can do that. I just edited code and readme here:&lt;BR /&gt;&lt;BR /&gt;&lt;A href="https://github.com/nies-ch/asa-backup/tree/master" target="_blank"&gt;https://github.com/nies-ch/asa-backup/tree/master&lt;/A&gt;&lt;/P&gt;&lt;P&gt;Add 'use-key: False' to the default section of the yaml config file as shown in the example.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Tue, 10 Dec 2024 08:04:21 GMT</pubDate>
      <guid>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234723#M1118075</guid>
      <dc:creator>Network Diver</dc:creator>
      <dc:date>2024-12-10T08:04:21Z</dc:date>
    </item>
    <item>
      <title>Re: Scheduled backup of ASA multicontext configuration</title>
      <link>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234736#M1118077</link>
      <description>&lt;P&gt;I reinstalled python and copied new script, change&amp;nbsp;&lt;SPAN&gt;use-key: False, but there is something else&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;before it I connected to 192.168.100.99 from 192.168.100.157 via ssh -l &lt;A href="mailto:admin@192.168.100.99" target="_blank" rel="noopener"&gt;admin@192.168.100.99&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;and after I did copy to check scp. Everythink works fine&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="markup"&gt;# CISCO ASA FIREWALLS BACKUP CONFIGURATION
#
# Use yamllint &amp;lt;thisfile&amp;gt; for syntax checking after editing this file.
# Defaults to be used for all the firewalls defined further below. They can
# be overwritten when needed. The device type is from Python netmiko library
# See https://github.com/ktbyers/netmiko for more.

defaults:
device-type: cisco_asa
conn-timeout: 30
read-timeout: 1800
username: admin
password: admin12345
use-key: False
ssh-key: ~/.ssh/id_rsa
backup-host: 192.168.100.157
backup-username: fractal
backup-password: admin12345
backup-dir: /home/fractal/backup/asa

firewalls:
asa1:
hostname: 192.168.100.99
enable-secret: ciscocisco&lt;/LI-CODE&gt;&lt;LI-CODE lang="markup"&gt;fractal@fractalubuntu:~/asa-backup$ python3 asa_backup.py -f asa1

================================================================================
Firewall name   : asa1
Firewall host   : 192.168.100.99
Backup host     : 192.168.100.157
Backup directory: /home/fractal/backup/asa/asa1/daily_1
Backup date/time: 2024-12-10 08:23:04
================================================================================

ERROR: Backing up 192.168.100.99 failed: TCP connection to device failed.

Common causes of this problem are:
1. Incorrect hostname or IP address.
2. Wrong TCP port.
3. Intermediate firewall blocking access.

Device settings: cisco_asa 192.168.100.99:22


Traceback (most recent call last):
  File "asa_backup.py", line 571, in &amp;lt;module&amp;gt;
    backup_firewall(cfg, fw)
  File "asa_backup.py", line 534, in backup_firewall
    verify_backup(destdir, failover_units, contexts)
UnboundLocalError: local variable 'failover_units' referenced before assignment
&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Tue, 10 Dec 2024 08:30:21 GMT</pubDate>
      <guid>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234736#M1118077</guid>
      <dc:creator>dijix1990</dc:creator>
      <dc:date>2024-12-10T08:30:21Z</dc:date>
    </item>
    <item>
      <title>Re: Scheduled backup of ASA multicontext configuration</title>
      <link>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234749#M1118080</link>
      <description>&lt;P&gt;There are lots of SSH options in NetMiko BaseConnection class.&amp;nbsp; I had to set&amp;nbsp;disable_sha2_fix to True to get it working. Your environment may be different. There are also options of enabled SSH algorithms. Netmiko is a wrapper for Paramiko, which is a pure Python implementation of SSH and does not use the OpenSSH shell commands. It works with Paramiko 3.5. You may dig into their documentation to get SSH to work via Paramiko/Netmiko.&lt;/P&gt;&lt;P&gt;&lt;A href="https://ktbyers.github.io/netmiko/docs/netmiko/index.html#netmiko.ssh_dispatcher" target="_blank"&gt;https://ktbyers.github.io/netmiko/docs/netmiko/index.html#netmiko.ssh_dispatcher&lt;/A&gt;&lt;/P&gt;&lt;P&gt;&lt;A href="https://www.paramiko.org/" target="_blank"&gt;https://www.paramiko.org/&lt;/A&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Tue, 10 Dec 2024 08:52:39 GMT</pubDate>
      <guid>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234749#M1118080</guid>
      <dc:creator>Network Diver</dc:creator>
      <dc:date>2024-12-10T08:52:39Z</dc:date>
    </item>
    <item>
      <title>Re: Scheduled backup of ASA multicontext configuration</title>
      <link>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234753#M1118082</link>
      <description>&lt;P&gt;hm, strange, OK. I think I need just install rconfig, because match every&amp;nbsp;&amp;nbsp;SSH algorithms netmiko with different asa software it's a little inconvinient&lt;/P&gt;</description>
      <pubDate>Tue, 10 Dec 2024 09:08:10 GMT</pubDate>
      <guid>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234753#M1118082</guid>
      <dc:creator>dijix1990</dc:creator>
      <dc:date>2024-12-10T09:08:10Z</dc:date>
    </item>
    <item>
      <title>Re: Scheduled backup of ASA multicontext configuration</title>
      <link>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234755#M1118083</link>
      <description>&lt;P&gt;It should be easy to adjust that per device in the Yaml config file. All parameters from the defaults section can go to a host.&lt;/P&gt;&lt;P&gt;Code section to apply config parameters from the config file to Netmiko session:&lt;/P&gt;&lt;LI-CODE lang="markup"&gt;cisco_asa = {
        "host":                  hostname,
        "device_type":           "cisco_asa",
        "username":              cfg["firewalls"][fw]["username"],
        "password":              cfg["firewalls"][fw]["password"],
        "secret":                cfg["firewalls"][fw]["enable-secret"],
        "read_timeout_override": cfg["firewalls"][fw]["read-timeout"],
        "conn_timeout":          cfg["firewalls"][fw]["conn-timeout"],
        "session_log":           destdir + "/" + "session.log",
        "use_keys":              cfg["firewalls"][fw]["use-key"],
        "key_file":              cfg["firewalls"][fw]["ssh-key"],
        "disable_sha2_fix":      True,
        "verbose":               True,
    }
&lt;/LI-CODE&gt;</description>
      <pubDate>Tue, 10 Dec 2024 09:15:27 GMT</pubDate>
      <guid>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234755#M1118083</guid>
      <dc:creator>Network Diver</dc:creator>
      <dc:date>2024-12-10T09:15:27Z</dc:date>
    </item>
    <item>
      <title>Re: Scheduled backup of ASA multicontext configuration</title>
      <link>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234767#M1118086</link>
      <description>&lt;P&gt;yes I saw it, but script can't connect to ASAv (9.20(3)7) via ssh (because of &lt;SPAN&gt;algorithms&lt;/SPAN&gt;). I think it to difficult for person who isn't familiar with netmiko.&lt;/P&gt;
&lt;P&gt;BTW Thanks&lt;/P&gt;</description>
      <pubDate>Tue, 10 Dec 2024 09:36:46 GMT</pubDate>
      <guid>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234767#M1118086</guid>
      <dc:creator>dijix1990</dc:creator>
      <dc:date>2024-12-10T09:36:46Z</dc:date>
    </item>
    <item>
      <title>Re: Scheduled backup of ASA multicontext configuration</title>
      <link>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234771#M1118087</link>
      <description>&lt;P&gt;Cisco wants user to migrate to new FMC managed FTD firewalls. ASA gets worse with every update. In 9.18 they dropped object dependency check, so you can delete objects and object-groups that are still in use.&lt;/P&gt;</description>
      <pubDate>Tue, 10 Dec 2024 09:46:04 GMT</pubDate>
      <guid>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234771#M1118087</guid>
      <dc:creator>Network Diver</dc:creator>
      <dc:date>2024-12-10T09:46:04Z</dc:date>
    </item>
    <item>
      <title>Re: Scheduled backup of ASA multicontext configuration</title>
      <link>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234773#M1118089</link>
      <description>&lt;P&gt;Yes, but it's very big project to move FTD, so nowadays I need to backup FPR1150/2130/3140 with asa software. Will try to use rConfig instead of script)&lt;/P&gt;</description>
      <pubDate>Tue, 10 Dec 2024 09:50:50 GMT</pubDate>
      <guid>https://community.cisco.com/t5/network-security/scheduled-backup-of-asa-multicontext-configuration/m-p/5234773#M1118089</guid>
      <dc:creator>dijix1990</dc:creator>
      <dc:date>2024-12-10T09:50:50Z</dc:date>
    </item>
  </channel>
</rss>

