--- name: configuring-aws-verified-access-for-ztna description: Configure AWS Verified Access to provide VPN-less zero trust network access to internal apps, combining identity trust providers (IAM Identity Center, Okta/OIDC), device posture providers (CrowdStrike, Jamf), Cedar policy authoring, and Terraform deployment. Use when replacing VPN access with ZTNA, writing Cedar access policies, or deploying Verified Access instances, groups, and endpoints across AWS accounts. domain: cybersecurity subdomain: zero-trust-architecture tags: - zero-trust - aws - verified-access - ztna - cedar-policy - vpn-less - identity-verification - device-posture - aws-ram version: '1.0' author: mahipal license: Apache-2.0 nist_csf: - PR.AA-01 - PR.AA-05 - PR.IR-01 - GV.PO-01 mitre_attack: - T1078.004 - T1133 - T1021.007 --- # Configuring AWS Verified Access for ZTNA ## Overview AWS Verified Access is a Zero Trust Network Access (ZTNA) service that provides secure, VPN-less access to corporate applications hosted in AWS. It evaluates each access request in real-time against granular conditional access policies written in the Cedar policy language, ensuring access is granted per-application only when specific security requirements such as user identity and device security posture are met and maintained. Verified Access integrates with AWS IAM Identity Center, third-party identity providers (Okta, CrowdStrike, JumpCloud, Jamf), and device management solutions. For multi-account deployments, AWS Resource Access Manager (RAM) enables sharing Verified Access groups across organizational units. ## When to Use - When deploying or configuring configuring aws verified access for ztna capabilities in your environment - When establishing security controls aligned to compliance requirements - When building or improving security architecture for this domain - When conducting security assessments that require this implementation ## Prerequisites - AWS account with appropriate IAM permissions - Identity provider (AWS IAM Identity Center, Okta, or OIDC-compatible) - Device trust provider (CrowdStrike, Jamf, JumpCloud, or AWS Verified Access native) - Internal Application Load Balancer (ALB) or network interface endpoint - Understanding of Cedar policy language - VPC with application workloads to protect ## Architecture ``` End User (Browser) | | HTTPS v +------+--------+ | Verified | | Access | | Endpoint | | (Public DNS) | +------+--------+ | +------+--------+ | Verified | <-- Cedar Access Policies | Access | <-- Identity Provider Signals | Instance | <-- Device Trust Signals | (Policy | | Evaluation) | +------+--------+ | +------+--------+ | Verified | | Access Group | | (App Group) | +------+--------+ | +------+--------+ | Internal ALB | | or ENI Target | +------+--------+ | +------+--------+ | Application | | (Private VPC) | +--------------+ ``` ## Core Components ### Verified Access Instance The regional entity that evaluates access requests against policies. ```bash # Create Verified Access Instance via AWS CLI aws ec2 create-verified-access-instance \ --description "Production Zero Trust Instance" \ --tag-specifications 'ResourceType=verified-access-instance,Tags=[{Key=Environment,Value=production}]' ``` ### Trust Providers #### Identity Trust Provider (AWS IAM Identity Center) ```bash # Create identity trust provider aws ec2 create-verified-access-trust-provider \ --trust-provider-type user \ --user-trust-provider-type iam-identity-center \ --policy-reference-name "idc" \ --description "IAM Identity Center trust provider" \ --tag-specifications 'ResourceType=verified-access-trust-provider,Tags=[{Key=Type,Value=identity}]' ``` #### Identity Trust Provider (OIDC - Okta) ```bash aws ec2 create-verified-access-trust-provider \ --trust-provider-type user \ --user-trust-provider-type oidc \ --oidc-options '{ "Issuer": "https://company.okta.com/oauth2/default", "AuthorizationEndpoint": "https://company.okta.com/oauth2/default/v1/authorize", "TokenEndpoint": "https://company.okta.com/oauth2/default/v1/token", "UserInfoEndpoint": "https://company.okta.com/oauth2/default/v1/userinfo", "ClientId": "0oa1234567890", "ClientSecret": "client-secret-here", "Scope": "openid profile groups" }' \ --policy-reference-name "okta" \ --description "Okta OIDC trust provider" ``` #### Device Trust Provider (CrowdStrike) ```bash aws ec2 create-verified-access-trust-provider \ --trust-provider-type device \ --device-trust-provider-type crowdstrike \ --device-options '{ "TenantId": "crowdstrike-tenant-id", "PublicSigningKeyUrl": "https://api.crowdstrike.com/zero-trust/v2/certificates" }' \ --policy-reference-name "crowdstrike" \ --description "CrowdStrike device trust provider" ``` ### Attach Trust Providers to Instance ```bash # Attach identity provider aws ec2 attach-verified-access-trust-provider \ --verified-access-instance-id vai-0123456789abcdef \ --verified-access-trust-provider-id vatp-0123456789abcdef # Attach device provider aws ec2 attach-verified-access-trust-provider \ --verified-access-instance-id vai-0123456789abcdef \ --verified-access-trust-provider-id vatp-device123456 ``` ### Verified Access Groups ```bash # Create a group for web applications aws ec2 create-verified-access-group \ --verified-access-instance-id vai-0123456789abcdef \ --description "Production Web Applications" \ --policy-document 'permit(principal, action, resource) when { context.okta.groups.contains("production-access") && context.crowdstrike.assessment.overall > 50 };' \ --tag-specifications 'ResourceType=verified-access-group,Tags=[{Key=Tier,Value=web}]' ``` ### Verified Access Endpoints ```bash # Create endpoint for ALB-backed application aws ec2 create-verified-access-endpoint \ --verified-access-group-id vag-0123456789abcdef \ --endpoint-type load-balancer \ --attachment-type vpc \ --domain-certificate-arn arn:aws:acm:us-east-1:123456789012:certificate/xxxx \ --application-domain app.internal.company.com \ --endpoint-domain-prefix myapp \ --load-balancer-options '{ "LoadBalancerArn": "arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/internal-alb/xxxx", "Port": 443, "Protocol": "https", "SubnetIds": ["subnet-abc123", "subnet-def456"] }' \ --security-group-ids sg-0123456789abcdef \ --description "Internal HR Application" ``` ## Cedar Policy Language ### Policy Basics ```cedar // Allow access for users in the engineering group with compliant devices permit(principal, action, resource) when { context.okta.groups.contains("engineering") && context.crowdstrike.assessment.overall > 70 && context.crowdstrike.assessment.sensor_config.status == "active" }; // Deny access from unmanaged devices forbid(principal, action, resource) when { !context.crowdstrike.assessment.sensor_config.status == "active" }; ``` ### Advanced Policy Examples ```cedar // Time-based access - only during business hours (UTC) permit(principal, action, resource) when { context.okta.groups.contains("contractors") && context.http_request.http_method == "GET" && context.crowdstrike.assessment.overall > 80 }; // Restrict admin access to specific user group with high device trust permit(principal, action, resource) when { context.idc.groups.contains("admins") && context.crowdstrike.assessment.overall > 90 && context.crowdstrike.assessment.os_version.startswith("Windows 11") || context.crowdstrike.assessment.os_version.startswith("macOS 14") }; // Allow read-only access for lower trust levels permit(principal, action, resource) when { context.okta.groups.contains("read-only") && context.crowdstrike.assessment.overall > 30 && context.http_request.http_method == "GET" }; ``` ### Group-Level vs Endpoint-Level Policies ```cedar // Group-level policy (applies to all endpoints in the group) // Set on the Verified Access Group permit(principal, action, resource) when { context.okta.groups.contains("employees") && context.crowdstrike.assessment.overall > 50 }; // Endpoint-level policy (additional restrictions for specific app) // Set on the Verified Access Endpoint permit(principal, action, resource) when { context.okta.groups.contains("hr-team") && context.okta.email.endsWith("@company.com") }; ``` ## Terraform Configuration ```hcl terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } } # Verified Access Instance resource "aws_verifiedaccess_instance" "main" { description = "Production Zero Trust Access" tags = { Environment = "production" } } # Identity Trust Provider (OIDC) resource "aws_verifiedaccess_trust_provider" "okta" { policy_reference_name = "okta" trust_provider_type = "user" user_trust_provider_type = "oidc" description = "Okta identity provider" oidc_options { authorization_endpoint = "https://company.okta.com/oauth2/default/v1/authorize" client_id = var.okta_client_id client_secret = var.okta_client_secret issuer = "https://company.okta.com/oauth2/default" scope = "openid profile groups" token_endpoint = "https://company.okta.com/oauth2/default/v1/token" user_info_endpoint = "https://company.okta.com/oauth2/default/v1/userinfo" } } # Device Trust Provider (CrowdStrike) resource "aws_verifiedaccess_trust_provider" "crowdstrike" { policy_reference_name = "crowdstrike" trust_provider_type = "device" device_trust_provider_type = "crowdstrike" description = "CrowdStrike device trust" device_options { tenant_id = var.crowdstrike_tenant_id } } # Attach providers to instance resource "aws_verifiedaccess_instance_trust_provider_attachment" "okta" { verifiedaccess_instance_id = aws_verifiedaccess_instance.main.id verifiedaccess_trust_provider_id = aws_verifiedaccess_trust_provider.okta.id } resource "aws_verifiedaccess_instance_trust_provider_attachment" "crowdstrike" { verifiedaccess_instance_id = aws_verifiedaccess_instance.main.id verifiedaccess_trust_provider_id = aws_verifiedaccess_trust_provider.crowdstrike.id } # Verified Access Group resource "aws_verifiedaccess_group" "web_apps" { verifiedaccess_instance_id = aws_verifiedaccess_instance.main.id description = "Production Web Applications" policy_document = <<-CEDAR permit(principal, action, resource) when { context.okta.groups.contains("production-access") && context.crowdstrike.assessment.overall > 50 }; CEDAR tags = { Tier = "web" } } # Verified Access Endpoint resource "aws_verifiedaccess_endpoint" "internal_app" { verified_access_group_id = aws_verifiedaccess_group.web_apps.id endpoint_type = "load-balancer" attachment_type = "vpc" domain_certificate_arn = aws_acm_certificate.app.arn application_domain = "app.internal.company.com" endpoint_domain_prefix = "myapp" description = "Internal Application" load_balancer_options { load_balancer_arn = aws_lb.internal.arn port = 443 protocol = "https" subnet_ids = var.private_subnet_ids } security_group_ids = [aws_security_group.verified_access.id] policy_document = <<-CEDAR permit(principal, action, resource) when { context.okta.groups.contains("app-users") }; CEDAR } # Logging configuration resource "aws_verifiedaccess_instance_logging_configuration" "main" { verifiedaccess_instance_id = aws_verifiedaccess_instance.main.id access_logs { cloudwatch_logs { enabled = true log_group = aws_cloudwatch_log_group.verified_access.name } s3 { enabled = true bucket_name = aws_s3_bucket.access_logs.id prefix = "verified-access/" } } } resource "aws_cloudwatch_log_group" "verified_access" { name = "/aws/verified-access/production" retention_in_days = 90 } ``` ## Multi-Account Deployment with AWS RAM ```hcl # Share Verified Access Group across accounts via RAM resource "aws_ram_resource_share" "verified_access" { name = "verified-access-share" allow_external_principals = false } resource "aws_ram_resource_association" "group_share" { resource_arn = aws_verifiedaccess_group.web_apps.verified_access_group_arn resource_share_arn = aws_ram_resource_share.verified_access.arn } resource "aws_ram_principal_association" "workload_ou" { principal = "arn:aws:organizations::123456789012:ou/o-xxxx/ou-xxxx-xxxxxxxx" resource_share_arn = aws_ram_resource_share.verified_access.arn } ``` ## Monitoring and Logging ```bash # Query access logs in CloudWatch aws logs filter-log-events \ --log-group-name /aws/verified-access/production \ --filter-pattern '{ $.status_code = "403" }' \ --start-time $(date -d '1 hour ago' +%s000) # CloudWatch alarm for access denials aws cloudwatch put-metric-alarm \ --alarm-name "VerifiedAccess-HighDenialRate" \ --metric-name "AccessDenied" \ --namespace "AWS/VerifiedAccess" \ --statistic Sum \ --period 300 \ --threshold 100 \ --comparison-operator GreaterThanThreshold \ --evaluation-periods 2 \ --alarm-actions arn:aws:sns:us-east-1:123456789012:security-alerts ``` ## Security Best Practices 1. **Layer policies**: Use group-level policies for broad controls and endpoint-level for app-specific restrictions 2. **Require device trust**: Always include device posture checks in Cedar policies 3. **Enable access logging**: Send to both CloudWatch and S3 for real-time monitoring and long-term retention 4. **Use RAM for multi-account**: Share groups across OUs instead of duplicating configuration 5. **Rotate OIDC secrets**: Automate client secret rotation via Secrets Manager 6. **Test policies in non-production**: Validate Cedar policies before production deployment 7. **Set high device trust thresholds**: Require overall score above 70 for production access 8. **Monitor for policy drift**: Use AWS Config rules to detect unauthorized changes ## References - [AWS Verified Access Documentation](https://docs.aws.amazon.com/verified-access/) - [Cedar Policy Language](https://www.cedarpolicy.com/) - [Building Zero Trust Across Multi-Account AWS Environments](https://aws.amazon.com/blogs/networking-and-content-delivery/building-zero-trust-access-across-multi-account-aws-environments/) - [Visual Guide to AWS Verified Access Setup](https://medium.com/@chaim_sanders/a-visual-guide-to-setting-up-aws-verified-access-1333466f7222) - [NIST SP 800-207 Zero Trust Architecture](https://csrc.nist.gov/publications/detail/sp/800-207/final)