A past Secplicity post explains how to automate deployment of a WatchGuard Firebox Cloud on AWS. The related GitHub repository includes some Python code that configures the Firebox via the WatchGuard Fireware CLI (Command Line Interface). The code runs in an AWS Lambda function, which is a way to execute source code without setting up and configuring a server. The related buzzword for this type of code execution is “Serverless,” further explained in that linked article on Martin Fowler’s web site.
Although the prior example shows configuration of a Firebox on AWS via a serverless architecture, you don’t need AWS or Lambda to achieve a similar result in a non-AWS environment. Any operating system that supports the correct version of Python and related libraries would be able to run the Python code. The sample code uses the Paramiko library to create an SSH connection to the Firebox and the Boto3 library to run AWS commands. If executing the code outside AWS, remove the boto library and alter the code to connect to internal file storage to retrieve the SSH key to connect to Fireboxes in other locations.
A more involved version of this code extracts common functions into a separate file or class, allowing reuse of the code in different programs. Take a look at fireboxcommands.py. This is a Python class which has several commands that perform configuration of a Firebox. If you don’t know anything about Python or a similar programming language, you may want to learn that first. For those familiar with programming, this example shows how to use Python to configure a Firebox Cloud.
Notice the __init__ function. When a program first uses this class (or “instantiates” an object based on that class template in software terms) Python executes this initialization function, which creates the connection to the Firebox. Subsequent functions in the code use this connection to send commands to the Firebox CLI.
To connect to the Firebox, the code needs an SSH key. This code connects to an AWS S3 bucket and retrieves the SSH key for the Firebox CLI connection:
Change that code to retrieve the key from your local key storage location if running in non-AWS environment. Download the key to the location and name specified in the local_key_file variable. Subsequent code uses that variable to make the connection to the Firebox.
Make sure to protect the key! One way would be to store the key in a file share on a server where only authorized machines and accounts can access it via restricted network rules and system permissions. Secure the machine that downloads the key and the network used to retrieve the key and access the Firebox.
Once the code has the key, it can connect to the Firebox and execute commands. The remaining functions in the class carry out Firebox commands. A generic function will execute any command sent to it. This function also handles waiting for commands to complete and deals with some errors that may occur.
More specific functions create command strings, call commands as needed or in a specific order, and include related error handling. For example, the add_alias function creates a Firebox alias. It calls the delete function if the alias already exists. Then it generates the alias command string that a network administrator would typically type into a terminal window after connecting to the Firebox. It sends the command to the exe function which in turn sends the command to the Firebox. In the case of an FQDN alias with an asterisk, the CLI needs single quotes around the FQDN so it handles that case as well.
The Firebox CLI must be in the correct mode to run that command or the command will fail. Find the correct mode for a command in the header of the CLI documentation on the page corresponding to that command. If the CLI generates an error, verify that the CLI is in the correct mode.
After commands complete in a mode, the apply command applies the changes to the Firebox and the exit command exits the mode. Our fireboxcommands.py class has methods for entering different modes, applying the changes, and exiting a mode.
It is important to close any connection in a software program that no longer needs the connection. Connections left open will bog down the network, and potentially cause security problems or memory leaks. The close_connections function handles closing the connection to the Firebox CLI.
First the code sets the value of variables. This code retrieves variable values from the AWS Lambda environment variables. You could hardcode the variables into the code or retrieve them from a configuration file of your choosing if not on AWS.
Next the code instantiates the fireboxcommands class. The initialization function mentioned above needs the name of an AWS S3 bucket where it can retrieve the key, the name of the key and the Firebox IP address. Change this as needed to match your environment, if not running this code on AWS.
If the fireboxcommands object initialized correctly it will have a connection to the Firebox. Now the other functions can configure the Firebox. First the code checks to see if the required rules and alias exist. There is no way to update rules and aliases via the CLI at this time. The library needs to know if the rules exist so it can delete a rule and re-add it if it requires an update.
Next the code executes the commands to enter the correct modes and configure the Firebox.
Last, the code calls the function to close open connections. One very important point to note: Always close connections in using finally. If the command to close the connection only exists at the end of the other commands, any error that occurs will halt the program and the command to close the connections will never execute. If the call to close_connections occurs in a finally block, it will always execute, even in case of an earlier exception.
Note that this code is for demonstration purposes only. A seasoned Python programmer could extend and improve upon this code, but this starting point shows how to automate configuration using code stored in a source code repository. Hopefully this example will inspire network administrators to use automation to improve security in any environment. — Teri Radichel (@teriradichel)