Okta conditoinal access
Since mTLS is used for conditional access with Okta, we need to move the connection request to a separate subdomain that requires mTLS and uses the CA stored in Fleet and the certificate issued to the device seeking access.
This module helps automate that for a terraform deployment. By default the module assumes that okta is added as a subdomain to the Fleet primary domain (e.g. fleet.example.com leverages okta.fleet.example.com), but this can be customized.
A separate load balancer is created to handle the mTLS authorization. The module only handles basic certificate validation and passes the certificate serial on to Fleet via a header in order to handle the final authorization or revocation step.
Requirements
- A separate subdomain for conditional access
- A valid TLS certificate issued for the domain name
- HTTPS listener rules applied to the main Fleet load balancer to handle redirects (generated as an output of this module)
- The load balancer configured by this module applied as an extra load balancer to the fleet_config on the primary Fleet in terraform
- The CA certificate in PEM format stored at "resources/conditional-ca.pem"
Obtaining the CA certificate
Run these commands while in your Fleet terraform directory/checkout.
mkdir -p resources
# Replace with your domain name. This is the public cert and doesn't need special protection.
curl 'https://fleet.example.com/api/fleet/conditional_access/scep?operation=GetCACert' --output cacert.tmp
openssl x509 -inform der -in cacert.tmp -out resources/conditional-ca.pem
rm cacert.tmp
Configuration Example
locals {
domain_name = "fleet.example.com"
okta_subdomain = "okta.${local.domain_name}"
}
module "okta_acm" {
source = "terraform-aws-modules/acm/aws"
version = "4.3.1"
domain_name = local.okta_subdomain
# Assumes you are managing your domain in route53 inside of this config.
zone_id = aws_route53_zone.main.id
wait_for_validation = true
}
resource "aws_route53_record" "okta" {
# If you change the route53_zone to a data source this also needs to become "data.aws_route53_zone.main.id"
zone_id = aws_route53_zone.main.id
name = local.okta_subdomain
type = "A"
alias {
name = module.okta-conditional-access.alb.lb_dns_name
zone_id = module.okta-conditional-access.alb.lb_zone_id
evaluate_target_health = true
}
}
module "fleet" {
source = "github.com/fleetdm/fleet-terraform?depth=1&ref=tf-mod-root-v1.19.0"
...
fleet_config = {
...
# Required to allow the ALB to talk to the ECS containers.
networking = {
ingress_sources = {
security_groups = [module.okta-conditional-access.alb.security_group_id]
}
}
extra_load_balancers = [{
target_group_arn = module.okta-conditional-access.alb.target_groups["tg-0"].arn
container_name = "fleet"
container_port = 8080
}]
}
...
alb_config = {
...
# If you have existing rules, use concat() to combine these with them.
# Note: by default the rules use the highest priority indexes starting at 1,
# but that can be configured inside the module.
https_listener_rules = module.okta-conditional-access.redirect_rules
}
}
module "okta-conditional-access" {
source = "github.com/fleetdm/fleet-terraform/addons/okta-conditional-access?depth=1&ref=tf-mod-addon-okta-conditional-access-v0.5.0"
customer_prefix = "fleet"
vpc_id = module.fleet.vpc.vpc_id
trust_store_s3_config = {
bucket_prefix = "fleet-okta-trust-store"
}
alb_config = {
name = "fleet-okta"
# If using the ALB logging module:
access_logs = {
bucket = module.logging_alb.log_s3_bucket_id
prefix = "fleet"
enabled = true
}
subnets = module.fleet.vpc.public_subnets
certificate_arn = module.okta_acm.acm_certificate_arn
idle_timeout = 60
trust_store = {
ca_certificates_bundle_s3_key = "ca.pem"
ca_certificates_bundle_s3_object_version = null
ca_certificates_bundle_file = "${path.module}/resources/conditional-ca.pem"
create_trust_store_revocation = false
trust_store_revocation_lists = {}
}
}
}
Final comments
Once this is configured, when you attempt to leverage the conditional access, you will be prompted to use the certificate that is installed on your system if it already there during the authoriation redriect. If the certificate is not installed, the load balancer will immediately terminate the connection during the handshake and you will get a connection resset error message.
Requirements
No requirements.
Providers
| Name | Version |
|---|---|
| aws | 6.28.0 |
Modules
| Name | Source | Version |
|---|---|---|
| lb_trust_store_bucket | terraform-aws-modules/s3-bucket/aws | 5.9.1 |
| okta_mtls_alb | terraform-aws-modules/alb/aws | 10.4.0 |
Resources
| Name | Type |
|---|---|
| aws_lb_trust_store.this | resource |
| aws_lb_trust_store_revocation.this | resource |
| aws_s3_object.trust_store_ca | resource |
| aws_security_group.alb | resource |
| aws_caller_identity.current | data source |
| aws_iam_policy_document.alb_trust_store_restricted | data source |
| aws_partition.current | data source |
| aws_region.current | data source |
Inputs
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| alb_config | n/a | object({ |
n/a | yes |
| customer_prefix | n/a | string |
"fleet" |
no |
| redirect_priority | The priority of the redirect https_listener_rule generated in the output | number |
1 |
no |
| trust_store_s3_config | n/a | object({ |
{ |
no |
| vpc_id | n/a | string |
n/a | yes |
Outputs
| Name | Description |
|---|---|
| alb | n/a |
| alb_security_group | n/a |
| lb_trust_store__bucket | n/a |
| redirect_rules | n/a |