13 KiB
S2I2S Project Setup
This OpenTofu configuration sets up the core infrastructure components for the S2I2S OpenStack project.
Overview
The project-setup module creates the following resources:
Virtual Machines
| VM | Purpose | Flavor | Private IP | Boot disk | Floating IP | DNS Record |
|---|---|---|---|---|---|---|
| SSH Jump Proxy | Secure SSH gateway for accessing internal VMs | m2.small | 10.10.0.5 | 30 GB | Yes | ssh-jump-proxy.s2i2s.cloud.isti.cnr.it |
| Internal CA | Certificate Authority for internal services | m1.small | 10.10.0.4 | 10 GB | No | - |
| HAProxy L7 (x2) | Layer 7 load balancers behind Octavia | m1.medium | 10.10.0.11, 10.10.0.12 | 10 GB | No | - |
| Prometheus | Monitoring and metrics collection | m1.medium | 10.10.0.10 | 10 GB | Yes | prometheus.s2i2s.cloud.isti.cnr.it,
alertmanager.s2i2s.cloud.isti.cnr.it (CNAME) |
All VMs run Ubuntu 24.04
(Ubuntu-Noble-24.04.img) and are provisioned with the
standard ubuntu2404.sh cloud-init user data script. Boot
disks are volume-backed with
delete_on_termination = false.
Load Balancer
An OVN-based Octavia load balancer
(s2i2s-cloud-l4-load-balancer) provides L4 load
balancing:
- Provider:
ovn(not amphora) - VIP: 10.10.0.20, on the main private subnet
- Floating IP: Yes
- DNS Record:
octavia-main-lb.s2i2s.cloud.isti.cnr.it - Backend: HAProxy L7 instances (anti-affinity for HA)
| Listener | Port | Protocol | Pool method | Health Check |
|---|---|---|---|---|
| HTTP | 80 | TCP | SOURCE_IP_PORT | TCP connect |
| HTTPS | 443 | TCP | SOURCE_IP_PORT | TCP connect |
| Stats | 8880 | TCP | SOURCE_IP_PORT | TCP connect |
Consequences of the OVN provider
The OVN driver is lighter than amphora (no amphora VMs, uses the main subnet directly), but it constrains the configuration in ways that are visible throughout this setup:
- No
allowed_cidrson listeners. Access restrictions must be enforced by security groups or inside HAProxy itself. This is why the stats port is filtered in the security group below. - Only
ROUND_ROBINandSOURCE_IP_PORTare available as pool methods;LEAST_CONNECTIONSis not supported. - Only
TCPandUDP-CONNECThealth monitors; HTTP/HTTPS monitors are not supported, so all three pools are probed with a plain TCP connect. - The client source IP is preserved. OVN does not SNAT the traffic to an amphora VIP, so the HAProxy L7 backends see the real client address rather than an address of the load balancer. Only health-monitor probes and hairpinned traffic originate from the private subnet.
Security Groups
| Security Group | Purpose | Use On |
|---|---|---|
default_for_all |
Default rules: SSH via jump proxy, ICMP, Prometheus node exporter | All VMs |
ssh_access_to_the_jump_node |
SSH access from VPN endpoints | SSH Jump Proxy only |
debugging_from_jump_node |
Web debugging via SSH tunnels (ports 80, 443, 8100) | VMs needing debug access |
traffic_from_the_main_load_balancers |
HTTP/HTTPS from the HAProxy L7 IPs (ports 80, 443, 8080, 8888) | Backend web services |
traffic_from_main_lb_to_haproxy_l7 |
Public HTTP/HTTPS plus filtered access to the stats port | HAProxy L7 VMs |
public_web_service |
HTTP/HTTPS from anywhere | Public-facing services with floating IP |
restricted_web_service |
HTTP from anywhere, HTTPS from VPNs only | Restricted services with floating IP |
prometheus_access_from_grafana |
HTTPS access from public Grafana server | Prometheus VM |
Do not confuse the two load balancer groups.
traffic_from_main_lb_to_haproxy_l7sits on the HAProxy L7 VMs themselves and faces the internet.traffic_from_the_main_load_balancerssits on the backend services behind HAProxy: since HAProxy is an L7 proxy that opens new connections from its own address, the client IP is not preserved on that hop, so its sources stay restricted to 10.10.0.11 and 10.10.0.12 and it must never be opened to0.0.0.0/0.
traffic_from_main_lb_to_haproxy_l7
Because the OVN load balancer preserves the client source IP (see above), the public listeners on the HAProxy L7 VMs cannot be restricted to the private subnet:
| Port | Source | Rationale |
|---|---|---|
| 80 | 0.0.0.0/0 |
HTTP arrives with the original client address |
| 443 | 0.0.0.0/0 |
HTTPS arrives with the original client address |
| 8880 | main private subnet | Health monitor probes for the stats pool |
| 8880 | D4S VPN 1/2, S2I2S VPN 1/2, InfraScience network | Human access to the HAProxy stats page |
| 8880 | 10.10.0.10/32 and the Prometheus floating IP | Prometheus scrapes the stats, over the private network and through the LB public IP |
| 10000 | 10.10.0.11/32, 10.10.0.12/32 | Peer traffic between the two HAProxy instances |
The stats port is deliberately not open to
0.0.0.0/0: since the OVN provider does not support
allowed_cidrs on the listener, this security group is the
only place where port 8880 can be restricted, and client-IP preservation
is precisely what makes filtering by real source address possible. The
per-source rules are generated with a for_each map in
haproxy.tf.
Storage
- Prometheus Data Volume: 100 GB SSD (CephSSD) with online resize enabled
Architecture
Internet
|
+-------------------+-------------------+
| | |
[SSH Jump Proxy] [Octavia LB] [Prometheus]
| (Floating IP) (Floating IP)
| |
| +-------+-------+
| | |
| [HAProxy L7-01] [HAProxy L7-02]
| | |
| +-------+-------+
| |
+-------------------+
|
[Internal Network]
|
+-------+-------+
| |
[Internal CA] [Backend VMs]
Prerequisites
- The
main_net_dns_routerconfiguration must be applied first (creates network, subnet, DNS zone) - SSH key must be configured in the OpenStack project
- OpenStack credentials must be configured (via
clouds.yamlor environment variables) - OpenTofu — this repository is
driven with
tofu, notterraform terraform-provider-openstack/openstack>= 2.0.0; the configuration is verified against 3.4.0
Usage
# Initialize OpenTofu
tofu init
# Review the plan
tofu plan
# Apply the configuration
tofu applyWhen re-initialising a directory that was last used with
terraform, runtofu init -upgrade. The provider address in.terraform.lock.hclmoves fromregistry.terraform.iotoregistry.opentofu.org; that is expected.Provider 3.x tracks a
statefulattribute onopenstack_networking_secgroup_v2that 1.53.0 did not. The first refresh after upgrading therefore reports+ stateful = falseas out-of-band drift on every security group. It produces no planned change and can be ignored.
SSH Jump Proxy Configuration
To access VMs in the S2I2S cloud, you must use the SSH jump proxy.
Add the following configuration to your ~/.ssh/config
file:
# S2I2S SSH Jump Proxy
# Replace <your_username> with your actual username
Host s2i2s-jump
HostName ssh-jump-proxy.s2i2s.cloud.isti.cnr.it
User <your_username>
IdentityFile ~/.ssh/your_private_key
ForwardAgent yes
# Keep connection alive
ServerAliveInterval 60
ServerAliveCountMax 3
# Pattern match for all S2I2S internal hosts by IP
# Matches any IP in the 10.10.0.x range
# Usage: ssh 10.10.0.10
Host 10.10.0.*
User <your_username>
ForwardAgent yes
ProxyJump <your_username>@ssh-jump-proxy.s2i2s.cloud.isti.cnr.it
# Alternative: named aliases for specific internal hosts
Host s2i2s-prometheus
HostName 10.10.0.10
User <your_username>
ForwardAgent yes
ProxyJump <your_username>@ssh-jump-proxy.s2i2s.cloud.isti.cnr.it
Host s2i2s-ca
HostName 10.10.0.4
User <your_username>
ForwardAgent yes
ProxyJump <your_username>@ssh-jump-proxy.s2i2s.cloud.isti.cnr.it
Host s2i2s-haproxy-01
HostName 10.10.0.11
User <your_username>
ForwardAgent yes
ProxyJump <your_username>@ssh-jump-proxy.s2i2s.cloud.isti.cnr.it
Host s2i2s-haproxy-02
HostName 10.10.0.12
User <your_username>
ForwardAgent yes
ProxyJump <your_username>@ssh-jump-proxy.s2i2s.cloud.isti.cnr.it
SSH Usage Examples
# Connect to the jump proxy directly
ssh s2i2s-jump
# Connect to an internal VM by IP (using pattern match from ssh config)
ssh 10.10.0.10
# Connect to a named internal host (if configured in ssh config)
ssh s2i2s-prometheus
# Connect without ssh config (replace <your_username>)
ssh -J <your_username>@ssh-jump-proxy.s2i2s.cloud.isti.cnr.it <your_username>@10.10.0.10
# Copy a file to an internal VM
scp -J <your_username>@ssh-jump-proxy.s2i2s.cloud.isti.cnr.it localfile.txt <your_username>@10.10.0.10:/tmp/
# Forward a local port to an internal service
ssh -L 8080:10.10.0.30:80 s2i2s-jump
# Create a SOCKS proxy through the jump host
ssh -D 1080 s2i2s-jump
# Then configure your browser to use SOCKS5 proxy at localhost:1080SSH Debugging via Tunnel
For debugging web applications on internal VMs, you can create SSH tunnels:
# Forward local port 8100 to a Tomcat debug port on internal VM
# (requires s2i2s-jump defined in ssh config)
ssh -L 8100:10.10.0.50:8100 s2i2s-jump
# Forward local port 8080 to HTTP on internal VM
ssh -L 8080:10.10.0.50:80 s2i2s-jump
# Forward local port 8443 to HTTPS on internal VM
ssh -L 8443:10.10.0.50:443 s2i2s-jump
# Without ssh config (replace <your_username>)
ssh -L 8080:10.10.0.50:80 <your_username>@ssh-jump-proxy.s2i2s.cloud.isti.cnr.itOutputs
The module exports the following outputs for use by other OpenTofu configurations:
VM IDs and IPs
ssh_jump_proxy_id,ssh_jump_proxy_public_ip,ssh_jump_proxy_hostnameinternal_ca_idmain_haproxy_l7_idsprometheus_server_id,prometheus_public_ip,prometheus_hostname
Load Balancer Outputs
main_loadbalancer_id,main_loadbalancer_ip,main_loadbalancer_public_ip,main_loadbalancer_hostname
Security Group Outputs
default_security_group,default_security_group_id,default_security_group_nameaccess_to_the_jump_proxydebuggingtraffic_from_main_haproxypublic_webrestricted_webmain_lb_to_haproxy_l7_security_groupprometheus_access_from_grafana
Network Outputs (re-exported from main_net_dns_router)
dns_zone,dns_zone_idmain_private_network,main_private_subnet,main_subnet_network_idbasic_services_ip,main_haproxy_l7_ip
Image Outputs (re-exported from labs_common_variables)
ubuntu_2404,ubuntu2404_data_file- the image and cloud-init script used by every VM hereubuntu_2204,ubuntu2204_data_file- kept for consumers still on 22.04
File Structure
project-setup/
├── provider.tf # OpenStack provider configuration
├── main.tf # Module references and local variables
├── security-groups.tf # All security group definitions
├── ssh-jump-proxy.tf # SSH jump proxy VM and floating IP
├── internal-ca.tf # Internal CA VM
├── haproxy.tf # HAProxy L7 VMs (pair with anti-affinity)
├── prometheus.tf # Prometheus VM with data volume
├── octavia.tf # OVN-based Octavia load balancer
├── outputs.tf # Output definitions
└── README.md # This file
Dependencies
This module depends on:
../main_net_dns_router- Network, subnet, router, and DNS zone../variables- Project-specific variables../../modules/labs_common_variables- Common variables (images, flavors, etc.)../../modules/ssh-key-ref- SSH key reference
Notes
- The HAProxy L7 VMs are deployed with anti-affinity to ensure they run on different hypervisors
- All VMs use volume-backed boot disks with
delete_on_termination = falsefor data persistence - The Prometheus data volume uses CephSSD storage for better I/O performance
- Volumes have
enable_online_resize = truefor live resizing capability - Security groups are designed to minimize attack surface while allowing necessary traffic flows
- Every instance carries
ignore_changes = [key_pair, user_data, network]so that rotating the SSH key or the cloud-init script does not trigger a rebuild - The main load balancer uses the OVN Octavia provider; see Consequences of the OVN provider before changing listeners, pool methods, health monitors or the HAProxy security group