Skip to content

Commit b9e0280

Browse files
authored
Merge pull request #118 from jackby03/feat/110-terraform-provisioner
2 parents 6abf203 + a116f27 commit b9e0280

File tree

12 files changed

+1433
-1
lines changed

12 files changed

+1433
-1
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
106106
- `docs/CLOUD-INIT.md` — usage guide covering quick-start commands, IAM/RBAC requirements, configuration reference, timer management, and troubleshooting for all three cloud-init templates
107107
- Ansible role (`ansible-role/hardbox/`) — Galaxy-compatible role wrapping the hardbox CLI; supports all profiles, audit-only mode, report fetching, rollback on failure, custom profile upload, and systemd re-hardening timer; includes Molecule integration tests for Ubuntu 22.04, Debian 12, and Rocky Linux 9 ([#109](https://github.com/jackby03/hardbox/issues/109))
108108
- `docs/ANSIBLE.md` — Ansible role documentation covering installation, variable reference, example playbooks, CI/CD integration, and Molecule test instructions
109+
- Terraform provider (`terraform-provider/`) — `jackby03/hardbox` provider for the Terraform Registry; exposes `hardbox_apply` resource with SSH-based install, checksum verification, profile selection, findings capture in state, and automatic rollback on destroy; examples for AWS EC2, GCP Compute Engine, and Azure VMs ([#110](https://github.com/jackby03/hardbox/issues/110))
110+
- `docs/TERRAFORM.md` — provider documentation covering installation, provider/resource schema, per-cloud examples, CI/CD snippets, and build-from-source instructions
109111

110112
### Changed
111113
- `install.sh` now resolves release assets via GitHub API instead of hardcoded filenames, improving compatibility across release archive naming formats.
@@ -119,7 +121,6 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
119121

120122
### Planned for v0.3
121123
- `nist-800-53` profile
122-
- Terraform provisioner plugin
123124
- cloud-init support
124125

125126
---

docs/TERRAFORM.md

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
# hardbox — Terraform Provider
2+
3+
The `jackby03/hardbox` Terraform provider applies OS hardening to remote Linux
4+
hosts as part of your infrastructure-as-code workflow. It installs the hardbox
5+
binary, runs a compliance profile, and surfaces audit findings in Terraform state.
6+
7+
Supported targets: **AWS EC2**, **GCP Compute Engine**, **Azure VMs**, and any
8+
SSH-accessible Linux host.
9+
10+
---
11+
12+
## Installation
13+
14+
Add the provider to your `required_providers` block:
15+
16+
```hcl
17+
terraform {
18+
required_providers {
19+
hardbox = {
20+
source = "jackby03/hardbox"
21+
version = "~> 0.3"
22+
}
23+
}
24+
}
25+
```
26+
27+
```bash
28+
terraform init
29+
```
30+
31+
---
32+
33+
## Provider Configuration
34+
35+
```hcl
36+
provider "hardbox" {
37+
# Optional: pin the hardbox binary version installed on all managed hosts.
38+
# Defaults to "latest" if omitted.
39+
hardbox_version = "v0.3.0"
40+
}
41+
```
42+
43+
| Argument | Type | Default | Description |
44+
|---|---|---|---|
45+
| `hardbox_version` | string | `"latest"` | hardbox release tag to install on remote hosts |
46+
47+
---
48+
49+
## Resource: `hardbox_apply`
50+
51+
Provisions a remote Linux host with hardbox OS hardening.
52+
53+
### What it does
54+
55+
1. Connects to the host over SSH.
56+
2. Downloads the hardbox binary and verifies its SHA-256 checksum.
57+
3. Runs `hardbox apply` with the selected compliance profile.
58+
4. Captures the audit report and surfaces finding counts in state.
59+
5. On `terraform destroy`, runs `hardbox rollback apply --last`.
60+
61+
### Arguments
62+
63+
#### SSH connection
64+
65+
| Argument | Required | Description |
66+
|---|---|---|
67+
| `host` | yes | IP address or hostname of the target |
68+
| `port` | no | SSH port (default: `22`) |
69+
| `user` | no | SSH username (default: `root`) |
70+
| `private_key` | no | PEM-encoded SSH private key |
71+
| `agent_socket` | no | Path to SSH agent socket (`$SSH_AUTH_SOCK`) |
72+
73+
#### hardbox options
74+
75+
| Argument | Default | Description |
76+
|---|---|---|
77+
| `profile` | required | Compliance profile name |
78+
| `hardbox_version` | provider default | Override version for this resource |
79+
| `dry_run` | `false` | Preview changes without applying |
80+
| `rollback_on_failure` | `true` | Rollback automatically on failure |
81+
| `report_format` | `"json"` | `json`, `html`, `text`, `markdown` |
82+
| `fail_on_critical` | `true` | Fail apply on critical findings |
83+
| `fail_on_high` | `true` | Fail apply on high findings |
84+
85+
### Computed attributes
86+
87+
| Attribute | Description |
88+
|---|---|
89+
| `id` | `<host>:<profile>@<applied_at>` |
90+
| `applied_at` | RFC3339 timestamp of last apply |
91+
| `installed_version` | hardbox version installed on the host |
92+
| `report_content` | Full audit report (JSON/HTML/text) |
93+
| `findings` | Map of severity counts: `{critical, high, medium, low, info}` |
94+
95+
---
96+
97+
## Examples
98+
99+
### AWS EC2
100+
101+
```hcl
102+
resource "hardbox_apply" "web" {
103+
host = aws_instance.web.public_ip
104+
user = "ubuntu"
105+
private_key = file("~/.ssh/id_rsa")
106+
107+
profile = "cloud-aws"
108+
report_format = "json"
109+
fail_on_critical = true
110+
fail_on_high = true
111+
rollback_on_failure = true
112+
}
113+
114+
output "findings" {
115+
value = hardbox_apply.web.findings
116+
}
117+
```
118+
119+
Full example: [`examples/aws/`](../terraform-provider/examples/aws/)
120+
121+
### GCP Compute Engine
122+
123+
```hcl
124+
resource "hardbox_apply" "web" {
125+
host = google_compute_instance.web.network_interface[0].network_ip
126+
user = "ubuntu"
127+
private_key = file("~/.ssh/id_rsa")
128+
129+
profile = "cloud-gcp"
130+
report_format = "json"
131+
}
132+
```
133+
134+
Full example: [`examples/gcp/`](../terraform-provider/examples/gcp/)
135+
136+
### Azure VM
137+
138+
```hcl
139+
resource "hardbox_apply" "vm" {
140+
host = azurerm_network_interface.nic.private_ip_address
141+
user = "azureuser"
142+
private_key = file("~/.ssh/id_rsa")
143+
144+
profile = "cloud-azure"
145+
report_format = "json"
146+
}
147+
```
148+
149+
Full example: [`examples/azure/`](../terraform-provider/examples/azure/)
150+
151+
### Audit-only (no changes)
152+
153+
```hcl
154+
resource "hardbox_apply" "audit" {
155+
host = var.host_ip
156+
user = "ubuntu"
157+
private_key = file(var.key_path)
158+
159+
profile = "cis-level2"
160+
dry_run = true # --dry-run: no changes applied
161+
}
162+
```
163+
164+
---
165+
166+
## Profiles
167+
168+
| Profile | Framework |
169+
|---|---|
170+
| `cis-level1` | CIS Benchmarks Level 1 |
171+
| `cis-level2` | CIS Benchmarks Level 2 |
172+
| `pci-dss` | PCI-DSS v4.0 |
173+
| `stig` | DISA STIG |
174+
| `hipaa` | HIPAA Security Rule |
175+
| `iso27001` | ISO/IEC 27001:2022 |
176+
| `cloud-aws` | CIS AWS Foundations v2.0 |
177+
| `cloud-gcp` | CIS GCP Foundations v2.0 |
178+
| `cloud-azure` | CIS Azure Foundations v2.1 |
179+
| `production` | hardbox curated |
180+
| `development` | hardbox curated |
181+
182+
---
183+
184+
## CI/CD Integration
185+
186+
### GitHub Actions
187+
188+
```yaml
189+
- name: Terraform apply (with hardbox hardening)
190+
run: terraform apply -auto-approve
191+
env:
192+
TF_VAR_private_key: ${{ secrets.SSH_PRIVATE_KEY }}
193+
```
194+
195+
### GitLab CI
196+
197+
```yaml
198+
terraform:
199+
script:
200+
- terraform init
201+
- terraform apply -auto-approve
202+
variables:
203+
TF_VAR_private_key: $SSH_PRIVATE_KEY
204+
```
205+
206+
---
207+
208+
## Building from source
209+
210+
```bash
211+
cd terraform-provider/
212+
go build -o terraform-provider-hardbox .
213+
214+
# Install locally for testing
215+
mkdir -p ~/.terraform.d/plugins/registry.terraform.io/jackby03/hardbox/0.3.0/linux_amd64
216+
cp terraform-provider-hardbox \
217+
~/.terraform.d/plugins/registry.terraform.io/jackby03/hardbox/0.3.0/linux_amd64/
218+
```
219+
220+
---
221+
222+
## Publishing to Terraform Registry
223+
224+
The provider is published to the [Terraform Registry](https://registry.terraform.io/providers/jackby03/hardbox)
225+
via GitHub Actions on release tag push. The registry indexes `terraform-provider/`
226+
as the provider source directory.
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
terraform {
2+
required_providers {
3+
aws = {
4+
source = "hashicorp/aws"
5+
version = "~> 5.0"
6+
}
7+
hardbox = {
8+
source = "jackby03/hardbox"
9+
version = "~> 0.3"
10+
}
11+
}
12+
}
13+
14+
provider "aws" {
15+
region = var.aws_region
16+
}
17+
18+
provider "hardbox" {
19+
hardbox_version = "latest"
20+
}
21+
22+
# ── EC2 instance ──────────────────────────────────────────────────────────────
23+
24+
resource "aws_instance" "web" {
25+
ami = var.ami_id
26+
instance_type = "t3.micro"
27+
key_name = var.key_name
28+
vpc_security_group_ids = [aws_security_group.web.id]
29+
subnet_id = var.subnet_id
30+
31+
tags = {
32+
Name = "hardbox-demo"
33+
}
34+
}
35+
36+
resource "aws_security_group" "web" {
37+
name = "hardbox-demo-sg"
38+
description = "hardbox demo — SSH restricted to known CIDRs"
39+
vpc_id = var.vpc_id
40+
41+
ingress {
42+
description = "SSH from bastion/VPN only"
43+
from_port = 22
44+
to_port = 22
45+
protocol = "tcp"
46+
cidr_blocks = var.ssh_allowed_cidrs
47+
}
48+
49+
egress {
50+
from_port = 0
51+
to_port = 0
52+
protocol = "-1"
53+
cidr_blocks = ["0.0.0.0/0"]
54+
}
55+
}
56+
57+
# ── hardbox hardening ─────────────────────────────────────────────────────────
58+
59+
resource "hardbox_apply" "web" {
60+
host = aws_instance.web.public_ip
61+
user = "ubuntu"
62+
private_key = file(var.private_key_path)
63+
# Obtain with: ssh-keyscan -t ed25519 <ip> | awk '{print $3}'
64+
host_key = var.host_public_key
65+
66+
profile = "cloud-aws"
67+
report_format = "json"
68+
69+
fail_on_critical = true
70+
fail_on_high = true
71+
rollback_on_failure = true
72+
73+
depends_on = [aws_instance.web]
74+
}
75+
76+
# ── Outputs ───────────────────────────────────────────────────────────────────
77+
78+
output "instance_id" {
79+
value = aws_instance.web.id
80+
}
81+
82+
output "hardbox_applied_at" {
83+
value = hardbox_apply.web.applied_at
84+
}
85+
86+
output "hardbox_findings" {
87+
value = hardbox_apply.web.findings
88+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
variable "aws_region" {
2+
description = "AWS region to deploy into."
3+
type = string
4+
default = "us-east-1"
5+
}
6+
7+
variable "ami_id" {
8+
description = "Ubuntu 22.04 LTS AMI ID for the target region."
9+
type = string
10+
}
11+
12+
variable "key_name" {
13+
description = "Name of the EC2 key pair for SSH access."
14+
type = string
15+
}
16+
17+
variable "vpc_id" {
18+
description = "VPC ID where the instance will be launched."
19+
type = string
20+
}
21+
22+
variable "subnet_id" {
23+
description = "Subnet ID for the EC2 instance."
24+
type = string
25+
}
26+
27+
variable "ssh_allowed_cidrs" {
28+
description = "CIDR blocks allowed to reach SSH (port 22)."
29+
type = list(string)
30+
default = ["10.0.0.0/8"]
31+
}
32+
33+
variable "private_key_path" {
34+
description = "Local path to the SSH private key file."
35+
type = string
36+
default = "~/.ssh/id_rsa"
37+
}
38+
39+
variable "host_public_key" {
40+
description = "Base64-encoded SSH public host key (from ssh-keyscan -t ed25519 <host> | awk '{print $3}')."
41+
type = string
42+
}

0 commit comments

Comments
 (0)