Skip to content

Add SSM Relay Stack: Private VPC + Mock Artifactory + Fine-Grained IAM#17

Open
devin-ai-integration[bot] wants to merge 3 commits into
mainfrom
devin/1775670482-ssm-relay-vpc-stack
Open

Add SSM Relay Stack: Private VPC + Mock Artifactory + Fine-Grained IAM#17
devin-ai-integration[bot] wants to merge 3 commits into
mainfrom
devin/1775670482-ssm-relay-vpc-stack

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a CloudFormation stack under cloudformation/ssm-relay/ that provisions a fully private VPC (no IGW, no NAT) with an EC2 relay box running a mock JFrog Artifactory (nginx on port 8081). Access is via AWS SSM port-forwarding only, using a dedicated IAM user with scoped permissions. Includes an architecture README with deploy/connect/teardown instructions.

Resources created: VPC, 2 private subnets, route table, S3 Gateway Endpoint, 3 SSM Interface Endpoints (ssm, ssmmessages, ec2messages), 2 security groups, IAM role + instance profile, EC2 instance, IAM user + policy + access key.

The stack was deployed to us-east-1 and connectivity was verified end-to-end: SSM tunnel established with the fine-grained IAM user → curl to localhost:8081 successfully reached the private nginx mock Artifactory.

Demo recording

View original video (rec-69634331047a4342aebd119c609a66fb-edited.mp4)

Changes since initial revision

  • Fixed UserData race condition: RelayInstance now has DependsOn on S3GatewayEndpoint and all three SSM interface endpoints, so dnf install and SSM agent registration will not race against endpoint creation.
  • Simplified heredoc content: Replaced multi-line indented JSON heredocs with single-line JSON and echo. This avoids the YAML block-scalar indentation ambiguity that caused malformed files during initial testing.
  • Tightened IAM policy: Split the ssm:StartSession permission into two statements — one targeting the instance ARN with a BoolIfExists: ssm:SessionDocumentAccessCheck: "true" condition, and a separate one targeting only the AWS-StartPortForwardingSession document. This enforces that both the instance AND the document must match.
  • Fixed sed regex: Uses sed -E with POSIX [[:space:]] character classes and handles both IPv4 and IPv6 listen directives.
  • Added nginx -t config validation before starting the service.
  • Updated README IAM policy snippet to reflect the new split-statement structure with SessionDocumentAccessCheck.

Review & Testing Checklist for Human

  • Deploy the stack from scratch — delete existing ssm-relay-demo, re-create, and verify nginx starts and serves on port 8081 without manual intervention. The current live deployment had nginx installed manually via SSM RunCommand after the initial UserData failure. A clean deploy is the only way to confirm the DependsOn + heredoc fixes actually work end-to-end. This is the highest-risk item.
  • Verify heredoc file content — the <<'EOF' blocks inside the YAML !Sub | block scalar will write files with leading whitespace (the indentation). Verify that nginx serves the JSON responses correctly and that curl ... | jq can parse them. If leading whitespace causes issues, the heredocs may need to be dedented or replaced with echo one-liners.
  • Verify BoolIfExists condition blocks interactive shell — confirm the fine-grained user can port-forward but cannot start a default SSM session (aws ssm start-session --target <id> without --document-name). Only positive-path (port-forwarding works) was tested; negative-path (shell denied) was not.
  • Secret key in CloudFormation outputsSsmPortForwardSecretAccessKey is emitted as a plaintext stack output. Anyone with cloudformation:DescribeStacks permission can read it. Consider using Secrets Manager or removing the output entirely.
  • sed pattern assumes trailing space — the regex listen[[:space:]]+80 (note trailing space) depends on Amazon Linux 2023's default nginx.conf formatting. Verify this matches on a fresh AL2023 instance; if the format is listen 80; without a space, the substitution would silently no-op and nginx would still bind port 80.

Recommended test plan: Delete the stack (aws cloudformation delete-stack), wait for deletion, then re-create from the template in this PR. After stack creation completes, use the output credentials to establish an SSM port-forward and run the three curl commands from the README. Also attempt a plain aws ssm start-session --target <id> (no document) to confirm it is denied.

Notes

  • The stack is currently deployed as ssm-relay-demo in us-east-1 (account 599083837640).
  • VPC endpoint costs: 3 interface endpoints incur ~$0.01/hr each per AZ ≈ $0.06/hr total. Teardown instructions are in the README.

Link to Devin session: https://partner-workshops.devinenterprise.com/sessions/4d18d5cc62634c0c83c0c0092e98acd3


Open with Devin

- CloudFormation template provisioning:
  - Private VPC (no IGW/NAT) with two subnets
  - VPC Endpoints for SSM, SSMMessages, EC2Messages, and S3
  - EC2 relay box with SSM agent + nginx mock Artifactory on port 8081
  - Security groups blocking all internet ingress
  - IAM role for EC2 (SSM managed instance)
  - IAM user with fine-grained port-forwarding-only permissions
  - IAM access key output for the scoped user

- Demonstrates secure data-plane connectivity to private resources
  via AWS SSM port-forwarding without any inbound network access
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

… regex

- Add DependsOn for S3/SSM VPC endpoints so EC2 boots after endpoints ready
- Fix heredoc indentation: use echo/single-line JSON to avoid leading spaces
- Restore ssm:SessionDocumentAccessCheck condition for true least-privilege
- Split IAM policy into separate instance + document statements
- Use sed -E with POSIX character classes for portable regex
- Add nginx -t config test before starting the service
- Handle both IPv4 and IPv6 listen directives in nginx.conf
Reflects the ssm:SessionDocumentAccessCheck condition and separate
AllowStartSessionOnInstance / AllowPortForwardDocumentOnly statements.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant