Skip to content

Commit 059229d

Browse files
committed
Merge branch 'feature/aws-terraform-security' into 'main'
feat: add bind_host options and conditional Grafana access See merge request postgres-ai/postgres_ai!60
2 parents 754cc4f + d3a6bab commit 059229d

File tree

8 files changed

+226
-49
lines changed

8 files changed

+226
-49
lines changed

docker-compose.yml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ services:
3535
"pg_stat_statements.track=all",
3636
]
3737
ports:
38-
- "55432:5432"
38+
- "${BIND_HOST:-}55432:5432"
3939
volumes:
4040
- target_db_data:/var/lib/postgresql/data
4141
- ./config/target-db/init.sql:/docker-entrypoint-initdb.d/init.sql
@@ -49,7 +49,7 @@ services:
4949
POSTGRES_USER: postgres
5050
POSTGRES_PASSWORD: postgres
5151
ports:
52-
- "55433:5432"
52+
- "${BIND_HOST:-}55433:5432"
5353
volumes:
5454
- sink_postgres_data:/var/lib/postgresql/data
5555
- ./config/sink-postgres/init.sql:/docker-entrypoint-initdb.d/init.sql
@@ -59,7 +59,7 @@ services:
5959
image: prom/prometheus:v3.4.2
6060
container_name: sink-prometheus
6161
ports:
62-
- "59090:9090"
62+
- "${BIND_HOST:-}59090:9090"
6363
volumes:
6464
- ./config/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
6565
- prometheus_data:/prometheus
@@ -83,7 +83,7 @@ services:
8383
"--web-addr=:8080",
8484
]
8585
ports:
86-
- "58080:8080"
86+
- "${BIND_HOST:-}58080:8080"
8787
depends_on:
8888
- sources-generator
8989
- sink-postgres
@@ -104,8 +104,8 @@ services:
104104
"--web-addr=:8089",
105105
]
106106
ports:
107-
- "58089:8089"
108-
- "59091:9091"
107+
- "${BIND_HOST:-}58089:8089"
108+
- "${BIND_HOST:-}59091:9091"
109109
depends_on:
110110
- sources-generator
111111
- sink-prometheus
@@ -123,7 +123,7 @@ services:
123123
GF_SECURITY_ADMIN_PASSWORD: ${GF_SECURITY_ADMIN_PASSWORD:-demo}
124124
GF_INSTALL_PLUGINS: yesoreyeram-infinity-datasource
125125
ports:
126-
- "3000:3000"
126+
- "${GRAFANA_BIND_HOST:-}3000:3000"
127127
volumes:
128128
- grafana_data:/var/lib/grafana
129129
- ./config/grafana/provisioning:/etc/grafana/provisioning
@@ -144,7 +144,7 @@ services:
144144
depends_on:
145145
- sink-prometheus
146146
ports:
147-
- "55000:5000"
147+
- "${BIND_HOST:-}55000:5000"
148148
restart: unless-stopped
149149
# PostgreSQL Reports Generator - Runs reports after 1 hour
150150
postgres-reports:

terraform/aws/QUICKSTART.md

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,15 @@ Uncomment and set all required parameters:
2929
- `instance_type` - EC2 instance type (e.g., t3.medium)
3030
- `data_volume_size` - data disk size in GiB
3131
- `data_volume_type` / `root_volume_type` - volume types (gp3, st1, sc1)
32-
- `allowed_ssh_cidr` / `allowed_cidr_blocks` - CIDR blocks for access
32+
- `allowed_ssh_cidr` - CIDR blocks for SSH access (use `["YOUR_IP/32"]`, get IP: `curl ifconfig.me`)
33+
- `allowed_cidr_blocks` - CIDR blocks for Grafana (use `[]` to disable direct access = SSH tunnel only, most secure)
3334
- `use_elastic_ip` - allocate Elastic IP (true/false)
3435
- `grafana_password` - Grafana admin password
35-
- `postgres_ai_version` - git branch/tag (optional, defaults to "main")
36+
- `bind_host` - port binding for internal services (optional, defaults to `"127.0.0.1:"`)
37+
38+
Optional parameters:
39+
- `grafana_bind_host` - Grafana port binding (defaults to `"127.0.0.1:"` for SSH tunnel)
40+
- `postgres_ai_version` - git branch/tag (defaults to "0.10")
3641

3742
## Add monitoring instances
3843

@@ -70,13 +75,38 @@ terraform output ssh_command
7075

7176
## Access
7277

78+
### Grafana
79+
80+
Terraform will show the correct access method after deployment:
81+
82+
```bash
83+
# See access instructions for your configuration
84+
terraform output grafana_access_hint
85+
```
86+
87+
**SSH tunnel access (default):**
88+
89+
```bash
90+
# Create SSH tunnel
91+
ssh -i ~/.ssh/postgres-ai-key.pem -NL 3000:localhost:3000 ubuntu@$(terraform output -raw public_ip)
92+
93+
# Open browser
94+
open http://localhost:3000
95+
# Login: monitor / <password from terraform.tfvars>
96+
```
97+
98+
**Direct access (if configured):**
99+
73100
```bash
74101
# Grafana dashboard
75102
open $(terraform output -raw grafana_url)
76103
# Login: monitor / <password from terraform.tfvars>
104+
```
77105

78-
# SSH
79-
ssh -i ~/.ssh/postgres-ai-key.pem ubuntu@$(terraform output -raw external_ip)
106+
### SSH
107+
108+
```bash
109+
ssh -i ~/.ssh/postgres-ai-key.pem ubuntu@$(terraform output -raw public_ip)
80110
```
81111

82112
## Operations

terraform/aws/README.md

Lines changed: 71 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ instance_type = "t3.medium"
4949
data_volume_size = 50
5050
data_volume_type = "gp3" # gp3 (SSD), st1 (HDD), sc1 (HDD)
5151
root_volume_type = "gp3"
52-
allowed_ssh_cidr = ["203.0.113.0/24"]
53-
allowed_cidr_blocks = ["203.0.113.0/24"]
52+
allowed_ssh_cidr = ["YOUR_IP/32"] # Replace with your actual IP address
53+
allowed_cidr_blocks = [] # Empty list = no direct access, SSH tunnel required (most secure)
5454
use_elastic_ip = true
5555
grafana_password = "YourSecurePassword123!"
5656
```
@@ -59,9 +59,13 @@ grafana_password = "YourSecurePassword123!"
5959

6060
```hcl
6161
# OPTIONAL (have defaults)
62-
postgres_ai_api_key = "your-api-key" # For uploading reports
63-
enable_demo_db = false # Demo database (default: true)
64-
postgres_ai_version = "main" # Git branch/tag (default: "main")
62+
postgres_ai_api_key = "your-api-key" # For uploading reports
63+
enable_demo_db = false # Demo database (default: false)
64+
postgres_ai_version = "0.10" # Git branch/tag (default: "0.10")
65+
bind_host = "127.0.0.1:" # Internal services on localhost (default, most secure)
66+
# bind_host = "" # OR: Bind to all interfaces
67+
grafana_bind_host = "127.0.0.1:" # Grafana on localhost only (default, use SSH tunnel)
68+
# grafana_bind_host = "" # OR: Grafana accessible from outside
6569
6670
monitoring_instances = [
6771
{
@@ -85,15 +89,17 @@ instance_type = "t3.medium"
8589
data_volume_size = 100
8690
data_volume_type = "gp3"
8791
root_volume_type = "gp3"
88-
allowed_ssh_cidr = ["203.0.113.0/24"]
89-
allowed_cidr_blocks = ["203.0.113.0/24"]
92+
allowed_ssh_cidr = ["YOUR_IP/32"] # Replace with your actual IP
93+
allowed_cidr_blocks = [] # Empty list = no direct access (most secure)
9094
use_elastic_ip = true
9195
grafana_password = "SecurePassword123!"
9296
9397
# OPTIONAL
9498
postgres_ai_api_key = "your-api-key"
9599
enable_demo_db = false
96-
postgres_ai_version = "v0.9"
100+
postgres_ai_version = "0.10"
101+
bind_host = "127.0.0.1:" # Default
102+
grafana_bind_host = "127.0.0.1:" # Default
97103
98104
monitoring_instances = [
99105
{
@@ -113,7 +119,34 @@ monitoring_instances = [
113119
```bash
114120
terraform output ssh_command
115121
# Or directly:
116-
ssh -i ~/.ssh/postgres-ai-key.pem ubuntu@$(terraform output -raw external_ip)
122+
ssh -i ~/.ssh/postgres-ai-key.pem ubuntu@$(terraform output -raw public_ip)
123+
```
124+
125+
### Grafana access
126+
127+
Terraform automatically detects the correct access method based on your configuration:
128+
129+
```bash
130+
# Get access instructions for your setup
131+
terraform output grafana_access_hint
132+
```
133+
134+
**Option 1: SSH tunnel (when `allowed_cidr_blocks = []` or `grafana_bind_host = "127.0.0.1:"`)**
135+
136+
```bash
137+
# Create SSH tunnel
138+
ssh -i ~/.ssh/postgres-ai-key.pem -NL 3000:localhost:3000 ubuntu@$(terraform output -raw public_ip)
139+
140+
# Open browser
141+
open http://localhost:3000
142+
# Login: monitor / <your grafana_password>
143+
```
144+
145+
**Option 2: Direct access (when `allowed_cidr_blocks = ["YOUR_IP/32"]` and `grafana_bind_host = ""`)**
146+
147+
```bash
148+
# Open browser
149+
open $(terraform output -raw grafana_url)
117150
```
118151

119152
### Service management
@@ -176,14 +209,25 @@ sudo docker-compose up -d
176209

177210
### Recommendations
178211

179-
1. Restrict SSH access:
212+
1. **Most secure setup (SSH tunnel only)**:
180213
```hcl
181-
allowed_ssh_cidr = ["your.ip.address/32"]
214+
allowed_ssh_cidr = ["your.ip.address/32"]
215+
allowed_cidr_blocks = [] # Empty list = no direct access to Grafana from anywhere
216+
bind_host = "127.0.0.1:"
217+
grafana_bind_host = "127.0.0.1:"
218+
```
219+
220+
Access Grafana via SSH tunnel:
221+
```bash
222+
ssh -i ~/.ssh/key.pem -NL 3000:localhost:3000 ubuntu@instance-ip
182223
```
183224

184-
2. Restrict Grafana access:
225+
2. **Production with direct Grafana access**:
185226
```hcl
186-
allowed_cidr_blocks = ["your.office.ip/24"]
227+
allowed_ssh_cidr = ["YOUR_OFFICE_IP/24"] # Replace with your office network
228+
allowed_cidr_blocks = ["YOUR_OFFICE_IP/24"] # Replace with your office network
229+
bind_host = "127.0.0.1:" # Internal services protected
230+
grafana_bind_host = "" # Grafana accessible
187231
```
188232

189233
3. Use AWS Systems Manager instead of SSH:
@@ -193,6 +237,13 @@ aws ssm start-session --target $(terraform output -raw instance_id)
193237

194238
4. Automate backups with AWS Backup or cron.
195239

240+
### Port binding configuration
241+
242+
- `bind_host = "127.0.0.1:"` - Internal services only on localhost (recommended)
243+
- `bind_host = ""` - Internal services on all interfaces
244+
- `grafana_bind_host = "127.0.0.1:"` - Grafana only via SSH tunnel (default)
245+
- `grafana_bind_host = ""` - Grafana accessible from network
246+
196247
## Monitoring
197248

198249
### CloudWatch metrics
@@ -231,6 +282,13 @@ ssh ubuntu@your-ip "sudo docker ps -a"
231282
### No access to Grafana
232283

233284
```bash
285+
# Check if allowed_cidr_blocks is empty (SSH tunnel required)
286+
grep allowed_cidr_blocks terraform.tfvars
287+
288+
# If empty, use SSH tunnel
289+
ssh -i ~/.ssh/key.pem -NL 3000:localhost:3000 ubuntu@your-ip
290+
# Then open http://localhost:3000
291+
234292
# Check Security Group
235293
aws ec2 describe-security-groups \
236294
--group-ids $(terraform output -raw security_group_id)

terraform/aws/main.tf

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ data "aws_ami" "ubuntu" {
4646
}
4747
}
4848

49-
# VPC (simplified - use default or create minimal)
49+
# VPC
5050
resource "aws_vpc" "main" {
5151
cidr_block = "10.0.0.0/16"
5252
enable_dns_hostnames = true
@@ -113,13 +113,17 @@ resource "aws_security_group" "main" {
113113
cidr_blocks = var.allowed_ssh_cidr
114114
}
115115

116-
# Grafana
117-
ingress {
118-
description = "Grafana"
119-
from_port = 3000
120-
to_port = 3000
121-
protocol = "tcp"
122-
cidr_blocks = var.allowed_cidr_blocks
116+
# Grafana (optional, only if allowed_cidr_blocks is not empty)
117+
# If empty, use SSH tunnel: ssh -i ~/.ssh/key.pem -NL 3000:localhost:3000 ubuntu@<instance-ip>
118+
dynamic "ingress" {
119+
for_each = length(var.allowed_cidr_blocks) > 0 ? [1] : []
120+
content {
121+
description = "Grafana"
122+
from_port = 3000
123+
to_port = 3000
124+
protocol = "tcp"
125+
cidr_blocks = var.allowed_cidr_blocks
126+
}
123127
}
124128

125129
# Allow all outbound
@@ -168,6 +172,8 @@ resource "aws_instance" "main" {
168172
postgres_ai_api_key = var.postgres_ai_api_key
169173
enable_demo_db = var.enable_demo_db
170174
postgres_ai_version = var.postgres_ai_version
175+
bind_host = var.bind_host
176+
grafana_bind_host = var.grafana_bind_host
171177
instances_yml = templatefile("${path.module}/instances.yml.tpl", {
172178
monitoring_instances = var.monitoring_instances
173179
enable_demo_db = var.enable_demo_db

0 commit comments

Comments
 (0)