Initial saml-auth-proxy code. (#14017)

This commit is contained in:
Robert Fairburn
2023-09-28 12:48:57 -05:00
committed by GitHub
parent 00e71761c5
commit 0f4e24a40a
12 changed files with 441 additions and 16 deletions
@@ -3,8 +3,9 @@
provider "registry.terraform.io/hashicorp/aws" {
version = "4.57.0"
constraints = ">= 2.67.0, >= 3.0.0, >= 3.73.0, >= 4.6.0, >= 4.9.0, >= 4.18.0, >= 4.27.0, >= 4.30.0, >= 4.40.0, 4.57.0"
constraints = ">= 2.67.0, >= 3.0.0, >= 3.73.0, >= 4.6.0, >= 4.8.0, >= 4.9.0, >= 4.18.0, >= 4.27.0, >= 4.30.0, >= 4.40.0, 4.57.0"
hashes = [
"h1:07cB50nnQkmdSHw5ehVEOny4czNYxAAdp00RUKWVa/w=",
"h1:0bd5IKkEF1TGE4tgm0VuVMFQg2s6GOXJBU+/b/siYKw=",
"zh:07d89ad94267b7d6285fd65fbd67f8680e111abf9bbcbcac2e30154262fbbe46",
"zh:0eeee044e6fc285c20241d3de7f9b79450cab2df1452a9c18c0bed1090085a25",
@@ -29,6 +30,7 @@ provider "registry.terraform.io/hashicorp/external" {
constraints = ">= 1.0.0"
hashes = [
"h1:bROCw6g5D/3fFnWeJ01L4IrdnJl1ILU8DGDgXCtYzaY=",
"h1:gznGscVJ0USxy4CdihpjRKPsKvyGr/zqPvBoFLJTQDc=",
"zh:001e2886dc81fc98cf17cf34c0d53cb2dae1e869464792576e11b0f34ee92f54",
"zh:2eeac58dd75b1abdf91945ac4284c9ccb2bfb17fa9bdb5f5d408148ff553b3ee",
"zh:2fc39079ba61411a737df2908942e6970cb67ed2f4fb19090cd44ce2082903dd",
@@ -49,6 +51,7 @@ provider "registry.terraform.io/hashicorp/local" {
constraints = ">= 1.0.0"
hashes = [
"h1:R97FTYETo88sT2VHfMgkPU3lzCsZLunPftjSI5vfKe8=",
"h1:ZUEYUmm2t4vxwzxy1BvN1wL6SDWrDxfH7pxtzX8c6d0=",
"zh:53604cd29cb92538668fe09565c739358dc53ca56f9f11312b9d7de81e48fab9",
"zh:66a46e9c508716a1c98efbf793092f03d50049fa4a83cd6b2251e9a06aca2acf",
"zh:70a6f6a852dd83768d0778ce9817d81d4b3f073fab8fa570bff92dcb0824f732",
@@ -65,9 +68,11 @@ provider "registry.terraform.io/hashicorp/local" {
}
provider "registry.terraform.io/hashicorp/null" {
version = "3.2.1"
version = "3.2.1"
constraints = ">= 2.0.0"
hashes = [
"h1:FbGfc+muBsC17Ohy5g806iuI1hQc4SIexpYCrQHQd8w=",
"h1:ydA0/SNRVB1o95btfshvYsmxA+jZFRZcvKzZSB+4S1M=",
"zh:58ed64389620cc7b82f01332e27723856422820cfd302e304b5f6c3436fb9840",
"zh:62a5cc82c3b2ddef7ef3a6f2fedb7b9b3deff4ab7b414938b08e51d6e8be87cb",
"zh:63cff4de03af983175a7e37e52d4bd89d990be256b16b5c7f919aff5ad485aa5",
@@ -87,6 +92,7 @@ provider "registry.terraform.io/hashicorp/random" {
version = "3.5.1"
constraints = ">= 2.2.0"
hashes = [
"h1:IL9mSatmwov+e0+++YX2V6uel+dV6bn+fC/cnGDK3Ck=",
"h1:VSnd9ZIPyfKHOObuQCaKfnjIHRtR7qTw19Rz8tJxm+k=",
"zh:04e3fbd610cb52c1017d282531364b9c53ef72b6bc533acb2a90671957324a64",
"zh:119197103301ebaf7efb91df8f0b6e0dd31e6ff943d231af35ee1831c599188d",
@@ -106,6 +112,7 @@ provider "registry.terraform.io/hashicorp/random" {
provider "registry.terraform.io/hashicorp/tls" {
version = "4.0.4"
hashes = [
"h1:GZcFizg5ZT2VrpwvxGBHQ/hO9r6g0vYdQqx3bFD3anY=",
"h1:pe9vq86dZZKCm+8k1RhzARwENslF3SXb9ErHbQfgjXU=",
"zh:23671ed83e1fcf79745534841e10291bbf34046b27d6e68a5d0aab77206f4a55",
"zh:45292421211ffd9e8e3eb3655677700e3c5047f71d8f7650d2ce30242335f848",
@@ -101,17 +101,79 @@ module "main" {
}
}
extra_iam_policies = concat(module.firehose-logging.fleet_extra_iam_policies, module.osquery-carve.fleet_extra_iam_policies, module.ses.fleet_extra_iam_policies)
extra_execution_iam_policies = concat(module.mdm.extra_execution_iam_policies, [aws_iam_policy.sentry.arn])
extra_execution_iam_policies = concat(module.mdm.extra_execution_iam_policies, [aws_iam_policy.sentry.arn]) #, module.saml_auth_proxy.fleet_extra_execution_policies)
extra_environment_variables = merge(module.mdm.extra_environment_variables, module.firehose-logging.fleet_extra_environment_variables, module.osquery-carve.fleet_extra_environment_variables, module.ses.fleet_extra_environment_variables, local.extra_environment_variables)
extra_secrets = merge(module.mdm.extra_secrets, local.sentry_secrets)
# extra_load_balancers = [{
# target_group_arn = module.saml_auth_proxy.lb_target_group_arn
# container_name = "fleet"
# container_port = 8080
# }]
}
alb_config = {
name = local.customer
access_logs = {
bucket = module.logging_alb.log_s3_bucket_id
prefix = local.customer
enabled = true
}
alb_config = {
name = local.customer
access_logs = {
bucket = module.logging_alb.log_s3_bucket_id
prefix = local.customer
enabled = true
}
# extra_target_groups = [
# {
# name = module.saml_auth_proxy.name
# backend_protocol = "HTTP"
# backend_port = 80
# target_type = "ip"
# health_check = {
# path = "/_health"
# matcher = "200"
# timeout = 10
# interval = 15
# healthy_threshold = 5
# unhealthy_threshold = 5
# }
# }
# ]
# https_listener_rules = [{
# https_listener_index = 0
# priority = 9000
# actions = [{
# type = "forward"
# target_group_index = 1
# }]
# conditions = [{
# path_patterns = ["/device/*", "/api/*/fleet/device/*", "/saml/*"]
# }]
# }, {
# https_listener_index = 0
# priority = 1
# actions = [{
# type = "forward"
# target_group_index = 0
# }]
# conditions = [{
# path_patterns = ["/api/*/fleet/device/*/migrate_mdm", "/api/*/fleet/device/*/rotate_encryption_key"]
# }]
# }, {
# https_listener_index = 0
# priority = 2
# actions = [{
# type = "forward"
# target_group_index = 0
# }]
# conditions = [{
# path_patterns = ["/api/*/fleet/device/*/debug/errors", "/api/*/fleet/device/*/desktop"]
# }]
# }, {
# https_listener_index = 0
# priority = 3
# actions = [{
# type = "forward"
# target_group_index = 0
# }]
# conditions = [{
# path_patterns = ["/api/*/fleet/device/*/refetch", "/api/*/fleet/device/*/transparency"]
# }]
# }]
}
}
@@ -297,3 +359,20 @@ module "waf" {
name = local.customer
lb_arn = module.main.byo-vpc.byo-db.alb.lb_arn
}
# module "saml_auth_proxy" {
# # source = "github.com/fleetdm/fleet//terraform/addons/saml-auth-proxy?ref=main"
# # public_alb_security_group_id = module.main.byo-vpc.byo-db.alb.security_group_id
# idp_metadata_url = "https://dev-99185346.okta.com/app/exkbcrjeqmahXWvW45d7/sso/saml/metadata"
# customer_prefix = local.customer
# ecs_cluster = module.main.byo-vpc.byo-db.byo-ecs.service.cluster
# ecs_execution_iam_role_arn = module.main.byo-vpc.byo-db.byo-ecs.execution_iam_role_arn
# ecs_iam_role_arn = module.main.byo-vpc.byo-db.byo-ecs.iam_role_arn
# security_groups = module.main.byo-vpc.byo-db.byo-ecs.service.network_configuration[0].security_groups
# base_url = "https://dogfood.fleetdm.com/"
# subnets = module.main.byo-vpc.byo-db.byo-ecs.service.network_configuration[0].subnets
# vpc_id = module.main.vpc.vpc_id
# logging_options = null # Figure it out later
# alb_target_group_arn = module.main.byo-vpc.byo-db.alb.target_group_arns[1]
# cookie_max_age = "15m"
# }
@@ -0,0 +1,5 @@
mkdir -p $(dirname ${SAML_PROXY_SP_CERT_PATH:?})
mkdir -p $(dirname ${SAML_PROXY_SP_KEY_PATH:?})
echo "${SAML_PROXY_SP_CERT_BYTES:?}" > "${SAML_PROXY_SP_CERT_PATH:?}"
echo "${SAML_PROXY_SP_KEY_BYTES:?}" > "${SAML_PROXY_SP_KEY_PATH:?}"
/usr/bin/saml-auth-proxy
+229
View File
@@ -0,0 +1,229 @@
data "aws_region" "current" {}
resource "aws_cloudwatch_log_group" "saml_auth_proxy" {
name = "${var.customer_prefix}-saml-auth-proxy"
}
data "aws_iam_policy_document" "saml_auth_proxy" {
// allow saml_auth_proxy to obtain certs from secrets manager
statement {
effect = "Allow"
actions = ["secretsmanager:GetSecretValue"]
resources = [aws_secretsmanager_secret.saml_auth_proxy_cert.arn]
}
}
resource "aws_iam_policy" "saml_auth_proxy" {
name = "${var.customer_prefix}-saml-auth-proxy"
description = "IAM Policy to provide saml_auth_proxy access to secrets"
policy = data.aws_iam_policy_document.saml_auth_proxy.json
}
resource "aws_secretsmanager_secret" "saml_auth_proxy_cert" {
name_prefix = "${var.customer_prefix}-saml-auth-proxy-cert"
}
resource "aws_security_group" "saml_auth_proxy_alb" {
#checkov:skip=CKV2_AWS_5:False positive
vpc_id = var.vpc_id
description = "Fleet ALB Security Group"
ingress {
description = "Internal HTTP back to Fleet"
from_port = 8080
to_port = 8080
protocol = "tcp"
security_groups = [aws_security_group.saml_auth_proxy_service.id]
}
egress {
description = "Egress to all"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = []
}
}
resource "aws_security_group" "saml_auth_proxy_service" {
#checkov:skip=CKV2_AWS_5:False positive
vpc_id = var.vpc_id
description = "Fleet ALB Security Group"
ingress {
description = "Internal HTTP back to Fleet"
from_port = 8080
to_port = 8080
protocol = "tcp"
cidr_blocks = ["10.0.0.0/8"]
}
egress {
description = "Egress to all"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = []
}
}
module "saml_auth_proxy_alb" {
source = "terraform-aws-modules/alb/aws"
version = "8.2.1"
name = "${var.customer_prefix}-saml-auth-proxy"
load_balancer_type = "application"
vpc_id = var.vpc_id
subnets = var.subnets
security_groups = [aws_security_group.saml_auth_proxy_alb.id]
# FIXME: Get this working eventually.
# access_logs = var.alb_config.access_logs
internal = true
target_groups = [
{
name = "${var.customer_prefix}-saml-to-fleet"
backend_protocol = "HTTP"
backend_port = 8080
target_type = "ip"
health_check = {
path = "/healthz"
matcher = "200"
timeout = 10
interval = 15
healthy_threshold = 5
unhealthy_threshold = 5
}
}
]
http_tcp_listeners = [
{
port = 8080
protocol = "HTTP"
target_group_index = 0
}
]
}
resource "aws_ecs_task_definition" "saml_auth_proxy" {
family = "${var.customer_prefix}-saml-auth-proxy"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
execution_role_arn = var.ecs_execution_iam_role_arn
task_role_arn = var.ecs_iam_role_arn
cpu = 256
memory = 1024
container_definitions = jsonencode(
[
{
name = "${var.customer_prefix}-saml-auth-proxy"
image = var.saml_auth_proxy_image
cpu = 256
memory = 512
mountPoints = []
volumesFrom = []
essential = true
ulimits = [
{
softLimit = 9999,
hardLimit = 9999,
name = "nofile"
}
]
portMappings = [
{
# This port is the same that the contained application also uses
containerPort = 8080
protocol = "tcp"
}
]
networkMode = "awsvpc"
logConfiguration = {
logDriver = "awslogs"
options = var.logging_options != null ? var.logging_options : {
awslogs-group = "${var.customer_prefix}-saml-auth-proxy"
awslogs-region = data.aws_region.current.name
awslogs-stream-prefix = "saml-auth-proxy"
}
}
workingDirectory = "/go",
secrets = [
{
name = "SAML_PROXY_SP_CERT_BYTES"
valueFrom = "${aws_secretsmanager_secret.saml_auth_proxy_cert.arn}:cert::"
},
{
name = "SAML_PROXY_SP_KEY_BYTES"
valueFrom = "${aws_secretsmanager_secret.saml_auth_proxy_cert.arn}:key::"
},
]
environment = [
{
name = "SAML_PROXY_SP_CERT_PATH"
value = "/tmp/saml-auth-proxy/cert.pem"
},
{
name = "SAML_PROXY_SP_KEY_PATH"
value = "/tmp/saml-auth-proxy/key.pem"
},
{
name = "SAML_PROXY_BACKEND_URL"
value = "http://${module.saml_auth_proxy_alb.lb_dns_name}:8080/"
},
{
name = "SAML_PROXY_IDP_METADATA_URL"
value = var.idp_metadata_url
},
{
name = "SAML_PROXY_BASE_URL"
value = var.base_url
},
{
name = "SAML_PROXY_COOKIE_MAX_AGE"
value = var.cookie_max_age
},
{
name = "SAML_PROXY_COOKIE_NAME"
value = "saml_auth_proxy_token"
},
{
name = "SAML_PROXY_ALLOW_IDP_INITIATED"
value = "true"
},
]
entryPoint = ["/bin/sh"],
command = ["-c", file("${path.module}/files/saml-auth-proxy.sh")]
}
]
)
lifecycle {
create_before_destroy = true
}
}
resource "aws_ecs_service" "saml_auth_proxy" {
name = "${var.customer_prefix}_saml_auth_proxy"
launch_type = "FARGATE"
cluster = var.ecs_cluster
task_definition = aws_ecs_task_definition.saml_auth_proxy.arn
desired_count = var.proxy_containers
deployment_minimum_healthy_percent = 100
deployment_maximum_percent = 200
network_configuration {
subnets = var.subnets
security_groups = [aws_security_group.saml_auth_proxy_service.id]
}
load_balancer {
target_group_arn = var.alb_target_group_arn
container_name = "${var.customer_prefix}-saml-auth-proxy"
container_port = 8080
}
}
@@ -0,0 +1,13 @@
output "fleet_extra_execution_policies" {
value = [
aws_iam_policy.saml_auth_proxy.arn
]
}
output "name" {
value = "${var.customer_prefix}-saml-auth-proxy"
}
output "lb_target_group_arn" {
value = module.saml_auth_proxy_alb.target_group_arns[0]
}
@@ -0,0 +1,71 @@
variable "cookie_max_age" {
type = string
default = "1h"
}
variable "alb_target_group_arn" {
type = string
}
# variable "public_alb_security_group_id" {
# type = string
# }
variable "idp_metadata_url" {
type = string
}
variable "customer_prefix" {
type = string
description = "customer prefix to use to namespace all resources"
default = "fleet"
}
variable "ecs_cluster" {
type = string
}
variable "ecs_execution_iam_role_arn" {
type = string
}
variable "ecs_iam_role_arn" {
type = string
}
variable "proxy_containers" {
type = number
default = 1
}
variable "logging_options" {
type = object({
awslogs-group = string
awslogs-region = string
awslogs-stream-prefix = string
})
}
variable "saml_auth_proxy_image" {
type = string
default = "itzg/saml-auth-proxy:1.12.0@sha256:ddff17caa00c1aad64d6c7b2e1d5eb93d97321c34d8ad12a25cfd8ce203db723"
}
variable "security_groups" {
type = list(string)
nullable = false
}
variable "base_url" {
type = string
}
variable "subnets" {
type = list(string)
nullable = false
}
variable "vpc_id" {
type = string
nullable = false
}
+14 -4
View File
@@ -7,6 +7,13 @@ locals {
name = k
valueFrom = v
}]
load_balancers = concat([
{
target_group_arn = var.fleet_config.loadbalancer.arn
container_name = "fleet"
container_port = 8080
}
], var.fleet_config.extra_load_balancers)
}
data "aws_region" "current" {}
@@ -21,10 +28,13 @@ resource "aws_ecs_service" "fleet" {
deployment_maximum_percent = 200
health_check_grace_period_seconds = 30
load_balancer {
target_group_arn = var.fleet_config.loadbalancer.arn
container_name = "fleet"
container_port = 8080
dynamic "load_balancer" {
for_each = local.load_balancers
content {
target_group_arn = load_balancer.value.target_group_arn
container_name = load_balancer.value.container_name
container_port = load_balancer.value.container_port
}
}
lifecycle {
@@ -57,6 +57,7 @@ variable "fleet_config" {
loadbalancer = object({
arn = string
})
extra_load_balancers = optional(list(any), [])
networking = object({
subnets = list(string)
security_groups = optional(list(string), null)
@@ -131,6 +132,7 @@ variable "fleet_config" {
loadbalancer = {
arn = null
}
extra_load_balacners = []
networking = {
subnets = null
security_groups = null
+2 -2
View File
@@ -37,7 +37,7 @@ module "alb" {
security_groups = concat(var.alb_config.security_groups, [aws_security_group.alb.id])
access_logs = var.alb_config.access_logs
target_groups = [
target_groups = concat([
{
name = var.alb_config.name
backend_protocol = "HTTP"
@@ -52,7 +52,7 @@ module "alb" {
unhealthy_threshold = 5
}
}
]
], var.alb_config.extra_target_groups)
# Require TLS 1.2 as earlier versions are insecure
listener_ssl_policy_default = "ELBSecurityPolicy-TLS-1-2-2017-01"
+3
View File
@@ -129,6 +129,7 @@ variable "fleet_config" {
}), {
arn = null
})
extra_load_balancers = optional(list(any), [])
networking = optional(object({
subnets = list(string)
security_groups = optional(list(string), null)
@@ -206,6 +207,7 @@ variable "fleet_config" {
loadbalancer = {
arn = null
}
extra_load_balancers = []
networking = {
subnets = null
security_groups = null
@@ -252,6 +254,7 @@ variable "alb_config" {
access_logs = optional(map(string), {})
certificate_arn = string
allowed_cidrs = optional(list(string), ["0.0.0.0/0"])
extra_target_groups = optional(any, [])
https_listener_rules = optional(any, [])
})
}
+3
View File
@@ -218,6 +218,7 @@ variable "fleet_config" {
}), {
arn = null
})
extra_load_balancers = optional(list(any), [])
networking = optional(object({
subnets = list(string)
security_groups = optional(list(string), null)
@@ -295,6 +296,7 @@ variable "fleet_config" {
loadbalancer = {
arn = null
}
extra_load_balancers = []
networking = {
subnets = null
security_groups = null
@@ -341,6 +343,7 @@ variable "alb_config" {
access_logs = optional(map(string), {})
certificate_arn = string
allowed_cidrs = optional(list(string), ["0.0.0.0/0"])
extra_target_groups = optional(any, [])
https_listener_rules = optional(any, [])
})
}
+3
View File
@@ -270,6 +270,7 @@ variable "fleet_config" {
}), {
arn = null
})
extra_load_balancers = optional(list(any), [])
networking = optional(object({
subnets = list(string)
security_groups = optional(list(string), null)
@@ -347,6 +348,7 @@ variable "fleet_config" {
loadbalancer = {
arn = null
}
extra_load_balancers = []
networking = {
subnets = null
security_groups = null
@@ -391,6 +393,7 @@ variable "alb_config" {
security_groups = optional(list(string), [])
access_logs = optional(map(string), {})
allowed_cidrs = optional(list(string), ["0.0.0.0/0"])
extra_target_groups = optional(any, [])
https_listener_rules = optional(any, [])
})
default = {}