Fortigate Backup With Gitlab Cicd Ansible
About this article
Backup is one of the critical points of a solid cyber security strategy, and it is a vast topic. It can cover file backup, disk backup, virtual disk backup, database and configuration backup. In addition, backups are crucial for our DRP(Disaster Recovery Plan); actually, there is not a DRP if there is not a backup in place.
This article will cover how to backup your Fortigate configuration automatically using Gitlab CI/CD and Ansible.
There are many ways to run our Ansible playbooks, and we could ask: Why use Gitlab CI/CD for it? The answer is quite simple, some customers do not need to run an Ansible Tower, AWX or Semaphore, and Gitlab CI/CD has excellent potential to automate and schedule tasks.
What do I need to follow in this article?
- Gitlab account.
- Gitlab Runner to run our CI/CD Jobs.
- Create an SSH Key to push config files to a repository.
In Gitlab, we will need to set our CI/CD environment variable. These variables will be used by the CI/CD pipeline and help us to keep our configuration and playbooks clear of sensitive data.
Variables:
- FTG_PASSWD # We recommend creating a user for the backup job
- KNOWN_HOSTS # Here, we need to get the public keys for Gitlab so that, later, we can push the config files to the repository.
- SSH_KEY # Your SSH Key to push to the Gitlab repository.
About the KNOWN_HOSTS, you can get that information from your terminal using the command.
ssh-keyscan gitlab-host
Now let us have a look at the files we need. In my case, I created a git repository called fortigate-backup. The repository contains:
- gitlab-ci.yml
- backup.yml
- inventory.yml
.gitlab-ci.yml
---
stages:
- linting
- backup
yamllint:
stage: linting
image: registry.gitlab.com/pipeline-components/yamllint:latest
script:
- yamllint .
ansible-lint:
stage: linting
image: registry.gitlab.com/pipeline-components/ansible-lint:latest
script:
- ansible-lint --show-relpath .
ftg-backup:
stage: backup
image: willhallonline/ansible:2.9.27-bullseye-slim
variables:
DOCKER_DRIVER: overlay
FTG_PASSWD: "${FTG_PASSWD}"
services:
- docker:dind
before_script:
- mkdir ~/.ssh/
- echo "${KNOWN_HOSTS}" > ~/.ssh/known_hosts
- echo "${SSH_KEY}" > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- git config --global user.email "john@example.com"
- git config --global user.name "Gitlab CI"
- export COMMIT_TIME=$(date)
script:
- apt update && apt install -y git
- ansible-galaxy collection install fortinet.fortios:1.1.9
- ansible-playbook -i fortinet/inventory.yml fortinet/backup.yml
- mkdir /fortigate-backups
- git clone git@gitlab.com:myusername/fortigate-configs-backup.git /fortigate-backups
- cd /fortigate-backups
- rm -f backup_global backup_vdom
- cp /tmp/backup_* .
- git add -A
- git commit -m "${COMMIT_TIME}"
- |
if [ -n "$(git status --porcelain)" ]; then
echo "there are changes";
git push
else
echo "no changes";
fi
environment:
name: main
rules:
- if: $CI_COMMIT_BRANCH == "main"
Lets explain the .gitlab-ci.yml
which is a very important part for our backup task.
First we have two jobs to run the yamllint
and ansible-lint
, it is important to write good Infrastructure as a Code.
Then we have the ftg-backup
job, as you can see, we use the ansible docker image. In variables we set the FTG_PASSWD
env variable, it will be use when running the playbook and authenticate with the firewalls. For before_script
we configure the SSH options and git configuration to use afterwards with our Gitlab repository.
In the main section script
we do very interesting things:
- Update and install git package.
- Install fortinet ansible collection which is used by our playbook.
- Run the playbook to backup the configs
- Clone the
fortigate-configs-backup
repository, this is the repo where we will store our backups. - At last we check if the files contain new changes or not to commit to the repository.
inventory.yml
---
fortigates:
hosts:
fw01.example.com:
ansible_host: 172.16.1.254
ansible_user: backup
ansible_password: '{{ lookup("env", "FTG_PASSWD") }}'
fw02.example.com:
ansible_host: 172.16.2.254
ansible_user: backup
ansible_password: '{{ lookup("env", "FTG_PASSWD") }}'
fw03.example.com:
ansible_host: 172.16.3.254
ansible_user: backup
ansible_password: '{{ lookup("env", "FTG_PASSWD") }}'
vars:
ansible_network_os: fortinet.fortios.fortios
In the inventory, it is essential to replace the information accordingly with your existing firewall infrastructure, hostname, IP address and credentials.
backup.yml
---
- hosts: fortigates
connection: httpapi
collections:
- fortinet.fortios
vars:
vdom: "root"
ansible_httpapi_use_ssl: "yes"
ansible_httpapi_validate_certs: "no"
ansible_httpapi_port: 443
tasks:
- name: backup a_specific_vdom settings
fortios_system_config_backup_restore:
config: "system config backup"
vdom: "{{ vdom }}"
backup: "yes"
scope: "vdom"
filename: '/tmp/backup_vdom_{{ inventory_hostname }}'
- name: backup global settings
fortios_system_config_backup_restore:
config: "system config backup"
vdom: "{{ vdom }}"
backup: "yes"
scope: "global"
filename: '/tmp/backup_global_{{ inventory_hostname }}'
In this playbook what we do is to take backup of the global configuration and the root vdom
. On the specific vdom backup, you can configure different vdoms if it is necessary or create multiple tasks for multiple vdoms. for more information, I recommend checking the ansible documentation.
After we push our .gitlab-ci.yml
and Ansible playbook, it will run automatically once. If we would like to schedule this pipeline job everyday or once a week we just need to go to CI/CD -> Schedules and create a new schedule.
In some cases, it may be important to encrypt files before pushing them to the repository, in this case we can use tools like:
- GPG
- ccrypt
- 7-zip
If you need to support with Fortinet or DevSecOps in your company, you can always contact us at Gonkar IT Security