From e46dc8d1d761db7920c514736997a6e81320ba4d Mon Sep 17 00:00:00 2001 From: Simon Guyennet Date: Tue, 30 Sep 2025 11:12:35 +0200 Subject: [PATCH] Stormshield SNS firewall HA deployment on PCI --- network/stormshield-sns-firewall/README.md | 67 ++++++++++ .../stormshield-sns-firewall/ovh_instance.tf | 65 ++++++++++ .../ovh_private_network.tf | 86 +++++++++++++ network/stormshield-sns-firewall/provider.tf | 7 + .../templates/stormshield-config.tpl | 120 ++++++++++++++++++ network/stormshield-sns-firewall/variables.tf | 57 +++++++++ 6 files changed, 402 insertions(+) create mode 100644 network/stormshield-sns-firewall/README.md create mode 100644 network/stormshield-sns-firewall/ovh_instance.tf create mode 100644 network/stormshield-sns-firewall/ovh_private_network.tf create mode 100644 network/stormshield-sns-firewall/provider.tf create mode 100644 network/stormshield-sns-firewall/templates/stormshield-config.tpl create mode 100644 network/stormshield-sns-firewall/variables.tf diff --git a/network/stormshield-sns-firewall/README.md b/network/stormshield-sns-firewall/README.md new file mode 100644 index 00000000..54b21639 --- /dev/null +++ b/network/stormshield-sns-firewall/README.md @@ -0,0 +1,67 @@ +# Deploy Stormshield SNS HA cluster on Public Cloud + +This Terraform automate the deployment of a Stormshield SNS HA cluster on Public Cloud. + +The manual steps to achieve this are described here: +https://help.ovhcloud.com/csm/en-public-cloud-network-stormshield-vrack?id=kb_article_view&sysparm_article=KB0065106 + +## Requirements + +* A Public Cloud project +* An additional IP block +* A vRack +* A Stormshield SNS image for OpenStack +* At least one Stormshield license with the corresponding maj file +* A SSH key +* Terraform binary +* Openstack CLI + +## Configuring the deployment + +* Create an OpenStack user in your Public Cloud project (Users & Roles->Add user) +* Download your user openrc file +* Load the Openstack environment variable: + ```source openrc.sh``` + ```export OS_REGION_NAME=``` + +* Add your Public Cloud project to your vRack +* Add your IP block to your vRack + https://help.ovhcloud.com/csm/en-dedicated-servers-ip-block-vrack?id=kb_article_view&sysparm_article=KB0043342 +* Upload Stormshield SNS image + ```openstack image create --disk-format raw --container-format bare --file ./utm-SNS-EVA-4.8.11-openstack.qcow2 utm-SNS-EVA-4.8.11``` + +* Configure the following variables in terraform.tfvars: + - ovh_os_instance_region: + The region where you would like to deploy Stromshield SNS (example: SBG7). + This needs to be the same region than where you upload your SNS image. + - ovh_os_instance_password: + The password for the stormshield admin user + - ssh_public_key: + Your SSH public key + - admin_client_ip: + The public IP of the machine used to administrate stormshield. + You can use `curl ipinfo.io/ip` to get the public IP of your machine. + - ovh_os_instance_wan_ip: + A list of two public IP within your IP block that can be used by the two Stormshield instances. + Only the first one will be kept as VIP for your Stormshield HA cluster. + - ovh_os_instance_wan_mask: + The mask of your IP block. + - ovh_os_instance_wan_gw + The gateway for your IP block. + - ovh_os_instance_image_name + The name of your Stormshield image (example: utm-SNS-EVA-4.8.11). + - stormshield_serial_number + Two serial numbers for your Stormshield instances (example: ["VMSNSXXXXXXXXXX", "VMSNSYYYYYYYYYYY"]). +* Add your maj files (corresponding to your stormshield_serial_number list) to the stormshield-maj directory (exemple: vminit-.maj) + +## Deploy Stormshield SNS HA cluster + +terraform init +terraform plan +terraform apply + +Browse: https:// +Login: admin +Password: The value of the Terraform ovh_os_instance_password variable + +**note:** The Stormshield SNS configuration can take a little bit of time, therefore wait a bit before trying to login. \ No newline at end of file diff --git a/network/stormshield-sns-firewall/ovh_instance.tf b/network/stormshield-sns-firewall/ovh_instance.tf new file mode 100644 index 00000000..125c73bc --- /dev/null +++ b/network/stormshield-sns-firewall/ovh_instance.tf @@ -0,0 +1,65 @@ +resource "openstack_compute_servergroup_v2" "stormshield" { + name = "${var.ovh_os_instance_name}-stormshield" + region = var.ovh_os_instance_region + policies = ["anti-affinity"] +} + +resource "openstack_compute_keypair_v2" "keypair" { + name = "${var.ovh_os_instance_name}-keypair" + public_key = var.ssh_public_key +} + +resource "openstack_compute_instance_v2" "instance" { + count = 2 + + name = "${var.ovh_os_instance_name}-${count.index + 1}" + region = var.ovh_os_instance_region + image_name = var.ovh_os_instance_image_name + flavor_name = var.ovh_os_instance_flavor_name + key_pair = openstack_compute_keypair_v2.keypair.name + + scheduler_hints { + group = openstack_compute_servergroup_v2.stormshield.id + } + + connection { + type = "ssh" + user = "admin" + password = var.ovh_os_instance_password + host = var.ovh_os_instance_wan_ip[count.index] + } + + provisioner "file" { + source = "./stormshield-maj/" + destination = "/tmp" + } + + user_data = base64encode(templatefile("templates/stormshield-config.tpl", + { + SERIAL_NUMBER=var.stormshield_serial_number[count.index] + PASSWORD=var.ovh_os_instance_password + WAN_IP=var.ovh_os_instance_wan_ip[count.index] + WAN_MASK=var.ovh_os_instance_wan_mask + WAN_GW=var.ovh_os_instance_wan_gw + LAN_IP=cidrhost(openstack_networking_subnet_v2.private-subnet-workload.cidr, 1) + LAN_MASK=cidrnetmask(openstack_networking_subnet_v2.private-subnet-workload.cidr) + HA_IP=cidrhost(openstack_networking_subnet_v2.private-subnet-ha.cidr, count.index + 3) + HA_MASK=cidrnetmask(openstack_networking_subnet_v2.private-subnet-ha.cidr) + HA_ID=count.index + 1 + HA_PRIMARY_IP=cidrhost(openstack_networking_subnet_v2.private-subnet-ha.cidr, count.index + 2) + ADMIN_CLIENT_IP=var.admin_client_ip + } + )) + + network { + name = openstack_networking_network_v2.private-net-ext.name + } + + network { + name = openstack_networking_network_v2.private-net-workload.name + } + + network { + name = openstack_networking_network_v2.private-net-ha.name + } +} \ No newline at end of file diff --git a/network/stormshield-sns-firewall/ovh_private_network.tf b/network/stormshield-sns-firewall/ovh_private_network.tf new file mode 100644 index 00000000..23df4a94 --- /dev/null +++ b/network/stormshield-sns-firewall/ovh_private_network.tf @@ -0,0 +1,86 @@ +resource "openstack_networking_network_v2" "private-net-ext" { + name = "${var.ovh_os_instance_name}-ext" + region = var.ovh_os_instance_region + admin_state_up = "true" + port_security_enabled = "false" + value_specs = { + "provider:network_type" = "vrack" + "provider:segmentation_id" = "0" + } +} + +resource "openstack_networking_subnet_v2" "private-subnet-ext" { + name = "${var.ovh_os_instance_name}-ext" + network_id = openstack_networking_network_v2.private-net-ext.id + region = var.ovh_os_instance_region + cidr = "192.168.1.0/29" + enable_dhcp = true + allocation_pool { + start = "192.168.1.2" + end = "192.168.1.6" + } + no_gateway = false +} + +data "openstack_networking_network_v2" "ext-net" { + name = "Ext-Net" + region = var.ovh_os_instance_region +} + +resource "openstack_networking_router_v2" "router" { + name = "${var.ovh_os_instance_name}-router" + region = var.ovh_os_instance_region + admin_state_up = true + external_network_id = "" +} + +resource "openstack_networking_router_interface_v2" "router-interface" { + region = var.ovh_os_instance_region + router_id = openstack_networking_router_v2.router.id + subnet_id = openstack_networking_subnet_v2.private-subnet-ext.id +} + +resource "openstack_networking_network_v2" "private-net-workload" { + name = "${var.ovh_os_instance_name}-workload" + region = var.ovh_os_instance_region + admin_state_up = "true" + port_security_enabled = "false" + value_specs = { + "provider:network_type" = "vrack" + "provider:segmentation_id" = var.ovh_os_private_network_vlan_id + } +} + +resource "openstack_networking_subnet_v2" "private-subnet-workload" { + name = "${var.ovh_os_instance_name}-workload" + network_id = openstack_networking_network_v2.private-net-workload.id + region = var.ovh_os_instance_region + cidr = "10.200.0.0/16" + enable_dhcp = true + allocation_pool { + start = "10.200.0.2" + end = "10.200.0.254" + } + dns_nameservers = ["213.186.33.99"] + no_gateway = false +} + +resource "openstack_networking_network_v2" "private-net-ha" { + name = "${var.ovh_os_instance_name}-ha" + region = var.ovh_os_instance_region + admin_state_up = "true" + port_security_enabled = "false" + value_specs = { + "provider:network_type" = "vrack" + "provider:segmentation_id" = var.ovh_os_private_network_ha_vlan_id + } +} + +resource "openstack_networking_subnet_v2" "private-subnet-ha" { + name = "${var.ovh_os_instance_name}-ha" + network_id = openstack_networking_network_v2.private-net-ha.id + region = var.ovh_os_instance_region + cidr = "192.168.2.0/29" + enable_dhcp = true + no_gateway = true +} \ No newline at end of file diff --git a/network/stormshield-sns-firewall/provider.tf b/network/stormshield-sns-firewall/provider.tf new file mode 100644 index 00000000..1b4f719d --- /dev/null +++ b/network/stormshield-sns-firewall/provider.tf @@ -0,0 +1,7 @@ +terraform { + required_providers { + openstack = { + source = "terraform-provider-openstack/openstack" + } + } +} \ No newline at end of file diff --git a/network/stormshield-sns-firewall/templates/stormshield-config.tpl b/network/stormshield-sns-firewall/templates/stormshield-config.tpl new file mode 100644 index 00000000..c5e36794 --- /dev/null +++ b/network/stormshield-sns-firewall/templates/stormshield-config.tpl @@ -0,0 +1,120 @@ +#!/bin/sh + +SETCONF=/usr/Firewall/sbin/setconf +CF=/usr/Firewall/ConfigFiles +SBIN=/usr/Firewall/sbin +GLOBAL=/usr/Firewall/System/global + +SERIAL_NUMBER="${SERIAL_NUMBER}" + +PASSWORD="${PASSWORD}" + +WAN_IP="${WAN_IP}" +WAN_MASK="${WAN_MASK}" +WAN_GW="${WAN_GW}" + +LAN_IP="${LAN_IP}" +LAN_MASK="${LAN_MASK}" + +HA_IP="${HA_IP}" +HA_MASK="${HA_MASK}" + +ADMIN_CLIENT_IP="${ADMIN_CLIENT_IP}" + +HA_ID="${HA_ID}" +HA_PRIMARY_IP="${HA_PRIMARY_IP}" + +$SETCONF $CF/system Console State Serial +$SBIN/enconsole + +# Configure password +$SBIN/fwpasswd -p $PASSWORD + +$SETCONF $GLOBAL Options SystemName "STORMSHIELD" +$SETCONF $GLOBAL Options SystemNodeName "HA-"$HA_ID +$SBIN/enservice + +#chsh -s /bin/sh admin + +# Add default gateway and admin IP ACL +echo '[Host]' >> /usr/Firewall/ConfigFiles/object.new +echo Firewall_out_router=$WAN_GW >> /usr/Firewall/ConfigFiles/object.new +echo admin_client=$ADMIN_CLIENT_IP >> /usr/Firewall/ConfigFiles/object.new +sed -e '1,1d' /usr/Firewall/ConfigFiles/object >> /usr/Firewall/ConfigFiles/object.new +mv /usr/Firewall/ConfigFiles/object.new /usr/Firewall/ConfigFiles/object + +# WAN interface configuration +sed -e '142,149d' /usr/Firewall/ConfigFiles/network > /usr/Firewall/ConfigFiles/network.new +echo '' >> /usr/Firewall/ConfigFiles/network.new +echo '[ethernet0]' >> /usr/Firewall/ConfigFiles/network.new +echo 'State=1' >> /usr/Firewall/ConfigFiles/network.new +echo 'Name=wan' >> /usr/Firewall/ConfigFiles/network.new +echo 'Color=800040' >> /usr/Firewall/ConfigFiles/network.new +echo 'Media=0' >> /usr/Firewall/ConfigFiles/network.new +echo 'EEE=0' >> /usr/Firewall/ConfigFiles/network.new +echo Address=$WAN_IP >> /usr/Firewall/ConfigFiles/network.new +echo Mask=$WAN_MASK >> /usr/Firewall/ConfigFiles/network.new +mv /usr/Firewall/ConfigFiles/network.new /usr/Firewall/ConfigFiles/network +$SBIN/ennetwork + +# LAN and HA interfaces configuration +cat << EOCONFIG > /tmp/stormshield.cfg +CONFIG NETWORK INTERFACE ADDRESS UPDATE ifname=ethernet1 address=$LAN_IP mask=$LAN_MASK addrnb=0 +CONFIG NETWORK INTERFACE ADDRESS UPDATE ifname=ethernet2 address=$HA_IP mask=$HA_MASK addrnb=0 +CONFIG NETWORK INTERFACE ACTIVATE +CONFIG NETWORK INTERFACE RENAME ifname=ethernet1 name=lan +CONFIG NETWORK INTERFACE RENAME ifname=ethernet2 name=ha +CONFIG NETWORK INTERFACE ACTIVATE +EOCONFIG +$SBIN/nsrpc -f -c /tmp/stormshield.cfg -l /tmp/stormshield.output admin:$PASSWORD@127.0.0.1:1300 + +# Create default firewall rules +cat << EOFILTER > $CF/Filter/01 +[Filter] +separator color="c0c0c0" comment="Remote Management" collapse="0" +pass from admin_client to firewall_all port firewall_srv|https # Admin from admin client IP +pass ipproto icmp type 8 code 0 proto none from admin_client to firewall_all # Allow Ping from admin client IP +pass from admin_client on wan to Firewall_wan port ssh # Allow SSH from admin client IP +separator color="c0c0c0" comment="HA policy" collapse="0" +pass from Network_ha on ha to Firewall_ha # Allow traffic between HA interfaces +separator color="c0c0c0" comment="Default policy" collapse="0" +block from any to any # Block all +EOFILTER +$SBIN/enfilter 01 + +# Enable SSH +$SETCONF $CF/system SSH State 1 +$SETCONF $CF/system SSH Password 1 +$SBIN/enservice + +# HA configuration +if [ $HA_ID -eq 1 ] +then + cat << EOHA > /tmp/ha.cfg +CONFIG HA CREATE password=stormshield ifname=ha Unicast=1 secure=1 +CONFIG HA ACTIVATE +EOHA + +$SBIN/nsrpc -f -c /tmp/ha.cfg -l /tmp/ha.output admin:$PASSWORD@127.0.0.1:1300 +else + cat << EOHA > /tmp/ha.cfg +CONFIG HA JOIN password=stormshield ip=$HA_PRIMARY_IP +CONFIG HA ACTIVATE +EOHA + +(sleep 300 && $SBIN/nsrpc -f -c /tmp/ha.cfg -l /tmp/ha.output admin:$PASSWORD@127.0.0.1:1300 && sleep 60 && hasync)& +fi + +# Configure licence +if [ $SERIAL_NUMBER ] +then + cat << EOUPDATEUPLOAD > /tmp/update-upload.cfg +SYSTEM UPDATE UPLOAD < /tmp/vminit-$SERIAL_NUMBER.maj +EOUPDATEUPLOAD + + cat << EOUPDATEAPPLY > /tmp/update-activate.cfg +SYSTEM UPDATE ACTIVATE +EOUPDATEAPPLY + +(while [ ! -f /tmp/vminit-$SERIAL_NUMBER.maj ]; do echo "Waiting for update file..." >> /tmp/update-upload.output && sleep 1; done && echo "Update file uploaded" >> /tmp/update-upload.output && sleep 10 && $SBIN/nsrpc -f -c /tmp/update-upload.cfg -l /tmp/update-upload.output admin:$PASSWORD@127.0.0.1:1300 && sed -i '' 's/VMSNSX00Z0000A0/'$SERIAL_NUMBER'/g' $CF/HA/hacluster && $SBIN/nsrpc -f -c /tmp/update-activate.cfg -l /tmp/update-activate.output admin:$PASSWORD@127.0.0.1:1300)& +fi \ No newline at end of file diff --git a/network/stormshield-sns-firewall/variables.tf b/network/stormshield-sns-firewall/variables.tf new file mode 100644 index 00000000..8d88644a --- /dev/null +++ b/network/stormshield-sns-firewall/variables.tf @@ -0,0 +1,57 @@ +variable ovh_os_instance_name { + type = string + default = "stormshield" +} + +variable ovh_os_instance_password { + type = string +} + +variable ssh_public_key { + type = string +} + +variable admin_client_ip { + type = string +} + +variable ovh_os_instance_wan_ip { + type = list(string) +} + +variable ovh_os_instance_wan_mask { + type = string +} + +variable ovh_os_instance_wan_gw { + type = string +} + +variable ovh_os_instance_region { + type = string + default = "SBG7" +} + +variable ovh_os_instance_image_name { + type = string + default = "utm-SNS-EVA-4.8.11" +} + +variable ovh_os_instance_flavor_name { + type = string + default = "b3-16" +} + +variable ovh_os_private_network_vlan_id { + type = number + default = 200 +} + +variable ovh_os_private_network_ha_vlan_id { + type = number + default = 199 +} + +variable stormshield_serial_number { + type = list(string) +} \ No newline at end of file