Skip to content

Instantly share code, notes, and snippets.

@the-maldridge
Created August 5, 2021 15:56
Show Gist options
  • Save the-maldridge/f23c888c711c1d26e672fa25af5bc8c1 to your computer and use it in GitHub Desktop.
Save the-maldridge/f23c888c711c1d26e672fa25af5bc8c1 to your computer and use it in GitHub Desktop.
Pomerium on Nomad with Traefik

Pomerium on Nomad - Forward Auth

Pomerium is a potential implementation of a BeyondCorp architecture that is similar to ORY OathKeeper or GCP IAP. This architecture is a newer security model than traditional VPNs and allows you to guard your internal services while still having them be directly internet facing. This makes both your individual machine story much easier as well as your BYOD story.

Pomerium has several limitations that may make it unsuitable for your network.

  • In order to work with a consul service mesh you are limited to forward-auth mode. This has only limited upstream support and can be fiddly to get working due to questionable documentation surrounding it both with proxy implementations and with Pomerium itself.
  • Even though Pomerium isn't proxying the traffic, the policy that you can create is limited by what Envoy (which is vendored into Pomerium as a binary artifact...) is capable of matching. This is why Pomerium has a static port attached to its consul service registration.
  • Pomerium does not document any part of their API surface, which makes real-time policy updates, such as those coordinated by a deployment manager, almost impossible to implement.

TL;DR this works and is handling production traffic, but I'll probably keep looking for a better IAP implementation that has better mesh with consul service meshes and better support for doing realtime policy updates without a hosted "enterprise" component.

job "pomerium" {
type = "service"
datacenters = ["DC1"]
namespace = "infrastructure"
priority = 90
group "pomerium" {
network {
mode = "bridge"
port "http" {
to = 443
static = 8443
}
}
service {
port = "http"
tags = [
"secureproxy.enable=true",
"secureproxy.http.routers.pomerium-pomerium.rule=Host(`auth.secure.example.com`)",
]
}
task "app" {
driver = "docker"
vault {
policies = ["app-pomerium"]
}
config {
image = "pomerium/pomerium:v0.14.7"
args = ["-config", "/local/config.yml"]
}
template {
data = <<EOF
---
{{- with secret "secret/pomerium/config" }}
cookie_secret: {{.Data.CookieSecret}}
idp_client_id: {{.Data.ClientID}}
idp_client_secret: {{.Data.ClientSecret}}
idp_provider: oidc
idp_provider_url: https://oidc.example.com
idp_scopes: "openid,profile,email,groups,offline_access"
insecure_server: true
shared_secret: {{.Data.SharedSecret}}
signing_key: {{.Data.SigningKey}}
{{- end }}
authenticate_service_url: https://auth.secure.example.com
forward_auth_url: http://pomerium-pomerium.service.consul:8443
xff_num_trusted_hops: 2
policy:
- from: https://auth.secure.example.com
to: https://auth.secure.example.com
allow_public_unauthenticated_access: true
- from: https://verify.secure.example.com
to: https://verify.secure.example.com
allowed_domains:
- voiceops.com
EOF
destination = "local/config.yml"
perms = 640
}
}
}
group "verify" {
network {
mode = "bridge"
port "http" { to = 80 }
}
service {
port = "http"
tags = [
"secureproxy.enable=true",
"secureproxy.http.routers.pomerium-verify.rule=Host(`verify.secure.example.com`)",
]
}
task "app" {
driver = "docker"
config {
image = "pomerium/verify:v0.0.4"
}
}
}
}
job "secureproxy" {
datacenters = ["DC1"]
namespace = "infrastructure"
type = "system"
priority = 79
group "lb" {
network {
mode = "host"
port "http" { static = 80 }
port "metrics" { static = 8080 }
}
task "traefik" {
driver = "docker"
config {
image = "traefik:v2.4.9"
network_mode = "host"
dns_servers = ["127.0.0.1"]
args = [
"--accesslog=true",
"--api.dashboard",
"--api.insecure=true",
"--entrypoints.http.address=:80",
"--entrypoints.http.transport.lifecycle.requestacceptgracetimeout=2m",
"--entrypoints.http.http.middlewares=authz@file",
"--entrypoints.traefik.address=:8080",
"--entrypoints.traefik.transport.lifecycle.requestacceptgracetimeout=2m",
"--log.level=INFO",
"--metrics.prometheus",
"--ping.entryPoint=traefik",
"--pilot.dashboard=false",
"--providers.consulcatalog.defaultrule=Host(`{{normalize .Name}}.secure.example.com`)",
"--providers.consulcatalog.exposedbydefault=false",
"--providers.consulcatalog.endpoint.address=127.0.0.1:8500",
"--providers.consulcatalog.prefix=secureproxy",
"--providers.file.filename=/local/dynamic.toml",
]
}
resources {
cpu = 500
memory = 64
}
template {
data = <<EOF
[http.middlewares]
[http.middlewares.authz.forwardauth]
address = "http://pomerium-pomerium.service.consul:8443"
trustForwardHeader = true
authResponseHeaders = ["X-Pomerium-Jwt-Assertion", "X-Pomerium-Claim-Email"]
EOF
destination = "/local/dynamic.toml"
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment