This repository provisions an AWS-based automation stack that runs a WireGuard VPN server in the Tokyo region and exposes a lightweight Web UI for controlled start/status operations, peer registration, and automated monitoring. Terraform creates every AWS resource, while Lambda functions implement the control logic that enforces weekday restrictions and low-traffic shutdowns.
- EC2 (default
t3a.small) launched from the configured AMI (vpn_ami_id, defaultami-0f52389a8648b923a) with a 30 GB encrypted gp3 root volume and no additional user-data overrides. - Dedicated networking stack (new VPC, single public subnet, internet gateway, and route table) plus an ENI with an Elastic IP to keep the VPN’s public address stable across reboots.
- Managed SSH key pair generated via Terraform and exposed as sensitive output for immediate download; the EC2 instance uses this key for admin access.
- Lambda functions (Python 3.12) handling start, stop, status, peer-registration, and network-monitor workflows.
- API Gateway (REST) exposing
/start,/status,/register, and (optionally)/stopendpoints locked behind an API key. - EventBridge Scheduler jobs for weekday midnight shutdown and 15-minute traffic monitoring, both timezone-aware for Australia/Sydney.
- S3 + CloudFront delivering a static HTML/CSS Web UI that interacts with the API.
- Admin console served at
admin.htmlfor trusted operators who need to bypass the maintenance window and access the full control surface. - SNS notifications fired whenever a manual start request is issued, with optional email subscriptions.
- IAM kept minimal: Lambda receives only the permissions required for EC2 control, monitoring, and Systems Manager commands, while the VPN instance is attached to an
AmazonSSMManagedInstanceCoreprofile for remote peer management.
[S3 + CloudFront] --> Web UI
|
v
[API Gateway] ---> [Lambda]
| \ | | | |
| > start_instance / stop_instance / check_status / register_peer / monitor_traffic
v
[Amazon EventBridge Scheduler] (scheduled stop + traffic checks)
|
v
[EC2 WireGuard VPN]
- Terraform ≥ 1.5.x
- AWS CLI configured with credentials that can create IAM, EC2, Lambda, API Gateway, CloudFront, and S3 resources in
ap-northeast-1. - Optional:
direnvor shell tooling to export Terraform variables.
terraform/ # Terraform configuration for the entire stack
lambda_functions/ # Python source for Lambda handlers
web/ # Static assets for the Web UI served via CloudFront
-
Review security defaults
- Update
terraform/network.tfto restrict SSH and WireGuard ingress to trusted IP ranges. - Adjust
timezone_name,vpn_instance_type,public_subnet_cidr, orvpn_private_ipvariables if you need a different layout. Ensure the instance type matches the AMI architecture. - Ensure
wireguard_client_subnetandwireguard_server_addressreflect the addressing used in/etc/wireguard/wg0.conf; the peer registration Lambda relies on these values when allocating client IPs. - Confirm the selected AMI includes the AWS Systems Manager Agent (SSM Agent). Amazon Linux 2/2023 images ship with it enabled by default.
- Set
admin_basic_auth_usernameandadmin_basic_auth_passwordto non-default values before deploying to production; these control the CloudFront basic-auth gate foradmin.html. - Optionally populate
start_notification_emailswith the addresses that should receive SNS alerts when the VPN is started.
- Update
-
Initialize Terraform
cd terraform terraform init -
Inspect the plan
terraform plan \ -var "project_name=vpn-auto-control" \ -var "web_bucket_name=<optional-custom-bucket>"
-
Apply the configuration
terraform apply
Confirm the run and wait until all resources finish provisioning. The outputs include the API invoke URL, the CloudFront domain, and the EC2 instance ID. If you supplied
start_notification_emails, check your email for the SNS subscription confirmation message and confirm it to begin receiving alerts. -
Retrieve the API key value Terraform outputs the API key ID, not the actual secret value. To fetch it:
aws apigateway get-api-key \ --api-key <api_key_id_from_outputs> \ --include-value
-
Download the SSH private key
terraform output --raw ssh_private_key_pem > ../vpn-server-tokyo-key.pem chmod 600 ../vpn-server-tokyo-key.pem -
Verify WireGuard
- SSH into the EC2 instance using the Elastic IP output and complete any WireGuard peer configuration.
- Confirm the existing WireGuard setup from your AMI works as expected.
- Browse to the CloudFront domain from the Terraform output.
- If it is a weekday between 00:00 and 13:00 Australia/Sydney, you will be shown
unavailable.html, which explains the downtime and automatically refreshes once access returns. - Outside the restricted window (including all weekend hours),
index.htmldisplays Start/Status buttons with a highlighted status panel. On the first click the page prompts for:- API base URL (for example,
https://<api-id>.execute-api.ap-northeast-1.amazonaws.com/prod). - API key copied from API Gateway.
- API base URL (for example,
- After the prompts, the request runs and the response appears in the result panel (e.g., “Server already running.” or “VPN warming up…”). Details are saved to
localStorage; use Reset saved API details to reconfigure. - Administrators can browse to
<cloudfront-domain>/admin.htmlto bypass the maintenance window, access the manual stop action, and register peers even during restricted hours. The admin page includes the WireGuard registration form that returns the allocated/32IP and generated preshared key—copy them into the client configuration. - You will be prompted for the HTTP Basic credentials configured via
admin_basic_auth_username/admin_basic_auth_password; share them (and the admin API key) only with trusted operators. Successful start requests emit an SNS notification (see Terraform outputs for the topic ARN).
Install the development dependencies and execute the pytest suite from the repository root:
pip install -r requirements-test.txt
pytestThe tests rely on moto to mock AWS services and currently cover the VPN start handler, ensuring time-window enforcement and the SNS notification path.
- start_instance: Enforces the Australia/Sydney weekday restriction (no manual starts between 00:00–13:00 local time). Returns the resulting instance state.
- stop_instance: Stops the instance when invoked via API or the scheduled weekday midnight rule. Idempotent when the instance is already stopping or stopped.
- check_status: Reports instance state, IPs, and the latest EC2 system/instance status checks, marking the VPN as available only after both checks pass, with timestamps in Australia/Sydney time.
- register_peer: Accepts a WireGuard public key, allocates the next free
/32in the configured client CIDR, updates/etc/wireguard/wg0.confthrough AWS Systems Manager, and returns the assigned IP plus a generated preshared key. - monitor_traffic: Runs every 15 minutes via EventBridge Scheduler, evaluates combined
NetworkIn/NetworkOutfor the trailing 1.5 hours, and stops the instance if sustained traffic ≤ 1 MB/hour.
To remove all resources, run:
cd terraform
terraform destroyNote that the S3 bucket is emptying automatically (force_destroy = true), but you should still confirm the bucket is no longer needed.
- Integrate Amazon Cognito or IAM authorizers for stronger authentication instead of API keys.
- Parameterize allowed ingress CIDR blocks and WireGuard settings.
- Add notifications (e.g., Amazon SNS) when automated stops occur.
- Introduce automated tests for the Lambda handlers (e.g., using
pytestwith moto).