Skip to content

Commit 97cbf4f

Browse files
authored
Merge pull request #116 from jackby03/feat/111-cloud-init-templates
2 parents a52f45d + fed3355 commit 97cbf4f

File tree

5 files changed

+694
-1
lines changed

5 files changed

+694
-1
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
102102
- `cloud-aws` compliance profile — CIS AWS Foundations Benchmark v2.0, OS hardening for Amazon EC2 with AWS-specific annotations (IAM, Security Groups, CloudTrail, EBS encryption, KMS); includes guidance for AWS Security Hub and AWS Config validation ([#106](https://github.com/jackby03/hardbox/issues/106))
103103
- `cloud-gcp` compliance profile — CIS GCP Foundations Benchmark v2.0, OS hardening for GCP Compute Engine with GCP-specific annotations (OS Login, IAP, Cloud Audit Logs, CMEK, Shielded VM); includes guidance for Security Command Center validation ([#107](https://github.com/jackby03/hardbox/issues/107))
104104
- `cloud-azure` compliance profile — CIS Azure Foundations Benchmark v2.1, OS hardening for Azure VMs with Azure-specific annotations (NSG, Defender for Cloud, Azure Policy, Disk Encryption, AAD login); includes guidance for Defender for Cloud Secure Score validation ([#108](https://github.com/jackby03/hardbox/issues/108))
105+
- cloud-init user-data templates for AWS EC2, GCP Compute Engine, and Azure VMs — each template installs hardbox with checksum verification, applies the provider-matched compliance profile on first boot, uploads the HTML audit report to cloud-native object storage (S3 / GCS / Blob), and configures a daily systemd re-hardening timer ([#111](https://github.com/jackby03/hardbox/issues/111))
106+
- `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
105107

106108
### Changed
107109
- `install.sh` now resolves release assets via GitHub API instead of hardcoded filenames, improving compatibility across release archive naming formats.
@@ -116,7 +118,7 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
116118
### Planned for v0.3
117119
- `nist-800-53` profile
118120
- Ansible role integration
119-
- Terraform provisioner
121+
- Terraform provisioner plugin
120122
- cloud-init support
121123

122124
---

cloud-init/aws-user-data.yaml

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#cloud-config
2+
# hardbox cloud-init user-data template — Amazon Web Services (EC2)
3+
#
4+
# Usage:
5+
# Pass this file as user-data when launching an EC2 instance:
6+
# aws ec2 run-instances \
7+
# --user-data file://cloud-init/aws-user-data.yaml \
8+
# ...
9+
#
10+
# Variables (customise before use):
11+
# HARDBOX_VERSION : hardbox release tag (default: latest)
12+
# HARDBOX_PROFILE : compliance profile to apply (default: cloud-aws)
13+
# HARDBOX_REPORT_S3 : S3 bucket for audit reports (e.g. s3://my-bucket/hardbox/)
14+
# HARDBOX_SCHEDULE : systemd timer interval for re-hardening (default: daily)
15+
#
16+
# Requirements:
17+
# - EC2 instance role must have s3:PutObject permission on HARDBOX_REPORT_S3
18+
# - Instance must have internet access (or VPC endpoint) to download hardbox
19+
20+
write_files:
21+
- path: /etc/hardbox/cloud-init.env
22+
permissions: "0640"
23+
owner: root:root
24+
content: |
25+
HARDBOX_VERSION=latest
26+
HARDBOX_PROFILE=cloud-aws
27+
HARDBOX_REPORT_S3=s3://CHANGE-ME/hardbox/reports/
28+
HARDBOX_SCHEDULE=daily
29+
HARDBOX_INSTALL_DIR=/usr/local/bin
30+
HARDBOX_REPORT_DIR=/var/lib/hardbox/reports
31+
CLOUD_PROVIDER=aws
32+
33+
- path: /usr/local/lib/hardbox/install.sh
34+
permissions: "0750"
35+
owner: root:root
36+
content: |
37+
#!/bin/bash
38+
# hardbox installer with checksum verification
39+
set -euo pipefail
40+
source /etc/hardbox/cloud-init.env
41+
42+
ARCH=$(uname -m)
43+
case "$ARCH" in
44+
x86_64) GOARCH="amd64" ;;
45+
aarch64) GOARCH="arm64" ;;
46+
*) echo "Unsupported architecture: $ARCH"; exit 1 ;;
47+
esac
48+
49+
if [ "$HARDBOX_VERSION" = "latest" ]; then
50+
HARDBOX_VERSION=$(curl -fsSL \
51+
"https://api.github.com/repos/jackby03/hardbox/releases/latest" \
52+
| grep '"tag_name"' | cut -d'"' -f4)
53+
fi
54+
55+
BASE_URL="https://github.com/jackby03/hardbox/releases/download/${HARDBOX_VERSION}"
56+
BINARY="hardbox_Linux_${GOARCH}"
57+
CHECKSUM_FILE="hardbox_${HARDBOX_VERSION#v}_checksums.txt"
58+
59+
echo "Installing hardbox ${HARDBOX_VERSION} (${GOARCH})..."
60+
curl -fsSL "${BASE_URL}/${BINARY}" -o /tmp/hardbox
61+
curl -fsSL "${BASE_URL}/${CHECKSUM_FILE}" -o /tmp/hardbox_checksums.txt
62+
63+
# Verify checksum
64+
EXPECTED=$(grep "${BINARY}" /tmp/hardbox_checksums.txt | awk '{print $1}')
65+
ACTUAL=$(sha256sum /tmp/hardbox | awk '{print $1}')
66+
if [ "$EXPECTED" != "$ACTUAL" ]; then
67+
echo "Checksum mismatch! Expected: $EXPECTED, Got: $ACTUAL" >&2
68+
exit 1
69+
fi
70+
71+
install -m 0755 /tmp/hardbox "${HARDBOX_INSTALL_DIR}/hardbox"
72+
rm -f /tmp/hardbox /tmp/hardbox_checksums.txt
73+
echo "hardbox installed: $(hardbox version)"
74+
75+
- path: /usr/local/lib/hardbox/harden.sh
76+
permissions: "0750"
77+
owner: root:root
78+
content: |
79+
#!/bin/bash
80+
# hardbox apply + report upload to S3
81+
set -euo pipefail
82+
source /etc/hardbox/cloud-init.env
83+
84+
mkdir -p "${HARDBOX_REPORT_DIR}"
85+
TIMESTAMP=$(date -u +%Y%m%dT%H%M%SZ)
86+
INSTANCE_ID=$(curl -fsSL http://169.254.169.254/latest/meta-data/instance-id 2>/dev/null || echo "unknown")
87+
REPORT_FILE="${HARDBOX_REPORT_DIR}/hardbox-${INSTANCE_ID}-${TIMESTAMP}.html"
88+
89+
echo "Applying hardbox profile: ${HARDBOX_PROFILE}"
90+
hardbox apply \
91+
--profile "${HARDBOX_PROFILE}" \
92+
--format html \
93+
--output "${REPORT_FILE}" \
94+
--non-interactive
95+
96+
# Upload report to S3 if bucket is configured
97+
if [ "${HARDBOX_REPORT_S3}" != "s3://CHANGE-ME/hardbox/reports/" ]; then
98+
echo "Uploading report to ${HARDBOX_REPORT_S3}"
99+
aws s3 cp "${REPORT_FILE}" \
100+
"${HARDBOX_REPORT_S3}${INSTANCE_ID}/${TIMESTAMP}.html" \
101+
--sse aws:kms \
102+
--no-progress
103+
echo "Report uploaded successfully"
104+
fi
105+
106+
- path: /etc/systemd/system/hardbox-harden.service
107+
permissions: "0644"
108+
owner: root:root
109+
content: |
110+
[Unit]
111+
Description=hardbox system hardening
112+
After=network-online.target
113+
Wants=network-online.target
114+
115+
[Service]
116+
Type=oneshot
117+
ExecStart=/usr/local/lib/hardbox/harden.sh
118+
StandardOutput=journal
119+
StandardError=journal
120+
SyslogIdentifier=hardbox
121+
122+
- path: /etc/systemd/system/hardbox-harden.timer
123+
permissions: "0644"
124+
owner: root:root
125+
content: |
126+
[Unit]
127+
Description=hardbox periodic re-hardening timer
128+
Requires=hardbox-harden.service
129+
130+
[Timer]
131+
OnBootSec=2min
132+
OnCalendar=daily
133+
Persistent=true
134+
135+
[Install]
136+
WantedBy=timers.target
137+
138+
runcmd:
139+
# Install hardbox binary with checksum verification
140+
- /usr/local/lib/hardbox/install.sh
141+
142+
# Enable and start systemd timer for periodic re-hardening
143+
- systemctl daemon-reload
144+
- systemctl enable hardbox-harden.timer
145+
- systemctl start hardbox-harden.timer
146+
147+
# Run initial hardening immediately
148+
- systemctl start hardbox-harden.service
149+
150+
# Log completion to CloudWatch (if aws cli available)
151+
- |
152+
INSTANCE_ID=$(curl -fsSL http://169.254.169.254/latest/meta-data/instance-id 2>/dev/null || echo "unknown")
153+
logger -t hardbox "cloud-init hardening complete — instance: ${INSTANCE_ID}"

cloud-init/azure-user-data.yaml

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
#cloud-config
2+
# hardbox cloud-init user-data template — Microsoft Azure (Virtual Machines)
3+
#
4+
# Usage:
5+
# Pass this file as custom-data when creating an Azure VM:
6+
# az vm create \
7+
# --name my-vm \
8+
# --custom-data cloud-init/azure-user-data.yaml \
9+
# ...
10+
#
11+
# Note: Azure passes custom-data as base64; the az CLI handles encoding automatically.
12+
# For ARM templates, base64-encode the file content in the customData property.
13+
#
14+
# Variables (customise before use):
15+
# HARDBOX_VERSION : hardbox release tag (default: latest)
16+
# HARDBOX_PROFILE : compliance profile to apply (default: cloud-azure)
17+
# HARDBOX_REPORT_CONTAINER : Azure Blob Storage container URI for reports
18+
# (e.g. https://myaccount.blob.core.windows.net/hardbox/)
19+
# HARDBOX_SCHEDULE : systemd timer interval for re-hardening (default: daily)
20+
#
21+
# Requirements:
22+
# - VM managed identity must have Storage Blob Data Contributor role on the container
23+
# - Instance must have internet access or Private Endpoint to download hardbox
24+
# - Azure Monitor Agent recommended for forwarding hardbox logs to Log Analytics
25+
26+
write_files:
27+
- path: /etc/hardbox/cloud-init.env
28+
permissions: "0640"
29+
owner: root:root
30+
content: |
31+
HARDBOX_VERSION=latest
32+
HARDBOX_PROFILE=cloud-azure
33+
HARDBOX_REPORT_CONTAINER=https://CHANGE-ME.blob.core.windows.net/hardbox/
34+
HARDBOX_SCHEDULE=daily
35+
HARDBOX_INSTALL_DIR=/usr/local/bin
36+
HARDBOX_REPORT_DIR=/var/lib/hardbox/reports
37+
CLOUD_PROVIDER=azure
38+
39+
- path: /usr/local/lib/hardbox/install.sh
40+
permissions: "0750"
41+
owner: root:root
42+
content: |
43+
#!/bin/bash
44+
# hardbox installer with checksum verification
45+
set -euo pipefail
46+
source /etc/hardbox/cloud-init.env
47+
48+
ARCH=$(uname -m)
49+
case "$ARCH" in
50+
x86_64) GOARCH="amd64" ;;
51+
aarch64) GOARCH="arm64" ;;
52+
*) echo "Unsupported architecture: $ARCH"; exit 1 ;;
53+
esac
54+
55+
if [ "$HARDBOX_VERSION" = "latest" ]; then
56+
HARDBOX_VERSION=$(curl -fsSL \
57+
"https://api.github.com/repos/jackby03/hardbox/releases/latest" \
58+
| grep '"tag_name"' | cut -d'"' -f4)
59+
fi
60+
61+
BASE_URL="https://github.com/jackby03/hardbox/releases/download/${HARDBOX_VERSION}"
62+
BINARY="hardbox_Linux_${GOARCH}"
63+
CHECKSUM_FILE="hardbox_${HARDBOX_VERSION#v}_checksums.txt"
64+
65+
echo "Installing hardbox ${HARDBOX_VERSION} (${GOARCH})..."
66+
curl -fsSL "${BASE_URL}/${BINARY}" -o /tmp/hardbox
67+
curl -fsSL "${BASE_URL}/${CHECKSUM_FILE}" -o /tmp/hardbox_checksums.txt
68+
69+
# Verify checksum
70+
EXPECTED=$(grep "${BINARY}" /tmp/hardbox_checksums.txt | awk '{print $1}')
71+
ACTUAL=$(sha256sum /tmp/hardbox | awk '{print $1}')
72+
if [ "$EXPECTED" != "$ACTUAL" ]; then
73+
echo "Checksum mismatch! Expected: $EXPECTED, Got: $ACTUAL" >&2
74+
exit 1
75+
fi
76+
77+
install -m 0755 /tmp/hardbox "${HARDBOX_INSTALL_DIR}/hardbox"
78+
rm -f /tmp/hardbox /tmp/hardbox_checksums.txt
79+
echo "hardbox installed: $(hardbox version)"
80+
81+
- path: /usr/local/lib/hardbox/harden.sh
82+
permissions: "0750"
83+
owner: root:root
84+
content: |
85+
#!/bin/bash
86+
# hardbox apply + report upload to Azure Blob Storage
87+
set -euo pipefail
88+
source /etc/hardbox/cloud-init.env
89+
90+
mkdir -p "${HARDBOX_REPORT_DIR}"
91+
TIMESTAMP=$(date -u +%Y%m%dT%H%M%SZ)
92+
93+
# Retrieve VM metadata from Azure IMDS
94+
IMDS_BASE="http://169.254.169.254/metadata/instance"
95+
VM_NAME=$(curl -fsSL "${IMDS_BASE}/compute/name?api-version=2021-02-01&format=text" \
96+
-H "Metadata:true" 2>/dev/null || echo "unknown")
97+
RESOURCE_GROUP=$(curl -fsSL \
98+
"${IMDS_BASE}/compute/resourceGroupName?api-version=2021-02-01&format=text" \
99+
-H "Metadata:true" 2>/dev/null || echo "unknown")
100+
REPORT_FILE="${HARDBOX_REPORT_DIR}/hardbox-${VM_NAME}-${TIMESTAMP}.html"
101+
102+
echo "Applying hardbox profile: ${HARDBOX_PROFILE}"
103+
hardbox apply \
104+
--profile "${HARDBOX_PROFILE}" \
105+
--format html \
106+
--output "${REPORT_FILE}" \
107+
--non-interactive
108+
109+
# Upload report to Azure Blob Storage via managed identity (az cli)
110+
if [ "${HARDBOX_REPORT_CONTAINER}" != "https://CHANGE-ME.blob.core.windows.net/hardbox/" ]; then
111+
if command -v az &>/dev/null; then
112+
echo "Uploading report to ${HARDBOX_REPORT_CONTAINER}"
113+
az storage blob upload \
114+
--blob-url "${HARDBOX_REPORT_CONTAINER}${RESOURCE_GROUP}/${VM_NAME}/${TIMESTAMP}.html" \
115+
--file "${REPORT_FILE}" \
116+
--auth-mode login \
117+
--overwrite \
118+
--no-progress
119+
echo "Report uploaded successfully"
120+
else
121+
echo "Warning: az CLI not available; skipping blob upload" >&2
122+
fi
123+
fi
124+
125+
logger -t hardbox \
126+
"hardbox apply complete — profile=${HARDBOX_PROFILE} vm=${VM_NAME} rg=${RESOURCE_GROUP}"
127+
128+
- path: /etc/systemd/system/hardbox-harden.service
129+
permissions: "0644"
130+
owner: root:root
131+
content: |
132+
[Unit]
133+
Description=hardbox system hardening
134+
After=network-online.target walinuxagent.service
135+
Wants=network-online.target
136+
137+
[Service]
138+
Type=oneshot
139+
ExecStart=/usr/local/lib/hardbox/harden.sh
140+
StandardOutput=journal
141+
StandardError=journal
142+
SyslogIdentifier=hardbox
143+
144+
- path: /etc/systemd/system/hardbox-harden.timer
145+
permissions: "0644"
146+
owner: root:root
147+
content: |
148+
[Unit]
149+
Description=hardbox periodic re-hardening timer
150+
Requires=hardbox-harden.service
151+
152+
[Timer]
153+
OnBootSec=2min
154+
OnCalendar=daily
155+
Persistent=true
156+
157+
[Install]
158+
WantedBy=timers.target
159+
160+
runcmd:
161+
# Install hardbox binary with checksum verification
162+
- /usr/local/lib/hardbox/install.sh
163+
164+
# Enable and start systemd timer for periodic re-hardening
165+
- systemctl daemon-reload
166+
- systemctl enable hardbox-harden.timer
167+
- systemctl start hardbox-harden.timer
168+
169+
# Run initial hardening immediately
170+
- systemctl start hardbox-harden.service

0 commit comments

Comments
 (0)