- Home
- Origin Server
- Verify
Verify
The Azure and AWS offerings have different runtimes and verification paths. Use SSH, Docker, nginx, and cloud-init checks only for the Azure full-origin. The AWS offering has no SSH-accessible host and must be verified through ECS, ELB, CloudWatch, S3, network reachability, and Terraform.
Azure Full-Origin Verification
Section titled “Azure Full-Origin Verification”Run these checks after allowing 5—10 minutes for cloud-init to complete.
Health Endpoint
Section titled “Health Endpoint”ORIGIN_IP=$(terraform output -raw public_ip)
curl -sf "http://${ORIGIN_IP}/health" | jq .Expected:
{ "status": "healthy", "component": "origin-server", "applications": ["juice-shop", "dvwa", "vampi", "httpbin", "whoami", "csd-demo", "dvga", "restaurant", "crapi"]}Application Endpoints
Section titled “Application Endpoints”# Default landing pagecurl -sf "http://${ORIGIN_IP}/" -o /dev/null -w "Landing: %{http_code}\n"
# Juice Shopcurl -sf "http://${ORIGIN_IP}/juice-shop/" -o /dev/null -w "Juice Shop: %{http_code}\n"
# DVWAcurl -sf "http://${ORIGIN_IP}/dvwa/" -o /dev/null -w "DVWA: %{http_code}\n"
# VAmPIcurl -sf "http://${ORIGIN_IP}/vampi/" -o /dev/null -w "VAmPI: %{http_code}\n"
# httpbincurl -sf "http://${ORIGIN_IP}/httpbin/get" -o /dev/null -w "httpbin: %{http_code}\n"
# whoamicurl -sf "http://${ORIGIN_IP}/whoami/" -o /dev/null -w "whoami: %{http_code}\n"
# DVGAcurl -sf "http://${ORIGIN_IP}/dvga/" -o /dev/null -w "DVGA: %{http_code}\n"
# RESTaurantcurl -sf "http://${ORIGIN_IP}/restaurant/docs" -o /dev/null -w "RESTaurant: %{http_code}\n"
# crAPI (port 8888)curl -sf "http://${ORIGIN_IP}:8888" -o /dev/null -w "crAPI: %{http_code}\n"All should return 200 (DVWA returns 302 redirect to login).
Automated Smoke Test
Section titled “Automated Smoke Test”The repository includes a 39-point smoke test suite:
./tests/smoke-test.sh ${ORIGIN_IP}This validates all 9 applications, health endpoints, VAmPI registration/login, CSD Demo exfil round-trip, DVGA GraphQL, RESTaurant Swagger, crAPI on port 8888, nginx gzip, and version hiding.
Docker Container Status
Section titled “Docker Container Status”SSH into the VM and verify all 41 containers are running:
ssh azureuser@${ORIGIN_IP} "sudo docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'" | sortExpected output (41 containers):
NAMES STATUS PORTScrapi-community Up X minutescrapi-identity Up X minutescrapi-mailhog Up X minutes 0.0.0.0:18025->8025/tcpcrapi-mongo Up X minutes 27017/tcpcrapi-postgres Up X minutes 5432/tcpcrapi-web Up X minutes 0.0.0.0:8888->80/tcpcrapi-workshop Up X minutescsd-demo-1 Up X minutes 127.0.0.1:5001->5001/tcpcsd-demo-2 Up X minutes 127.0.0.1:5002->5001/tcpcsd-demo-3 Up X minutes 127.0.0.1:5003->5001/tcpcsd-demo-4 Up X minutes 127.0.0.1:5004->5001/tcpdvga-1 Up X minutes 127.0.0.1:5201->5013/tcpdvga-2 Up X minutes 127.0.0.1:5202->5013/tcpdvga-3 Up X minutes 127.0.0.1:5203->5013/tcpdvga-4 Up X minutes 127.0.0.1:5204->5013/tcpdvwa-1 Up X minutes 127.0.0.1:8101->80/tcpdvwa-2 Up X minutes 127.0.0.1:8102->80/tcpdvwa-3 Up X minutes 127.0.0.1:8103->80/tcpdvwa-4 Up X minutes 127.0.0.1:8104->80/tcpdvwa-db Up X minutes 3306/tcphttpbin-1 Up X minutes 127.0.0.1:8201->80/tcphttpbin-2 Up X minutes 127.0.0.1:8202->80/tcphttpbin-3 Up X minutes 127.0.0.1:8203->80/tcphttpbin-4 Up X minutes 127.0.0.1:8204->80/tcpjuice-shop-1 Up X minutes 127.0.0.1:3001->3000/tcpjuice-shop-2 Up X minutes 127.0.0.1:3002->3000/tcpjuice-shop-3 Up X minutes 127.0.0.1:3003->3000/tcpjuice-shop-4 Up X minutes 127.0.0.1:3004->3000/tcprestaurant-1 Up X minutes 127.0.0.1:8301->8080/tcprestaurant-2 Up X minutes 127.0.0.1:8302->8080/tcprestaurant-3 Up X minutes 127.0.0.1:8303->8080/tcprestaurant-4 Up X minutes 127.0.0.1:8304->8080/tcprestaurant-db Up X minutes 5432/tcpvampi-1 Up X minutes 127.0.0.1:5101->5000/tcpvampi-2 Up X minutes 127.0.0.1:5102->5000/tcpvampi-3 Up X minutes 127.0.0.1:5103->5000/tcpvampi-4 Up X minutes 127.0.0.1:5104->5000/tcpwhoami-1 Up X minutes 127.0.0.1:8082->80/tcpwhoami-2 Up X minutes 127.0.0.1:8083->80/tcpwhoami-3 Up X minutes 127.0.0.1:8084->80/tcpwhoami-4 Up X minutes 127.0.0.1:8085->80/tcpnginx Status
Section titled “nginx Status”ssh azureuser@${ORIGIN_IP} "sudo nginx -t && sudo systemctl status nginx --no-pager"AWS Juice Shop Verification
Section titled “AWS Juice Shop Verification”Run these checks from the Terraform root that consumes terraform/modules/aws-juice-shop. Expose the module outputs from that root, then capture them:
output "origin_url" { value = module.juice_shop.origin_url}
output "ecs_cluster_id" { value = module.juice_shop.ecs_cluster_id}
output "ecs_service_id" { value = module.juice_shop.ecs_service_id}
output "target_group_arn" { value = module.juice_shop.target_group_arn}ORIGIN=$(terraform output -raw origin_url)ECS_CLUSTER=$(terraform output -raw ecs_cluster_id)ECS_SERVICE=$(terraform output -raw ecs_service_id)TARGET_GROUP_ARN=$(terraform output -raw target_group_arn)The AWS origin serves Juice Shop at ${ORIGIN}/; /juice-shop/, DVGA, RESTaurant, crAPI, and every other non-Juice-Shop endpoint in this documentation require the Azure full-origin.
Choose the HTTP execution location
Section titled “Choose the HTTP execution location”Before running any AWS curl command, choose an execution location that can reach the configured ALB:
- Internal ALB (default): run from a host or runner with private DNS resolution and an implemented route into the VPC, such as VPN, Direct Connect, peering, or an F5 Distributed Cloud connected network. If F5 Distributed Cloud is the caller, its private origin connectivity must already be implemented and verified.
- Internet-facing ALB: run from a source included in
allowed_ingress_cidrs. The ALB subnets must have routes to an Internet Gateway; selecting subnets in the correct VPC and Availability Zones does not make them public.
The module validates VPC and Availability Zone placement only. It does not create or validate subnet routes, gateways, VPNs, Direct Connect, peering, or F5 Distributed Cloud private connectivity.
ECS steady state and target health
Section titled “ECS steady state and target health”aws ecs wait services-stable --cluster "${ECS_CLUSTER}" --services "${ECS_SERVICE}"aws ecs describe-services --cluster "${ECS_CLUSTER}" --services "${ECS_SERVICE}" \ --query 'services[0].{desired:desiredCount,running:runningCount,pending:pendingCount,deployments:deployments[*].rolloutState}'aws elbv2 describe-target-health --target-group-arn "${TARGET_GROUP_ARN}" \ --query 'TargetHealthDescriptions[].TargetHealth.{state:State,reason:Reason,description:Description}'The service must reach steady state with the desired task count running and every registered target reporting healthy.
CloudWatch container logs
Section titled “CloudWatch container logs”The module log group is /ecs/<module-name>, where <module-name> is the module’s name input.
AWS_LOG_GROUP="/ecs/<module-name>"aws logs tail "${AWS_LOG_GROUP}" --since 10mConfirm recent juice-shop stream events exist and that there are no repeated startup failures. Do not use SSH or Docker commands: Fargate does not provide the Azure VM’s container-host access model.
ALB access logs
Section titled “ALB access logs”The caller supplies the S3 bucket and optional prefix. From the execution location selected above, generate one authorized request, then confirm that ELB log objects arrive under the regional AWSLogs/<account-id>/elasticloadbalancing/ hierarchy beneath that prefix:
curl -sf "${ORIGIN}/" -o /dev/null -w "Juice Shop: %{http_code}\n"aws s3api list-objects-v2 \ --bucket "<caller-managed-alb-log-bucket>" \ --prefix "<configured-prefix>/AWSLogs/" \ --query 'reverse(sort_by(Contents,&LastModified))[:5].[Key,LastModified,Size]'Delivery is asynchronous; retry after several minutes before treating an empty result as a failure.
Authorized-source reachability
Section titled “Authorized-source reachability”From the applicable execution location described above, test ${ORIGIN}/ from an address included in allowed_ingress_cidrs; expect HTTP 200. Test from a controlled address outside those CIDRs; the connection must fail.
Do not make the ALB public to work around missing routing. If public_exposure = true is an explicit requirement, restrict allowed_ingress_cidrs to approved sources and verify that each selected ALB subnet routes through an Internet Gateway.
The F5 Distributed Cloud-to-ALB HTTP hop is acceptable only inside the documented trusted routing domain; use a different origin design if interception protection cannot be established.
No-drift plan
Section titled “No-drift plan”terraform plan -detailed-exitcodeExit code 0 confirms no changes. Exit code 2 means drift or unapplied configuration and must be reviewed; exit code 1 is an error.
Azure Full-Origin Troubleshooting
Section titled “Azure Full-Origin Troubleshooting”Cloud-init still running
Section titled “Cloud-init still running”If endpoints return 502 or connection refused, cloud-init may still be provisioning:
ssh azureuser@${ORIGIN_IP} "cloud-init status"status: running— still provisioning, wait a few minutesstatus: done— provisioning complete, check Docker containersstatus: error— check logs below
Cloud-init logs
Section titled “Cloud-init logs”ssh azureuser@${ORIGIN_IP} "sudo tail -100 /var/log/cloud-init-output.log"Container not starting
Section titled “Container not starting”# Check container logs (each app has 4 instances: -1 through -4)ssh azureuser@${ORIGIN_IP} "sudo docker logs juice-shop-1 2>&1 | tail -20"ssh azureuser@${ORIGIN_IP} "sudo docker logs dvwa-1 2>&1 | tail -20"ssh azureuser@${ORIGIN_IP} "sudo docker logs vampi-1 2>&1 | tail -20"ssh azureuser@${ORIGIN_IP} "sudo docker logs httpbin-1 2>&1 | tail -20"ssh azureuser@${ORIGIN_IP} "sudo docker logs csd-demo-1 2>&1 | tail -20"ssh azureuser@${ORIGIN_IP} "sudo docker logs dvga-1 2>&1 | tail -20"ssh azureuser@${ORIGIN_IP} "sudo docker logs restaurant-1 2>&1 | tail -20"ssh azureuser@${ORIGIN_IP} "sudo docker logs crapi-web 2>&1 | tail -20"nginx returning 502
Section titled “nginx returning 502”The upstream container is not ready or has crashed:
# Check nginx error logssh azureuser@${ORIGIN_IP} "sudo tail -20 /var/log/nginx/error.log"
# Restart all instances of a specific appssh azureuser@${ORIGIN_IP} "for i in 1 2 3 4; do sudo docker restart juice-shop-\$i; done"
# Rebuild and restart all containersssh azureuser@${ORIGIN_IP} "cd /opt/origin-server && sudo docker compose build && sudo docker compose up -d"Disk space
Section titled “Disk space”Docker images consume approximately 4.7 GiB. Access logging is disabled to prevent disk exhaustion under CDN load testing. Logrotate is configured as defense-in-depth (500 MiB cap). Verify disk usage:
ssh azureuser@${ORIGIN_IP} "df -h / && sudo docker system df"Container memory
Section titled “Container memory”Check for containers approaching their memory limits (OOM risk):
ssh azureuser@${ORIGIN_IP} "sudo docker stats --no-stream --format 'table {{.Name}}\t{{.MemUsage}}\t{{.MemPerc}}' | sort"Restart everything
Section titled “Restart everything”If needed, rebuild and restart all services:
ssh azureuser@${ORIGIN_IP} "cd /opt/origin-server && sudo docker compose down && sudo docker compose build && sudo docker compose up -d && sudo systemctl restart nginx"AWS Troubleshooting
Section titled “AWS Troubleshooting”- ECS does not stabilize: inspect
aws ecs describe-servicesevents. Verify private task-subnet DNS and HTTPS egress to Docker Hub and AWS Logs through caller-managed NAT, proxy, or equivalent endpoints. - Targets remain unhealthy: inspect
describe-target-health, ECS events, and CloudWatch logs. Confirm the image listens oncontainer_port, the target-group health check reaches/, and the task security group allows that port only from the ALB security group. - CloudWatch logs are absent: verify task egress or private endpoints, the regional Logs service principal permissions in the caller-managed KMS key policy, and the execution role’s log-stream permissions.
- ALB logs are absent: verify the caller-managed bucket is in a supported region and its bucket policy permits regional Elastic Load Balancing log delivery to the configured prefix.
- Internal URL is unreachable: verify DNS, routes, and source authorization from the approved connected network. Keep the ALB internal unless public exposure is a deliberate, reviewed requirement.
- Terraform reports changes: review the plan against caller-managed VPC, routes, KMS, S3, provider authentication, and state. Do not apply until the ownership boundary and proposed changes are understood.