Openstack Security Group module (VKCS)
Terraform module designed to simplify and standardize day-by-day Openstack security group (firewall rulset) creation and operations.
Module has been only tested to work with VKCS (mcs.mail.ru) cloud platform, but feel free to use it as a starting point to create your own module for your Openstack hostring provider.
Features
- Create Group and Rules in one module
- Set list of ports and list of adressess to create Rules for every combnation
- Use one Port value to set port range or only one port
- Set protocols and direction
- Use ip-address or other Security Group ID as destination
- Standardized names for created sub-objects
- Few times less code
Without module
86 lines of messy code hard to maintain
resource "openstack_networking_secgroup_v2" "i_example" {
name = "i_example"
description = "Group to access some service"
}
resource "openstack_networking_secgroup_rule_v2" "i_example_1" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 80
port_range_max = 80
remote_ip_prefix = "10.10.0.0/24"
security_group_id = "${openstack_networking_secgroup_v2.i_example.id}"
}
resource "openstack_networking_secgroup_rule_v2" "i_example_2" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 80
port_range_max = 80
remote_ip_prefix = "10.20.0.0/24"
security_group_id = "${openstack_networking_secgroup_v2.i_example.id}"
}
resource "openstack_networking_secgroup_rule_v2" "i_example_3" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 80
port_range_max = 80
remote_ip_prefix = "10.30.0.0/24"
security_group_id = "${openstack_networking_secgroup_v2.i_example.id}"
}
resource "openstack_networking_secgroup_rule_v2" "i_example_4" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 80
port_range_max = 80
remote_ip_prefix = "10.40.10.1/32"
security_group_id = "${openstack_networking_secgroup_v2.i_example.id}"
}
resource "openstack_networking_secgroup_rule_v2" "i_example_5" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 443
port_range_max = 443
remote_ip_prefix = "10.10.0.0/24"
security_group_id = "${openstack_networking_secgroup_v2.i_example.id}"
}
resource "openstack_networking_secgroup_rule_v2" "i_example_6" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 443
port_range_max = 443
remote_ip_prefix = "10.20.0.0/24"
security_group_id = "${openstack_networking_secgroup_v2.i_example.id}"
}
resource "openstack_networking_secgroup_rule_v2" "i_example_7" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 443
port_range_max = 443
remote_ip_prefix = "10.30.0.0/24"
security_group_id = "${openstack_networking_secgroup_v2.i_example.id}"
}
resource "openstack_networking_secgroup_rule_v2" "i_example_8" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 443
port_range_max = 443
remote_ip_prefix = "10.40.10.1/32"
security_group_id = "${openstack_networking_secgroup_v2.i_example.id}"
}
resource "openstack_networking_secgroup_rule_v2" "i_example_9" {
direction = "ingress"
ethertype = "IPv4"
protocol = "udp"
port_range_min = 9000
port_range_max = 11000
remote_ip_prefix = "192.168.0.0/24"
security_group_id = "${openstack_networking_secgroup_v2.i_example.id}"
}
With module
24 lines of easy-to-read code
module "i_example" {
source = "git::https://github.com/realscorp/tf-openstack-vkcs-secgroup.git?ref=v1.0.0"
name = "i_example"
description = "Group to access some service"
rules = [{
direction = "ingress"
protocol = "tcp"
ports = ["80", "443"]
remote_ips = {
"Office 1" = "10.10.0.0/24"
"Office 2" = "10.20.0.0/24"
"Office 3" = "10.30.0.0/24"
"Server" = "10.40.10.1"
}
},
{
direction = "ingress"
protocol = "udp"
ports = ["9000-11000"]
remote_ips = {
"Remote access VPN" = "192.168.0.0/24"
}
}]
}
Variables
- name - (string; required) - Security Group name
- description - (string; default: "Terraform Managed") - SG description
- delete_default_rules - (boolean; default: true) - delete default platform rules (e.g. allow all proto egress to 0.0.0.0/0)
- rules - (list(object); required) - list of rules
- direction - (string; required) - connection direction for rule ingress / egress
- protocol - (string; required) - connection IP protocol, e.g. (tcp / udp / icmp / other)
- ports - (list of string; required) - tcp/udp port list, each item should be x for one port or x-y for port range
- remote_ips - (map of string; required) - addresses list in form of "address name" = "address value", where address value is subnet in CIDR notation a.b.c.d/m or host a.b.c.d/32. It can also be ID of another Security Group if address name starts with prefix SG (see Examples)
Requirements
You should have Openstack provider declared and set up in your root module.
Example of main.tf in your root module
terraform {
required_providers {
openstack = {
source = "terraform-provider-openstack/openstack"
version = "1.33.0"
}
}
}
Example of init.sh to set-up provider via enviromental variables
#!/usr/bin/env bash
# Openstack (VKCS)
export OS_AUTH_URL="https://infra.mail.ru:35357/v3/"
export OS_PROJECT_ID="xxxxxxxxxxxxxxxxxxxxxxx"
export OS_REGION_NAME="RegionOne"
export OS_USER_DOMAIN_NAME="users"
# Remove legacy vars
unset OS_TENANT_ID
unset OS_TENANT_NAME
unset OS_PROJECT_NAME
unset OS_PROJECT_DOMAIN_ID
# Ask for credentials if it is not set already
if [[ -z $OS_USERNAME ]] || [[ -z $OS_PASSWORD ]]; then
echo "Please enter your OpenStack Username for project $OS_PROJECT_ID: "
read -sr OS_USERNAME_INPUT
export OS_USERNAME=$OS_USERNAME_INPUT
echo "Please enter your OpenStack Password for project $OS_PROJECT_ID as user $OS_USERNAME: "
read -sr OS_PASSWORD_INPUT
export OS_PASSWORD=$OS_PASSWORD_INPUT
fi
Examples
Simple Security Group with 2 rules
module "i_web_dns_example" {
source = "git::https://github.com/realscorp/tf-openstack-vkcs-secgroup.git?ref=v1.0.0"
name = "i_web_dns_example"
description = "Group to access some service"
rules = [{
direction = "ingress"
protocol = "tcp"
ports = ["80", "443"]
remote_ips = {
"Office 1" = "10.10.0.0/24"
"Office 2" = "10.20.0.0/24"
"Office 3" = "10.30.0.0/24"
}
},{
direction = "ingress"
protocol = "udp"
ports = ["53"]
remote_ips = {
"All internal" = "10.0.0.0/8"
}
}]
}
Create Security Group alongside with Instance
# Create Security Group alongside with Instance
module "i_int_test" {
source = "git::https://github.com/realscorp/tf-openstack-vkcs-secgroup.git?ref=v1.0.0"
name = "i_int_test"
rules = [{
direction = "ingress"
protocol = "tcp"
ports = ["80","443"]
remote_ips = {
"Office IT subnet" = "10.0.0.0/24"
}
}]
}
# We'll set even optional variables
module "windows-vm" {
source = "git::https://github.com/realscorp/tf-openstack-vkcs-vm.git?ref=v1.0.0"
name = "windows-vm"
flavor = "Standard-4-8-80"
az = "DP1"
dns_ttl = 600
region = "RegionOne"
image = "Windows-Server-2019Std-en.202105"
winrm_cert_path = "~/.winrm/winrm.der"
ssh_key_name = "ansible-key"
user_data = file(pathexpand("${path.module}/some.userdata"))
metadata = {
os = "windows"
os_ver = "2019"
service = "test"
}
ports = [
{
network = "network-1"
subnet = "subnet-1"
ip_address = "10.1.0.66"
dns_record = true
dns_zone = "domain.example.com"
security_groups = ["i_default","o_default"]
security_groups_ids = [module.i_int_test.sg.id] # Id of a SG we created
},
{
network = "ext-net"
subnet = ""
ip_address = ""
dns_record = false
dns_zone = ""
security_groups = ["o_default"]
security_groups_ids = []
}
]
volumes = {
root = {
type = "ceph-ssd"
size = 50
}
}
}
Create 2 Security Groups with ID of first group as rule address in second group
# Due to Terrafrom limitations you should apply this code in 2 steps: first create groups without using ID in rule then add ID.
# Or you can use -target CLI option (https://www.terraform.io/cli/commands/apply#target-resource)
module "i_int_ldap" {
source = "git::https://github.com/realscorp/tf-openstack-vkcs-secgroup.git?ref=v1.0.0"
name = "i_int_ldap"
description = "Group to access LDAP service"
rules = [{
direction = "ingress"
protocol = "tcp"
ports = ["389","636"]
remote_ips = {
"Office 1" = "10.10.0.0/16"
"_SG_o_int_ldap" = module.o_int_ldap.id # We use prefix _SG_ to pass ID
}
}
]
}
module "o_int_ldap" {
source = "git::https://github.com/realscorp/tf-openstack-vkcs-secgroup.git?ref=v1.0.0"
name = "o_int_test"
description = "Egress group to access LDAP service"
rules = [{
direction = "egress"
protocol = "tcp"
ports = ["389","636"]
remote_ips = {
"dc1" = "10.1.0.10"
"dc2" = "10.1.0.20"
}
}
]
}
Output usage examples
# Get all SG attributes
output "i_int_example" {
value = module.i_int_example.sg
}
# Get Security Group ID
output "id" {
value = module.i_int_example.id
}