{"id":78578,"date":"2026-09-14T16:06:04","date_gmt":"2026-09-14T16:06:04","guid":{"rendered":"https:\/\/www.devopsschool.com\/blog\/?p=78578"},"modified":"2026-09-14T16:06:08","modified_gmt":"2026-09-14T16:06:08","slug":"pomerium-tutorial-complete-end-to-end-guide-to-zero-trust-access-identity-aware-proxy-sso-kubernetes-ssh-and-production-deployment","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/blog\/pomerium-tutorial-complete-end-to-end-guide-to-zero-trust-access-identity-aware-proxy-sso-kubernetes-ssh-and-production-deployment\/","title":{"rendered":"Pomerium Tutorial: Complete End-to-End Guide to Zero-Trust Access, Identity-Aware Proxy, SSO, Kubernetes, SSH and Production Deployment"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">The tutorial below targets <strong>Pomerium Core<\/strong>, the open-source\/self-managed edition, using the current <strong>v0.33 stable generation<\/strong> as the baseline. Pomerium\u2019s documentation also has a rolling <code>main<\/code> version, so features appearing there may occasionally be newer than stable. Pomerium Core is Apache-2.0 licensed. (<a href=\"https:\/\/www.pomerium.com\/docs\/versions?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">1. What Is Pomerium?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Pomerium is an <strong>open-source identity-aware access proxy<\/strong> based on BeyondCorp and Zero Trust principles.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of deciding whether someone should access an application based primarily on where they are connected from\u2014office network, VPN, trusted subnet, static IP\u2014Pomerium asks:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Who is this user or workload, and is this specific request authorized right now?<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">It sits between users and protected applications:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">User\n  \u2502\n  \u2502 HTTPS\n  \u25bc\nPomerium\n  \u2502\n  \u251c\u2500\u2500 Authenticate user\n  \u251c\u2500\u2500 Verify session\n  \u251c\u2500\u2500 Evaluate policy\n  \u251c\u2500\u2500 Add identity context\n  \u2502\n  \u25bc\n<span class=\"hljs-keyword\">Private<\/span> Application<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Pomerium can protect internal websites, dashboards, APIs, Kubernetes applications, SSH servers, databases and other TCP\/UDP services without giving every user broad network-level access. For ordinary web applications, users normally need nothing more than their browser. (<a href=\"https:\/\/www.pomerium.com\/docs?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A useful one-line definition is:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Pomerium is an identity-aware reverse proxy that converts application access from network-based trust to per-request identity-based authorization.<\/strong><\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">2. The Problem Pomerium Solves<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Consider a company with these internal systems:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Grafana\nArgo CD\nKibana\nJenkins\nInternal admin portal\nKubernetes dashboards\nPostgreSQL\nRedis\nSSH servers\nInternal APIs<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The traditional design often looks like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">Internet\n   \u2502\n   \u25bc\nCorporate VPN\n   \u2502\n   \u25bc\n<span class=\"hljs-keyword\">Private<\/span> Network\n   \u2502\n   \u251c\u2500\u2500 Grafana\n   \u251c\u2500\u2500 Jenkins\n   \u251c\u2500\u2500 Database\n   \u251c\u2500\u2500 SSH\n   \u2514\u2500\u2500 Admin applications<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Once someone obtains VPN access, they may receive network connectivity to far more resources than they actually need.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The security decision is essentially:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Are you on the trusted network?\n\nYES\n \u2193\nYou can reach lots of things.<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Pomerium changes the model.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">Internet\n   \u2502\n   \u25bc\nPomerium\n   \u2502\n   \u251c\u2500\u2500 Who are you?\n   \u251c\u2500\u2500 Did the IdP authenticate you?\n   \u251c\u2500\u2500 What claims <span class=\"hljs-keyword\">do<\/span> you have?\n   \u251c\u2500\u2500 Which application are you requesting?\n   \u251c\u2500\u2500 What policy applies?\n   \u2514\u2500\u2500 Is <span class=\"hljs-keyword\">this<\/span> request permitted?\n             \u2502\n       \u250c\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2510\n       \u25bc           \u25bc\n     Allow        Deny\n       \u2502\n       \u25bc\nApplication<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Authorization happens at the resource boundary rather than merely at the network boundary. Pomerium evaluates policy continuously as requests pass through it. (<a href=\"https:\/\/www.pomerium.com\/docs\/capabilities\/authorization?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">3. Pomerium Is Not an Identity Provider<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">This is one of the most important concepts to understand.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pomerium does <strong>not normally maintain your users and passwords<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Instead, it integrates with your existing Identity Provider.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Examples include:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Microsoft Entra ID\nGoogle\nOkta\nAuth0\nGitHub\nGitLab\nAWS Cognito\nKeycloak\nPing Identity\nOneLogin\nOther OAuth\/OIDC providers<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Pomerium uses OAuth 2.0\/OIDC integrations and can work with major SSO providers. A normal integration requires a client ID, client secret and redirect URI. (<a href=\"https:\/\/www.pomerium.com\/docs\/integrations\/user-identity\/identity-providers?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Think of the responsibilities this way:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">Identity Provider\n        \u2502\n        \u2502 <span class=\"hljs-string\">\"This is Alice\"<\/span>\n        \u25bc\n     Pomerium\n        \u2502\n        \u2502 <span class=\"hljs-string\">\"Alice may access Grafana,\n        \u2502  but not Production Admin\"<\/span>\n        \u25bc\n    Application<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The IdP answers:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Who are you?<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Pomerium answers:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Given who you are, may you access this resource?<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">4. Pomerium vs VPN<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Pomerium can eliminate the need for a VPN for many application-access use cases, but it should not be confused with a general Layer-3 network VPN.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A VPN usually grants network connectivity:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Laptop\n  \u2502\n VPN\n  \u25bc\n10.0.0.0\/8\n\nLaptop can potentially discover\/reach:\n \u251c\u2500\u2500 host A\n \u251c\u2500\u2500 host B\n \u251c\u2500\u2500 database\n \u251c\u2500\u2500 printers\n \u251c\u2500\u2500 services\n \u2514\u2500\u2500 anything routing\/firewalls permit<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Pomerium instead grants access to explicitly configured resources:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Alice\n \u2502\n \u2514\u2500\u2500 Grafana          ALLOW\n\nAlice\n \u2502\n \u2514\u2500\u2500 Production DB    DENY\n\nBob\n \u2502\n \u2514\u2500\u2500 Production DB    ALLOW<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">That dramatically reduces implicit network trust.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">5. Pomerium vs Traditional Reverse Proxy<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">A normal reverse proxy generally performs:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">TLS termination\nrouting\nload balancing\nheaders<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Pomerium adds an identity-aware security layer:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">TLS\n+\nRouting\n+\nAuthentication\n+\nAuthorization\n+\nSSO\n+\nIdentity-aware policy\n+\nSigned identity propagation\n+\nTCP\/SSH access<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">In other words:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">NGINX \/ HAProxy \/ Envoy\n        +\nIdentity-aware access control\n        \u2248\nPomerium conceptually<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Pomerium itself uses Envoy in its data-plane architecture.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">6. When Should You Use Pomerium?<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Pomerium becomes especially attractive when you have resources that should be reachable remotely but should <strong>not simply become publicly accessible after login logic is bolted onto each application<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Excellent use cases include:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Use case<\/th><th>Example<\/th><\/tr><\/thead><tbody><tr><td>Internal applications<\/td><td>Admin portals<\/td><\/tr><tr><td>Observability<\/td><td>Grafana, Kibana<\/td><\/tr><tr><td>Deployment tooling<\/td><td>Argo CD, Jenkins<\/td><\/tr><tr><td>Infrastructure consoles<\/td><td>Kubernetes dashboards<\/td><\/tr><tr><td>Developer tools<\/td><td>Jupyter, internal IDEs<\/td><\/tr><tr><td>APIs<\/td><td>Internal REST\/gRPC APIs<\/td><\/tr><tr><td>SSH<\/td><td>Linux servers<\/td><\/tr><tr><td>Databases<\/td><td>PostgreSQL\/MySQL<\/td><\/tr><tr><td>Caches<\/td><td>Redis<\/td><\/tr><tr><td>Kubernetes services<\/td><td>Internal Ingress<\/td><\/tr><tr><td>Contractors<\/td><td>Restricted application access<\/td><\/tr><tr><td>Multi-cloud environments<\/td><td>AWS + GCP + on-prem<\/td><\/tr><tr><td>Machine identities<\/td><td>GitHub Actions\/Kubernetes workloads<\/td><\/tr><tr><td>MCP\/AI services<\/td><td>Restricted internal MCP endpoints<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">7. When Pomerium May Not Be the Right Tool<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Do not force Pomerium into every networking problem.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you need arbitrary IP routing such as:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">entire subnet access\nlegacy broadcast protocols\narbitrary workstation networking\nsite-to-site connectivity\ngeneral Layer-3 connectivity<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">a VPN, SD-WAN or network overlay may still be appropriate.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pomerium shines when the requirement can be expressed as:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Identity X should be able to access resource Y under policy Z.<\/strong><\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">8. Pomerium Product Editions<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Understanding the editions avoids a lot of confusion.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Product<\/th><th>Control Plane<\/th><th>Data Plane<\/th><th>Primary Use<\/th><\/tr><\/thead><tbody><tr><td><strong>Pomerium Core<\/strong><\/td><td>You manage it<\/td><td>Your infrastructure<\/td><td>Open-source\/self-managed<\/td><\/tr><tr><td><strong>Pomerium Zero<\/strong><\/td><td>Pomerium-managed<\/td><td>Your infrastructure<\/td><td>Easier managed control plane<\/td><\/tr><tr><td><strong>Pomerium Enterprise<\/strong><\/td><td>Self-hosted<\/td><td>Your infrastructure<\/td><td>Enterprise governance<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Pomerium Core is the open-source engine beneath the other deployment models.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zero adds a managed control plane.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enterprise adds capabilities such as centralized configuration, directory synchronization, RBAC\/namespaces, audit\/deployment history, service accounts and additional governance functionality while retaining the control plane in your environment. (<a href=\"https:\/\/www.pomerium.com\/docs?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For this tutorial, the primary focus is:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Pomerium Core \u2014 fully self-managed.<\/strong><\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">9. Pomerium Architecture<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Pomerium logically consists of four major services:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502                  POMERIUM                    \u2502\n\u2502                                              \u2502\n\u2502  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510       \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510   \u2502\n\u2502  \u2502    <span class=\"hljs-built_in\">Proxy<\/span>     \u2502\u2500\u2500\u2500\u2500\u2500\u2500\u25ba\u2502   Authorize    \u2502   \u2502\n\u2502  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518       \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518   \u2502\n\u2502          \u2502                       \u2502            \u2502\n\u2502          \u2502                \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u25bc\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510    \u2502\n\u2502          \u2502                \u2502  Databroker  \u2502    \u2502\n\u2502          \u2502                \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518    \u2502\n\u2502          \u2502                       \u25b2            \u2502\n\u2502  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25bc\u2500\u2500\u2500\u2500\u2500\u2500\u2510                \u2502            \u2502\n\u2502  \u2502 Authenticate \u2502\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518            \u2502\n\u2502  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518                             \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Pomerium documents these as Proxy, Authenticate, Authorize and Databroker services. They can run together in one process or, for specialized architectures, separately. (<a href=\"https:\/\/www.pomerium.com\/docs\/internals\/architecture?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">10. Proxy Service<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">The Proxy is the front door.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">User\n \u2502\n \u25bc\n<span class=\"hljs-built_in\">Proxy<\/span>\n \u2502\n \u25bc\nApplication<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">It:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>terminates user connections,<\/li>\n\n\n\n<li>selects routes,<\/li>\n\n\n\n<li>requests authorization decisions,<\/li>\n\n\n\n<li>forwards approved traffic,<\/li>\n\n\n\n<li>injects configured identity information,<\/li>\n\n\n\n<li>handles redirects and sessions.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Users normally interact with Proxy rather than directly with the other Pomerium services.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">11. Authenticate Service<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Authenticate handles user authentication.<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">User\n \u2502\n \u25bc\nPomerium Authenticate\n \u2502\n \u25bc\nIdentity Provider\n \u2502\n \u25bc\nGoogle \/ Entra \/ Okta \/ Keycloak<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">It performs the OAuth\/OIDC authentication flow and obtains identity information that Pomerium can use to establish the session. (<a href=\"https:\/\/www.pomerium.com\/docs\/capabilities\/authentication?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">12. Authorize Service<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Authorize evaluates the access policy.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Suppose:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Alice\nemail = alice@example.com\ngroups = engineering<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">and the route policy is:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">allow Engineering\ndeny everyone <span class=\"hljs-keyword\">else<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Authorize decides whether the request is permitted.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Importantly, Pomerium&#8217;s model evaluates authorization on requests rather than assuming that an authenticated user is universally trusted.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">13. Databroker<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Databroker maintains state required by the Pomerium services, including session-related information.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Conceptually:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Authenticate\n     \u2502\n     \u25bc\n Databroker\n     \u25b2\n     \u2502\n Authorize<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Storage choices include:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Memory\nFile\nPostgreSQL<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The default memory backend is convenient but loses state after restart. File storage provides persistence for a single instance. PostgreSQL supports persistent multi-replica deployments. (<a href=\"https:\/\/www.pomerium.com\/docs\/reference\/databroker?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">14. Complete Authentication Flow<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Suppose a user visits:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">https:<span class=\"hljs-comment\">\/\/grafana.access.example.com<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The flow looks like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-number\">1.<\/span> Browser\n     \u2502\n     \u25bc\n<span class=\"hljs-number\">2.<\/span> Pomerium <span class=\"hljs-built_in\">Proxy<\/span>\n     \u2502\n     \u2502 No valid session\n     \u25bc\n<span class=\"hljs-number\">3.<\/span> Authenticate Service\n     \u2502\n     \u25bc\n<span class=\"hljs-number\">4.<\/span> Identity Provider\n     \u2502\n     \u2502 User signs <span class=\"hljs-keyword\">in<\/span>\n     \u25bc\n<span class=\"hljs-number\">5.<\/span> Authenticate\n     \u2502\n     \u2502 Session information\n     \u25bc\n<span class=\"hljs-number\">6.<\/span> Databroker\n     \u2502\n     \u25bc\n<span class=\"hljs-number\">7.<\/span> <span class=\"hljs-built_in\">Proxy<\/span>\n     \u2502\n     \u25bc\n<span class=\"hljs-number\">8.<\/span> Authorize\n     \u2502\n     \u2502 Evaluate policy\n     \u25bc\n<span class=\"hljs-number\">9.<\/span> ALLOW\n     \u2502\n     \u25bc\n<span class=\"hljs-number\">10.<\/span> Grafana<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">On subsequent requests, the complete IdP login normally does not repeat. The existing Pomerium session is used and the authorization decision is reevaluated. (<a href=\"https:\/\/www.pomerium.com\/docs\/internals\/architecture?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">15. Core Pomerium Concepts<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Before installing anything, understand these five objects:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Identity Provider\nRoute\nPolicy\nSession\nUpstream<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">A Pomerium route connects an externally visible resource to an upstream:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">from<\/span>:\nhttps:<span class=\"hljs-comment\">\/\/grafana.example.com<\/span>\n\nto:\nhttp:<span class=\"hljs-comment\">\/\/grafana.internal:3000<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">A policy then determines who may traverse that route.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">16. Routes<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">A minimal route resembles:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">routes:\n  - <span class=\"hljs-keyword\">from<\/span>: https:<span class=\"hljs-comment\">\/\/grafana.access.example.com<\/span>\n    to: http:<span class=\"hljs-comment\">\/\/grafana:3000<\/span>\n\n    policy:\n      - allow:\n          and:\n            - authenticated_user: <span class=\"hljs-literal\">true<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><code>from<\/code> means:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Where users connect.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\"><code>to<\/code> means:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Where Pomerium sends authorized traffic.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">A route needs a destination such as <code>to<\/code>, <code>redirect<\/code>, or <code>response<\/code>, plus authorization logic before it becomes useful for protected traffic. (<a href=\"https:\/\/www.pomerium.com\/docs\/reference\/routes?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">17. Pomerium Policy Language \u2014 PPL<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Authorization is usually expressed using <strong>Pomerium Policy Language<\/strong>, or PPL.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">PPL supports:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">allow\ndeny\n\nand\nor\nnot\nnor<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">policy<\/span>:\n  <span class=\"hljs-selector-tag\">-<\/span> <span class=\"hljs-selector-tag\">allow<\/span>:\n      <span class=\"hljs-selector-tag\">and<\/span>:\n        <span class=\"hljs-selector-tag\">-<\/span> <span class=\"hljs-selector-tag\">domain<\/span>:\n            <span class=\"hljs-selector-tag\">is<\/span>: <span class=\"hljs-selector-tag\">example<\/span><span class=\"hljs-selector-class\">.com<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Users whose authenticated email belongs to <code>example.com<\/code> are allowed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pomerium&#8217;s important rule is:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">A request needs an applicable allow, while a matching deny takes precedence. (<a href=\"https:\/\/www.pomerium.com\/docs\/internals\/ppl?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">18. Policy Example: Any Authenticated User<\/h1>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">policy:\n  - allow:\n      and:\n        - authenticated_user: <span class=\"hljs-literal\">true<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Use this for relatively low-risk internal applications where simply belonging to your IdP is sufficient.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">19. Policy Example: Corporate Domain<\/h1>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">policy<\/span>:\n  <span class=\"hljs-selector-tag\">-<\/span> <span class=\"hljs-selector-tag\">allow<\/span>:\n      <span class=\"hljs-selector-tag\">and<\/span>:\n        <span class=\"hljs-selector-tag\">-<\/span> <span class=\"hljs-selector-tag\">domain<\/span>:\n            <span class=\"hljs-selector-tag\">is<\/span>: <span class=\"hljs-selector-tag\">example<\/span><span class=\"hljs-selector-class\">.com<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Good for company-wide applications.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">20. Policy Example: Specific Users<\/h1>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">policy<\/span>:\n  <span class=\"hljs-selector-tag\">-<\/span> <span class=\"hljs-selector-tag\">allow<\/span>:\n      <span class=\"hljs-selector-tag\">or<\/span>:\n        <span class=\"hljs-selector-tag\">-<\/span> <span class=\"hljs-selector-tag\">email<\/span>:\n            <span class=\"hljs-selector-tag\">is<\/span>: <span class=\"hljs-selector-tag\">alice<\/span><span class=\"hljs-keyword\">@example<\/span>.com\n\n        - <span class=\"hljs-attribute\">email:<\/span>\n            <span class=\"hljs-attribute\">is:<\/span> bob@example.com<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Useful for highly restricted services.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">21. Policy Example: Group Claim<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">If your IdP supplies an appropriate claim:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">policy:\n  - allow:\n      and:\n        - claim\/groups: engineering<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">This uses the group claim already present in the authenticated token.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Do not confuse this with Pomerium Enterprise&#8217;s dedicated <code>groups<\/code> criterion, which uses directory synchronization. Core can authorize against claims supplied by the IdP via <code>claim\/...<\/code>. (<a href=\"https:\/\/www.pomerium.com\/docs\/internals\/ppl?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">22. Combined Policy<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">A more realistic policy:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">policy:\n  - allow:\n      and:\n        - domain:\n            is: example.com\n\n        - claim\/groups: sre<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Meaning:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">email domain = example.com\n        AND\ngroup claim contains\/matches sre<\/code><\/span><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">23. Explicit Deny<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">You can also implement guardrails:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">policy<\/span>:\n  <span class=\"hljs-selector-tag\">-<\/span> <span class=\"hljs-selector-tag\">allow<\/span>:\n      <span class=\"hljs-selector-tag\">and<\/span>:\n        <span class=\"hljs-selector-tag\">-<\/span> <span class=\"hljs-selector-tag\">domain<\/span>:\n            <span class=\"hljs-selector-tag\">is<\/span>: <span class=\"hljs-selector-tag\">example<\/span><span class=\"hljs-selector-class\">.com<\/span>\n\n  <span class=\"hljs-selector-tag\">-<\/span> <span class=\"hljs-selector-tag\">deny<\/span>:\n      <span class=\"hljs-selector-tag\">or<\/span>:\n        <span class=\"hljs-selector-tag\">-<\/span> <span class=\"hljs-selector-tag\">email<\/span>:\n            <span class=\"hljs-selector-tag\">is<\/span>: <span class=\"hljs-selector-tag\">suspended-user<\/span><span class=\"hljs-keyword\">@example<\/span>.com<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The deny wins over an allow. (<a href=\"https:\/\/www.pomerium.com\/docs\/internals\/ppl?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">24. Recommended DNS Architecture<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">A clean deployment might use:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">access<\/span><span class=\"hljs-selector-class\">.example<\/span><span class=\"hljs-selector-class\">.com<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">with services beneath it:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">authenticate<\/span><span class=\"hljs-selector-class\">.access<\/span><span class=\"hljs-selector-class\">.example<\/span><span class=\"hljs-selector-class\">.com<\/span>\n<span class=\"hljs-selector-tag\">grafana<\/span><span class=\"hljs-selector-class\">.access<\/span><span class=\"hljs-selector-class\">.example<\/span><span class=\"hljs-selector-class\">.com<\/span>\n<span class=\"hljs-selector-tag\">argocd<\/span><span class=\"hljs-selector-class\">.access<\/span><span class=\"hljs-selector-class\">.example<\/span><span class=\"hljs-selector-class\">.com<\/span>\n<span class=\"hljs-selector-tag\">jenkins<\/span><span class=\"hljs-selector-class\">.access<\/span><span class=\"hljs-selector-class\">.example<\/span><span class=\"hljs-selector-class\">.com<\/span>\n<span class=\"hljs-selector-tag\">admin<\/span><span class=\"hljs-selector-class\">.access<\/span><span class=\"hljs-selector-class\">.example<\/span><span class=\"hljs-selector-class\">.com<\/span>\n<span class=\"hljs-selector-tag\">ssh<\/span><span class=\"hljs-selector-class\">.access<\/span><span class=\"hljs-selector-class\">.example<\/span><span class=\"hljs-selector-class\">.com<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">DNS points the protected route hostnames toward Pomerium:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\">*<span class=\"hljs-selector-class\">.access<\/span><span class=\"hljs-selector-class\">.example<\/span><span class=\"hljs-selector-class\">.com<\/span>\n          \u2502\n          \u25bc\n      <span class=\"hljs-selector-tag\">Load<\/span> <span class=\"hljs-selector-tag\">Balancer<\/span>\n          \u2502\n          \u25bc\n       <span class=\"hljs-selector-tag\">Pomerium<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The applications themselves can remain private.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">25. Recommended Network Architecture<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">For a production cloud environment:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code>                    Internet\n                         \u2502\n                         \u25bc\n                 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n                 \u2502   L4 Load     \u2502\n                 \u2502   Balancer    \u2502\n                 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n                         \u2502\n               \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n               \u25bc                   \u25bc\n        Pomerium AZ-A        Pomerium AZ-B\n               \u2502                   \u2502\n               \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n                         \u2502\n                    PostgreSQL\n                         \u2502\n            \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n            \u25bc            \u25bc             \u25bc\n         Grafana      Argo CD       Internal API<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Pomerium recommends multiple replicas for production and recommends Layer-4 load balancing in front of Proxy. Multiple all-in-one or Databroker replicas require shared\/appropriate external storage; PostgreSQL is the conventional production choice. (<a href=\"https:\/\/www.pomerium.com\/docs\/internals\/configuration?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">26. Installation Method Choices<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Pomerium Core can be installed using:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Method<\/th><th>Best use<\/th><\/tr><\/thead><tbody><tr><td>Docker<\/td><td>Testing \/ simple servers<\/td><\/tr><tr><td>Docker Compose<\/td><td>Labs \/ smaller deployments<\/td><\/tr><tr><td>Linux package<\/td><td>VM production<\/td><\/tr><tr><td>Standalone binary<\/td><td>Controlled VM deployments<\/td><\/tr><tr><td>Kubernetes Ingress Controller<\/td><td>Kubernetes<\/td><\/tr><tr><td>Source build<\/td><td>Development<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Official Linux packages are available in DEB\/RPM forms, and official container images and binaries are published as well. (<a href=\"https:\/\/www.pomerium.com\/docs\/deploy\/core?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For learning Pomerium, <strong>Docker Compose is the easiest starting point<\/strong>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">27. End-to-End Lab<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">We will build:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Browser\n   \u2502\n   \u25bc\nPomerium\n   \u2502\n   \u25bc\nVerify Application<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Use a real domain in production.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-20\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">authenticate<\/span><span class=\"hljs-selector-class\">.access<\/span><span class=\"hljs-selector-class\">.example<\/span><span class=\"hljs-selector-class\">.com<\/span>\n<span class=\"hljs-selector-tag\">verify<\/span><span class=\"hljs-selector-class\">.access<\/span><span class=\"hljs-selector-class\">.example<\/span><span class=\"hljs-selector-class\">.com<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">28. Prerequisites<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">You need:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Linux VM\/server\nDocker\nDocker Compose\nDomain you control\nDNS access\nTCP 443 reachable\nTCP 80 reachable for ACME when required<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Pomerium&#8217;s Core quickstart uses a public server, controllable DNS and ports 80\/443 for the Autocert-based scenario. (<a href=\"https:\/\/www.pomerium.com\/docs\/get-started\/quickstart?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">29. Create the Project<\/h1>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">mkdir pomerium\ncd pomerium<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Our directory becomes:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pomerium\/\n\u251c\u2500\u2500 config.yaml\n\u2514\u2500\u2500 docker-compose.yaml<\/code><\/span><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">30. Generate Secrets<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Generate a Pomerium shared secret:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">head -c32 \/dev\/urandom | base64<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Generate another independent secret for cookies:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">head -c32 \/dev\/urandom | base64<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The shared secret is used for mutual authentication between Pomerium services. Cookie secrets protect the Pomerium session cookie. (<a href=\"https:\/\/www.pomerium.com\/docs\/reference\/shared-secret?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Do not reuse the examples from documentation.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">31. Initial <code>config.yaml<\/code><\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">For an initial test:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-21\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">address: <span class=\"hljs-string\">\":443\"<\/span>\n\n<span class=\"hljs-attr\">autocert<\/span>: <span class=\"hljs-literal\">true<\/span>\n\n<span class=\"hljs-attr\">databroker_storage_type<\/span>: file\n<span class=\"hljs-attr\">databroker_storage_connection_string<\/span>: file:<span class=\"hljs-comment\">\/\/\/data\/databroker<\/span>\n\nroutes:\n  - <span class=\"hljs-keyword\">from<\/span>: https:<span class=\"hljs-comment\">\/\/verify.access.example.com<\/span>\n    to: http:<span class=\"hljs-comment\">\/\/verify:8000<\/span>\n\n    policy:\n      - allow:\n          and:\n            - email:\n                is: your.email@example.com<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-21\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">For the quickest trial, Pomerium currently provides a Hosted Authenticate Service, meaning you can test Core before configuring your own IdP. (<a href=\"https:\/\/www.pomerium.com\/docs\/get-started\/quickstart?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">32. Docker Compose<\/h1>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-22\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">services:\n\n  pomerium:\n    image: pomerium\/pomerium:v0<span class=\"hljs-number\">.33<\/span><span class=\"hljs-number\">.0<\/span>\n\n    ports:\n      - <span class=\"hljs-string\">\"80:80\"<\/span>\n      - <span class=\"hljs-string\">\"443:443\"<\/span>\n\n    volumes:\n      - .\/config.yaml:\/pomerium\/config.yaml:ro\n      - pomerium-data:\/data\n\n    environment:\n      SHARED_SECRET: <span class=\"hljs-string\">\"REPLACE_WITH_SHARED_SECRET\"<\/span>\n      COOKIE_SECRET: <span class=\"hljs-string\">\"REPLACE_WITH_COOKIE_SECRET\"<\/span>\n\n    restart: unless-stopped\n\n  verify:\n    image: pomerium\/verify:latest\n\n    expose:\n      - <span class=\"hljs-string\">\"8000\"<\/span>\n\n    restart: unless-stopped\n\nvolumes:\n  pomerium-data:<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-22\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">For a production build, pin every image to a version that you have tested rather than relying on <code>latest<\/code>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">33. Configure DNS<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Create:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-23\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">verify<\/span><span class=\"hljs-selector-class\">.access<\/span><span class=\"hljs-selector-class\">.example<\/span><span class=\"hljs-selector-class\">.com<\/span>\n        \u2502\n        \u25bc\n<span class=\"hljs-selector-tag\">Pomerium<\/span> <span class=\"hljs-selector-tag\">public<\/span> <span class=\"hljs-selector-tag\">IP<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-23\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">If you will self-host Authenticate, also create:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-24\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">authenticate<\/span><span class=\"hljs-selector-class\">.access<\/span><span class=\"hljs-selector-class\">.example<\/span><span class=\"hljs-selector-class\">.com<\/span>\n        \u2502\n        \u25bc\n<span class=\"hljs-selector-tag\">Pomerium<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-24\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">A wildcard record is convenient:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-25\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\">*<span class=\"hljs-selector-class\">.access<\/span><span class=\"hljs-selector-class\">.example<\/span><span class=\"hljs-selector-class\">.com<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-25\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">but explicit DNS records can provide tighter control.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">34. Start Pomerium<\/h1>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">docker compose up -d<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Inspect:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">docker compose ps<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">and:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">docker compose logs -f pomerium<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Now browse to:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-26\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">https:<span class=\"hljs-comment\">\/\/verify.access.example.com<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-26\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Expected workflow:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Verify URL\n   \u2193\nPomerium\n   \u2193\nAuthentication\n   \u2193\nPolicy evaluation\n   \u2193\nVerify application<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">At this point you have a working identity-aware proxy.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">35. Moving to Your Own Identity Provider<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">The hosted authentication option is excellent for learning.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For a serious fully controlled environment, integrate Pomerium with your organization&#8217;s IdP.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Create an OAuth\/OIDC client with this redirect URI pattern:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-27\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">https:<span class=\"hljs-comment\">\/\/authenticate.access.example.com\/oauth2\/callback<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-27\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Pomerium standardizes the callback path on <code>\/oauth2\/callback<\/code>. (<a href=\"https:\/\/www.pomerium.com\/docs\/integrations\/user-identity\/identity-providers?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">36. Generic OIDC Configuration<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Update <code>config.yaml<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-28\" data-shcb-language-name=\"HTTP\" data-shcb-language-slug=\"http\"><span><code class=\"hljs language-http\"><span class=\"hljs-attribute\">authenticate_service_url<\/span>: https:\/\/authenticate.access.example.com\n\nidp_provider: oidc\n\nidp_provider_url: YOUR_OIDC_ISSUER\n\nidp_scopes:\n  - openid\n  - profile\n  - email<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-28\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTTP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">http<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Provide secrets through your secret mechanism:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">IDP_CLIENT_ID\nIDP_CLIENT_SECRET<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">rather than embedding them directly in Git.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">37. Example Architecture with Keycloak<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">For Keycloak:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Browser\n   \u2502\n   \u25bc\nPomerium\n   \u2502\n   \u25bc\nKeycloak\n   \u2502\n   \u25bc\nUser authentication<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The Pomerium OIDC configuration pattern is:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-29\" data-shcb-language-name=\"HTTP\" data-shcb-language-slug=\"http\"><span><code class=\"hljs language-http\"><span class=\"hljs-attribute\">authenticate_service_url<\/span>: https:\/\/authenticate.access.example.com\n\nidp_provider: oidc\n\nidp_provider_url: YOUR_KEYCLOAK_REALM\n\nidp_client_id: YOUR_CLIENT_ID\n\nidp_client_secret: YOUR_CLIENT_SECRET<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-29\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTTP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">http<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Pomerium&#8217;s current Keycloak integration uses the standard OIDC provider mode and the same callback pattern. (<a href=\"https:\/\/www.pomerium.com\/docs\/integrations\/user-identity\/oidc?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">38. Protect Grafana<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Suppose Grafana runs privately at:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-30\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">grafana<\/span><span class=\"hljs-selector-class\">.internal<\/span><span class=\"hljs-selector-pseudo\">:3000<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-30\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Add:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-31\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">routes:\n\n  - <span class=\"hljs-keyword\">from<\/span>: https:<span class=\"hljs-comment\">\/\/grafana.access.example.com<\/span>\n    to: http:<span class=\"hljs-comment\">\/\/grafana.internal:3000<\/span>\n\n    policy:\n      - allow:\n          and:\n            - claim\/groups: sre<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-31\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Now Grafana does not need to be publicly exposed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The network becomes:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Internet\n\n   X\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25ba Grafana\n\n   \u2502\n   \u25bc\nPomerium\n   \u2502\n   \u2502 authorized only\n   \u25bc\nGrafana<\/code><\/span><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">39. Passing User Identity to the Application<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Sometimes the upstream application needs to know who Pomerium authenticated.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enable:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-32\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">pass_identity_headers: <span class=\"hljs-literal\">true<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-32\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Pomerium can send:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">X-Pomerium-Jwt-Assertion\nX-Pomerium-Claim-*<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">to upstream services. The JWT assertion provides a cryptographically signed identity artifact that the upstream can verify. (<a href=\"https:\/\/www.pomerium.com\/docs\/reference\/pass-identity-headers?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-33\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">routes:\n\n  - <span class=\"hljs-keyword\">from<\/span>: https:<span class=\"hljs-comment\">\/\/app.access.example.com<\/span>\n    to: http:<span class=\"hljs-comment\">\/\/app:8080<\/span>\n\n    pass_identity_headers: <span class=\"hljs-literal\">true<\/span>\n\n    <span class=\"hljs-attr\">policy<\/span>:\n      - allow:\n          and:\n            - authenticated_user: <span class=\"hljs-literal\">true<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-33\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">40. Important Identity-Header Security Rule<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Do not blindly trust:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">X-Email\nX-Username\nX-Pomerium-Claim-*<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">merely because they exist.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A malicious client could potentially send fake headers if the application remains reachable through another path.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The stronger pattern is:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Internet\n   \u2502\n   \u25bc\nPomerium\n   \u2502\n   \u25bc\nApplication<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">with the application firewall allowing requests <strong>only from Pomerium<\/strong>, combined with verification of <code>X-Pomerium-Jwt-Assertion<\/code> where appropriate.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">41. Signed JWT Flow<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">The design looks like:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">User\n  \u2502\n  \u25bc\nPomerium\n  \u2502\n  \u2502 signed JWT\n  \u25bc\nApplication\n  \u2502\n  \u25bc\nPomerium JWKS<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The application fetches Pomerium&#8217;s public keys from its JWKS endpoint and verifies:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">signature\nissuer\naudience\nexpiration<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">This is substantially stronger than blindly trusting an unsigned <code>X-User<\/code> header. (<a href=\"https:\/\/www.pomerium.com\/docs\/get-started\/fundamentals\/core\/jwt-verification?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">42. Pomerium Internal Discovery Endpoints<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Useful internal endpoints include:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-34\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">\/ping\n\/healthz\n\n\/.well-known\/pomerium\n\/.well-known\/pomerium\/jwks.json\n\n\/.pomerium\/\n<span class=\"hljs-regexp\">\/.pomerium\/u<\/span>ser\n\/.pomerium\/sign_out<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-34\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><code>\/ping<\/code> and <code>\/healthz<\/code> provide basic health checks, while the JWKS endpoint exposes the public keys required to verify Pomerium-issued JWTs. (<a href=\"https:\/\/www.pomerium.com\/docs\/internals\/special-routes?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">43. Protecting Multiple Applications<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">One Pomerium deployment can front many applications:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-35\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">routes:\n\n  - <span class=\"hljs-keyword\">from<\/span>: https:<span class=\"hljs-comment\">\/\/grafana.access.example.com<\/span>\n    to: http:<span class=\"hljs-comment\">\/\/grafana:3000<\/span>\n\n    policy:\n      - allow:\n          and:\n            - claim\/groups: sre\n\n\n  - <span class=\"hljs-keyword\">from<\/span>: https:<span class=\"hljs-comment\">\/\/argocd.access.example.com<\/span>\n    to: http:<span class=\"hljs-comment\">\/\/argocd:8080<\/span>\n\n    policy:\n      - allow:\n          and:\n            - claim\/groups: platform\n\n\n  - <span class=\"hljs-keyword\">from<\/span>: https:<span class=\"hljs-comment\">\/\/admin.access.example.com<\/span>\n    to: http:<span class=\"hljs-comment\">\/\/admin:9000<\/span>\n\n    policy:\n      - allow:\n          or:\n            - email:\n                is: security-admin@example.com<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-35\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">That is one of Pomerium&#8217;s strongest design properties: centralized access enforcement without requiring each application to implement the same OAuth integration.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">44. TCP Applications<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Pomerium is not limited to HTTP.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For TCP services it uses an authenticated tunnel through <code>pomerium-cli<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-36\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">routes:\n\n  - <span class=\"hljs-keyword\">from<\/span>: tcp+https:<span class=\"hljs-comment\">\/\/redis.access.example.com:6379<\/span>\n    to: tcp:<span class=\"hljs-comment\">\/\/redis.internal:6379<\/span>\n\n    policy:\n      - allow:\n          and:\n            - claim\/groups: platform<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-36\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">A user then connects with Pomerium CLI.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Conceptually:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Redis Client\n    \u2502\nlocalhost\n    \u2502\n    \u25bc\npomerium-cli\n    \u2502\n HTTPS tunnel\n    \u25bc\nPomerium\n    \u2502\n    \u25bc\nRedis<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Pomerium supports this approach for protocols such as SSH, databases and Redis, and also supports UDP-over-HTTP patterns. (<a href=\"https:\/\/www.pomerium.com\/docs\/deploy\/clients?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">45. SSH<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">There are two important SSH models.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Model A \u2014 TCP Tunnel<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">SSH Client\n   \u2502\n   \u25bc\npomerium-cli\n   \u2502\n   \u25bc\nPomerium\n   \u2502\n   \u25bc\nSSH Server<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">This is convenient because the SSH server requires relatively little modification.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Model B \u2014 Native SSH<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Modern Pomerium releases also support Native SSH access with OAuth-based login and ephemeral certificates.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This produces a better long-term production UX but requires configuring SSH servers to trust the appropriate Pomerium CA. Native SSH was significantly expanded in the v0.32 generation. (<a href=\"https:\/\/www.pomerium.com\/docs\/capabilities\/native-ssh-access?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">46. Machine-to-Machine Access<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Zero Trust cannot stop at human users.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Examples:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">GitHub Action\nKubernetes Pod\nCI\/CD runner\nservice\nautomation process\nSPIFFE workload<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">These cannot complete browser authentication.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pomerium therefore supports bearer-token authentication.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A request might contain:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-37\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\">Authorization: Bearer <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">JWT<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-37\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Pomerium can verify JWTs from explicitly trusted issuers such as:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Kubernetes\nGitHub Actions OIDC\nSPIFFE\nOIDC-compatible issuers<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">and then apply normal policy to the verified claims. (<a href=\"https:\/\/www.pomerium.com\/docs\/reference\/bearer-token-format?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-38\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">identity_providers:\n\n  ci:\n    issuer: YOUR_CI_ISSUER\n\n    <span class=\"hljs-attr\">audiences<\/span>:\n      - internal-api\n\n\n<span class=\"hljs-attr\">routes<\/span>:\n\n  - <span class=\"hljs-keyword\">from<\/span>: https:<span class=\"hljs-comment\">\/\/api.access.example.com<\/span>\n    to: http:<span class=\"hljs-comment\">\/\/internal-api:8080<\/span>\n\n    bearer_token_format: jwt\n    <span class=\"hljs-attr\">identity_providers<\/span>:\n      - ci\n\n    <span class=\"hljs-attr\">policy<\/span>:\n      - allow:\n          and:\n            - claim\/sub: deployment-job<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-38\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">This is a very powerful pattern:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Human access\n     +\nMachine identity\n     +\nSame policy enforcement layer<\/code><\/span><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">47. TLS Architecture<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">There are two distinct TLS relationships:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Client\n  \u2502\n  \u2502 Downstream TLS\n  \u25bc\nPomerium\n  \u2502\n  \u2502 Upstream TLS\n  \u25bc\nApplication<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">For strong Zero Trust architecture, protect both.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">48. Downstream TLS<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">This protects:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">User \u2192 Pomerium<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Use publicly trusted certificates where the service is internet accessible.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pomerium can work with supplied certificates or certificate automation. (<a href=\"https:\/\/www.pomerium.com\/docs\/reference\/certificates?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">49. Upstream TLS<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">This protects:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Pomerium \u2192 Application<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Instead of:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Pomerium \u2500\u2500\u2500 HTTP \u2500\u2500\u2500\u25ba Application<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">production-sensitive environments can use:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Pomerium \u2500\u2500\u2500 HTTPS \u2500\u2500\u2500\u25ba Application<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">or even:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Pomerium \u2550\u2550\u2550 mTLS \u2550\u2550\u2550\u25ba Application<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Pomerium supports custom certificate authorities, upstream server-name validation and client certificates for mutual TLS. (<a href=\"https:\/\/www.pomerium.com\/docs\/internals\/certificates-and-tls?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">50. Never Normalize <code>tls_skip_verify<\/code><\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Pomerium supports:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-39\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">tls_skip_verify: <span class=\"hljs-literal\">true<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-39\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">but official documentation explicitly warns that doing so disables certificate chain and hostname verification and exposes the connection to man-in-the-middle risk. It should be considered a troubleshooting\/test option rather than a normal production solution. (<a href=\"https:\/\/www.pomerium.com\/docs\/reference\/routes\/tls?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Instead, fix trust correctly:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Valid internal certificate\n\nor\n\nCustom internal CA\n\nor\n\nmTLS<\/code><\/span><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">51. Upstream Load Balancing<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Pomerium can route to multiple upstream servers:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-40\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">to:\n\n  - http:<span class=\"hljs-comment\">\/\/app-1:8080<\/span>\n  - http:<span class=\"hljs-comment\">\/\/app-2:8080<\/span>\n  - http:<span class=\"hljs-comment\">\/\/app-3:8080<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-40\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Supported load-balancing selections include strategies such as:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">ROUND_ROBIN\nLEAST_REQUEST\nRANDOM\nRING_HASH\nMAGLEV<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">It also supports HTTP, TCP and gRPC health checks so unhealthy upstream instances can be removed from service. (<a href=\"https:\/\/www.pomerium.com\/docs\/reference\/routes\/load-balancing?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">52. Example Upstream Health Check<\/h1>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-41\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">routes:\n\n  - from: https:<span class=\"hljs-comment\">\/\/app.access.example.com<\/span>\n\n    to:\n      - http:<span class=\"hljs-comment\">\/\/app-1:8080<\/span>\n      - http:<span class=\"hljs-comment\">\/\/app-2:8080<\/span>\n\n    health_checks:\n\n      - timeout: <span class=\"hljs-number\">3<\/span>s\n        interval: <span class=\"hljs-number\">10<\/span>s\n        healthy_threshold: <span class=\"hljs-number\">2<\/span>\n        unhealthy_threshold: <span class=\"hljs-number\">3<\/span>\n\n        http_health_check:\n          path: \/health\n\n    policy:\n\n      - allow:\n          <span class=\"hljs-keyword\">and<\/span>:\n            - authenticated_user: <span class=\"hljs-keyword\">true<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-41\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Now Pomerium provides both:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">identity-aware authorization\n+\nupstream availability checking<\/code><\/span><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">53. Pomerium on Kubernetes<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Pomerium has an official Kubernetes Ingress Controller.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of maintaining routes exclusively in one central <code>config.yaml<\/code>, Kubernetes applications can define routes using Kubernetes resources. (<a href=\"https:\/\/www.pomerium.com\/docs\/deploy\/k8s\/ingress?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Architecture:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Internet\n   \u2502\n   \u25bc\nCloud Load Balancer\n   \u2502\n   \u25bc\nPomerium Ingress Controller\n   \u2502\n   \u251c\u2500\u2500\u2500\u2500\u25ba Service A\n   \u2502\n   \u251c\u2500\u2500\u2500\u2500\u25ba Service B\n   \u2502\n   \u2514\u2500\u2500\u2500\u2500\u25ba Service C<\/code><\/span><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">54. Kubernetes Installation<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">For the current v0.33 controller generation:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-42\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">kubectl apply -k \\\n  github.com\/pomerium\/ingress-controller\/config\/<span class=\"hljs-keyword\">default<\/span>?ref=<span class=\"hljs-number\">0<\/span><span class=\"hljs-number\">-33<\/span><span class=\"hljs-number\">-0<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-42\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The standard installation creates the Pomerium namespace, controller\/Core deployment, Proxy LoadBalancer service, metrics service, bootstrap secret generation, CRDs, RBAC and the <code>pomerium<\/code> IngressClass. (<a href=\"https:\/\/www.pomerium.com\/docs\/deploy\/k8s\/install?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A great production practice from Pomerium&#8217;s own documentation is:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Pin the installation reference to a known version rather than tracking <code>main<\/code>. (<a href=\"https:\/\/www.pomerium.com\/docs\/deploy\/k8s\/install?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">55. Kubernetes Application Example<\/h1>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-43\" data-shcb-language-name=\"HTTP\" data-shcb-language-slug=\"http\"><span><code class=\"hljs language-http\"><span class=\"hljs-attribute\">apiVersion<\/span>: networking.k8s.io\/v1\n\nkind: Ingress\n\nmetadata:\n\n  name: grafana\n\n  annotations:\n\n    ingress.pomerium.io\/policy: |\n      allow:\n        and:\n          - claim\/groups: sre\n\n    ingress.pomerium.io\/pass_identity_headers: \"true\"\n\nspec:\n\n  ingressClassName: pomerium\n\n  rules:\n\n    - host: grafana.example.com\n\n      http:\n\n        paths:\n\n          - path: \/\n            pathType: Prefix\n\n            backend:\n\n              service:\n\n                name: grafana\n\n                port:\n\n                  number: 3000<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-43\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTTP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">http<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Now application access policy lives next to the application&#8217;s Kubernetes configuration.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That makes GitOps possible:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Developer\n   \u2502\n   \u25bc\nPull Request\n   \u2502\n   \u25bc\nReview policy\n   \u2502\n   \u25bc\nMerge\n   \u2502\n   \u25bc\nKubernetes\n   \u2502\n   \u25bc\nPomerium route automatically updated<\/code><\/span><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">56. Gateway API<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Pomerium also supports Kubernetes Gateway API integration.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">However, as of the current v0.33 documentation, Gateway API support remains described as <strong>experimental\/partial<\/strong>, so I would not automatically choose it over the mature Ingress Controller for a critical production environment without validating the required features. (<a href=\"https:\/\/www.pomerium.com\/docs\/deploy\/k8s\/gateway-api?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">57. High Availability<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">A production deployment should avoid:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Internet\n   \u2502\n   \u25bc\nOne Pomerium VM<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">because the identity gateway itself becomes the single point of failure.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Instead:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code>                 Load Balancer\n                       \u2502\n             \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n             \u25bc                   \u25bc\n        Pomerium A          Pomerium B\n          AZ-A                AZ-B\n             \u2502                   \u2502\n             \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n                       \u25bc\n                  PostgreSQL<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Pomerium recommends multiple instances separated across failure domains for resilient deployments. (<a href=\"https:\/\/www.pomerium.com\/docs\/internals\/configuration?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">58. Storage for Production<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">The useful rule is simple:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Single lab\n   \u2193\nMemory\n\nSingle persistent instance\n   \u2193\nFile\n\nMultiple production replicas\n   \u2193\nPostgreSQL<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-44\" data-shcb-language-name=\"HTTP\" data-shcb-language-slug=\"http\"><span><code class=\"hljs language-http\"><span class=\"hljs-attribute\">databroker_storage_type<\/span>: postgres\n\ndatabroker_storage_connection_string: YOUR_POSTGRES_CONNECTION<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-44\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTTP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">http<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">PostgreSQL provides persistence and support for multiple Pomerium replicas. Pomerium recommends a dedicated database and recommends encryption at rest because Databroker record values are not themselves encrypted by the application storage layer. (<a href=\"https:\/\/www.pomerium.com\/docs\/internals\/data-storage?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">59. All-in-One vs Split Services<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Pomerium can run:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">All-in-One<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">where Proxy, Authenticate, Authorize and Databroker run together.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Or:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-45\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-built_in\">Proxy<\/span> cluster\nAuthenticate cluster\nAuthorize cluster\nDatabroker cluster<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-45\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Pomerium&#8217;s current guidance is interesting: <strong>all-in-one is desirable for most deployments<\/strong> because it lowers complexity and resource footprint. Split-service architecture makes sense for specialized security constraints, very large environments or cases requiring independent scaling\/blast-radius boundaries. (<a href=\"https:\/\/www.pomerium.com\/docs\/internals\/configuration?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Do not split services just because the diagram has four boxes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Complexity is not automatically architecture maturity.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">60. Production Load Balancer Choice<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">For Pomerium Proxy, the recommended architecture is:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Layer 4 \/ TCP Load Balancer<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">rather than inserting another generic Layer-7 reverse proxy in front of it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This becomes particularly important when using TCP tunneling because Pomerium uses CONNECT semantics and expects the connection path to preserve them. (<a href=\"https:\/\/www.pomerium.com\/docs\/internals\/configuration?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">61. Monitoring Pomerium<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">A Zero Trust gateway should itself be heavily monitored.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pomerium exposes Prometheus-compatible metrics covering areas such as:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">HTTP traffic\nauthorization\nstorage\nDatabroker\ndatabase connection pools\ncache behavior\nEnvoy<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Pomerium also supports Prometheus scraping in Kubernetes. (<a href=\"https:\/\/www.pomerium.com\/docs\/reference\/metrics?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Architecture:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Pomerium\n   \u2502\n   \u25bc\nPrometheus\n   \u2502\n   \u25bc\nGrafana<\/code><\/span><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">62. OpenTelemetry<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Current Pomerium versions use OpenTelemetry for tracing rather than the older OpenCensus tracing path. (<a href=\"https:\/\/www.pomerium.com\/docs\/deploy\/upgrading?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A mature environment can therefore implement:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Pomerium\n \u2502\n \u251c\u2500\u2500 Logs\n \u251c\u2500\u2500 Metrics\n \u2514\u2500\u2500 Traces\n        \u2502\n        \u25bc\n OpenTelemetry Collector\n        \u2502\n        \u25bc\nObservability Backend<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">That becomes extremely useful when investigating authentication latency, upstream latency or unexpected authorization behavior.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">63. Access Logging<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Pomerium logs structured information for proxied HTTP requests.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Useful fields can include:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">method\nauthority\npath\nresponse-code\nduration\nrequest-id\nuser-agent\nforwarded-for\nupstream-cluster<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Pomerium warns that logging query parameters can expose sensitive data, so enabling query logging should be done deliberately. (<a href=\"https:\/\/www.pomerium.com\/docs\/reference\/access-log-fields?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For security teams, these logs are especially valuable because they provide an identity-aware record of access activity.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">64. Monitor Pomerium Externally Too<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Do not monitor Pomerium only from inside the same cluster.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use synthetic monitoring against:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-46\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">\/healthz\n\/ping\ncritical <span class=\"hljs-keyword\">protected<\/span> route\nactual login journey where practical<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-46\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">A good design:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">External Monitor\n      \u2502\n      \u25bc\n   Pomerium\n      \u2502\n      \u25bc\nCritical Application<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">This detects the exact failure your users would experience.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">65. Production Security Best Practices<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">A strong Pomerium deployment should follow these principles:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Default deny.<\/strong> Create explicit access rules rather than broad global allowances.<\/li>\n\n\n\n<li><strong>Use your real IdP.<\/strong> Hosted authentication is excellent for learning, but serious environments should integrate with their organizational identity source.<\/li>\n\n\n\n<li><strong>Enable MFA at the IdP.<\/strong> Pomerium benefits from stronger authentication upstream.<\/li>\n\n\n\n<li><strong>Keep applications private.<\/strong> If the application remains directly internet-accessible, users may simply bypass Pomerium.<\/li>\n\n\n\n<li><strong>Use signed identity assertions<\/strong> when applications rely on Pomerium identity.<\/li>\n\n\n\n<li><strong>Never broadly enable <code>tls_skip_verify<\/code>.<\/strong><\/li>\n\n\n\n<li><strong>Use TLS or mTLS upstream<\/strong> for sensitive systems.<\/li>\n\n\n\n<li><strong>Store secrets outside Git.<\/strong> Use Vault, AWS Secrets Manager, Kubernetes Secrets + External Secrets, cloud secret stores or equivalent.<\/li>\n\n\n\n<li><strong>Use PostgreSQL for multi-replica production deployments.<\/strong><\/li>\n\n\n\n<li><strong>Deploy multiple Pomerium replicas across failure domains.<\/strong><\/li>\n\n\n\n<li><strong>Use L4 load balancing<\/strong> where possible, especially if non-HTTP tunnels are required.<\/li>\n\n\n\n<li><strong>Pin Pomerium versions<\/strong> rather than using floating container or Kubernetes references.<\/li>\n\n\n\n<li><strong>Monitor metrics, logs and health endpoints.<\/strong><\/li>\n\n\n\n<li><strong>Encrypt Databroker persistence at rest.<\/strong><\/li>\n\n\n\n<li><strong>Regularly review policies.<\/strong> Access rules tend to accumulate just like firewall rules.<\/li>\n\n\n\n<li><strong>Restrict Pomerium&#8217;s network access.<\/strong> It should reach only the upstream systems, IdPs, databases and infrastructure required for its operation.<\/li>\n\n\n\n<li><strong>Protect administrative\/debug interfaces.<\/strong> Never casually expose Envoy&#8217;s administration endpoint publicly.<\/li>\n\n\n\n<li><strong>Test upgrades in staging.<\/strong> Pomerium publishes upgrade notes and security fixes; review them before production rollout. (<a href=\"https:\/\/www.pomerium.com\/docs\/deploy\/upgrading?utm_source=chatgpt.com\">Pomerium<\/a>)<\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">66. Common Troubleshooting Flow<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">When Pomerium does not work, debug it in layers.<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">DNS\n \u2193\nTLS\n \u2193\nPomerium listener\n \u2193\nAuthentication\n \u2193\nSession\n \u2193\nAuthorization\n \u2193\nRoute matching\n \u2193\nUpstream DNS\n \u2193\nUpstream TLS\n \u2193\nApplication<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Do not immediately assume every 403 or 502 is an identity problem.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">67. Common Errors<\/h1>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Symptom<\/th><th>Likely area<\/th><\/tr><\/thead><tbody><tr><td>Domain doesn&#8217;t resolve<\/td><td>DNS<\/td><\/tr><tr><td>Certificate warning<\/td><td>TLS\/certificate<\/td><\/tr><tr><td>Endless authentication loop<\/td><td>Cookie\/IdP\/callback<\/td><\/tr><tr><td>OAuth callback error<\/td><td>IdP redirect URI<\/td><\/tr><tr><td><code>401<\/code><\/td><td>Authentication\/bearer token<\/td><\/tr><tr><td><code>403<\/code><\/td><td>Policy authorization<\/td><\/tr><tr><td><code>502\/503<\/code><\/td><td>Upstream connectivity<\/td><\/tr><tr><td>x509 error<\/td><td>Upstream certificate\/CA<\/td><\/tr><tr><td>Sessions vanish after restart<\/td><td>Nonpersistent Databroker<\/td><\/tr><tr><td>Different replicas behave inconsistently<\/td><td>Storage\/shared-secret issue<\/td><\/tr><tr><td>TCP tunnel fails<\/td><td>L7 proxy\/WAF in front<\/td><\/tr><tr><td>Group policy never matches<\/td><td>IdP claim not present<\/td><\/tr><tr><td>App sees no identity<\/td><td><code>pass_identity_headers<\/code> disabled<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">For Kubernetes:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">kubectl describe pomerium<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">and:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-47\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\">kubectl describe ingress <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">name<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-47\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">are particularly useful because the controller publishes status\/events to Kubernetes resources. (<a href=\"https:\/\/www.pomerium.com\/docs\/deploy\/k8s\/reference?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">68. A Gold-Standard Production Architecture<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">For a company running Kubernetes across AWS, this is the type of design I would aim for:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code>                        USERS\n                           \u2502\n                           \u2502 HTTPS\n                           \u25bc\n                 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n                 \u2502 Route53 \/ DNS    \u2502\n                 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n                          \u2502\n                          \u25bc\n                 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n                 \u2502 AWS NLB          \u2502\n                 \u2502 Layer 4          \u2502\n                 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n                          \u2502\n              \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n              \u2502                       \u2502\n              \u25bc                       \u25bc\n      Pomerium Replica A      Pomerium Replica B\n           AZ-A                    AZ-C\n              \u2502                       \u2502\n              \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n                          \u2502\n                     PostgreSQL\n                          \u2502\n         \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n         \u25bc                \u25bc                \u25bc\n      Grafana          Argo CD         Admin API\n       private          private          private\n\n                          \u25b2\n                          \u2502\n                  Authentication\n                          \u2502\n                          \u25bc\n                  Enterprise IdP\n               Entra \/ Google \/ Okta<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Add:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Prometheus\nOpenTelemetry\ncentral logs\nsynthetic health checks\nsecrets manager\ncertificate management<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">around that architecture.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The important property is:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>The upstream applications do not need to trust the internet. They trust the controlled Pomerium path.<\/strong><\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">69. Gold-Standard Kubernetes Pattern<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">For Kubernetes:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-48\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">Git Repository\n     \u2502\n     \u25bc\nIngress + Pomerium Policy\n     \u2502\n     \u25bc\nArgo CD \/ Flux\n     \u2502\n     \u25bc\nKubernetes API\n     \u2502\n     \u25bc\nPomerium Ingress Controller\n     \u2502\n     \u25bc\n<span class=\"hljs-keyword\">Protected<\/span> Service<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-48\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">This gives you something traditional VPN access rarely provides:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Access change\n   =\nPull request\n   =\nCode review\n   =\nAudit trail\n   =\nGit history<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">That is a major operational benefit.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">70. Recommended Adoption Strategy<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Do not start by replacing every VPN use case on day one.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A safer migration looks like:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Phase 1\nLow-risk internal websites\nGrafana \/ dashboards\n\n          \u2193\n\nPhase 2\nAdmin applications\nArgo CD \/ Jenkins\n\n          \u2193\n\nPhase 3\nInternal APIs\nMachine-to-machine access\n\n          \u2193\n\nPhase 4\nSSH and databases\n\n          \u2193\n\nPhase 5\nReduce VPN dependency<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">This gives the team time to establish:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">IdP integration\npolicy conventions\nlogging\nmonitoring\nHA\nincident procedures\naccess reviews<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">before Pomerium becomes business-critical infrastructure.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">71. Suggested Policy Model<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Avoid hundreds of policies based on named individuals.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Prefer:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Identity Provider\n      \u2502\n      \u25bc\nRole \/ group claims\n      \u2502\n      \u25bc\nPomerium policies\n      \u2502\n      \u25bc\nApplications<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">employees\ndevelopers\nsre\nsecurity\nfinance\nproduction-admin\ncontractor<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Then:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Grafana\n\u2192 sre\n\nArgo CD staging\n\u2192 developers + sre\n\nArgo CD production\n\u2192 production-admin\n\nBilling portal\n\u2192 finance\n\nSecurity tooling\n\u2192 security<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">This scales far better than individual-email allowlists.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">72. Pomerium&#8217;s Biggest Strength<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">The real power of Pomerium is not SSO.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">SSO has existed for decades.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The valuable architectural change is:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">OLD MODEL\n\nauthenticated once\n      \u2193\ntrusted network\n      \u2193\nbroad access<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">versus:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">POMERIUM MODEL\n\nauthenticate identity\n       \u2193\nrequest resource\n       \u2193\nevaluate explicit policy\n       \u2193\nauthorize request\n       \u2193\nforward only to that resource<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">That is the core Zero Trust idea.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">73. Pomerium&#8217;s Biggest Operational Risk<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Pomerium becomes a critical dependency.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If everything is protected by:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Pomerium<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">then:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Pomerium down\n      =\nInternal applications unavailable<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">So mature deployments require the same engineering discipline as any other Tier-0 access system:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">HA\nmulti-AZ\npersistent storage\nbackups\nmonitoring\nsynthetic checks\ncapacity planning\nupgrade testing\ndisaster recovery\nIdP availability\nDNS reliability\ncertificate lifecycle management<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Treat Pomerium as <strong>security infrastructure<\/strong>, not merely another reverse proxy container.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">74. Final Decision Guide<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Use <strong>Pomerium Core<\/strong> when you want:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-49\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">Open source\n<span class=\"hljs-keyword\">Self<\/span>-hosting\nConfiguration <span class=\"hljs-keyword\">as<\/span> code\nFull operational control<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-49\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Use <strong>Pomerium Zero<\/strong> when you want:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Pomerium-managed control plane\n+\nCore running in your environment\n+\nsimpler operations<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Use <strong>Pomerium Enterprise<\/strong> when you need:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-50\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-keyword\">Self<\/span>-hosted control plane\nDirectory sync\nRBAC\nNamespaces\nGovernance\nCentralized administration\nAudit\/deployment history\nEnterprise management<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-50\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Pomerium&#8217;s own current architecture follows exactly this separation between Core, Zero and Enterprise. (<a href=\"https:\/\/www.pomerium.com\/docs?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">75. The Final Mental Model<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">If you remember only one diagram, remember this:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code>                     Identity Provider\n                            \u25b2\n                            \u2502\n                       Authentication\n                            \u2502\n                            \u2502\nUSER \u2500\u2500\u2500\u2500\u2500 HTTPS \u2500\u2500\u2500\u2500\u2500\u25ba POMERIUM\n                            \u2502\n                       Authorization\n                            \u2502\n                     Policy evaluation\n                            \u2502\n                   \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n                   \u2502                 \u2502\n                 ALLOW             DENY\n                   \u2502\n                   \u25bc\n             PRIVATE SERVICE\n                   \u2502\n            Grafana \/ API \/\n          SSH \/ DB \/ Kubernetes<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Pomerium turns:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-51\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-string\">\"Are you inside my network?\"<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-51\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">into:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-52\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-string\">\"Who are you,\nwhat exactly are you trying to access,\nand does policy permit this request?\"<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-52\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">That is why Pomerium is much more interesting than simply being another reverse proxy.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It represents a shift from:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>network-centric access<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">to:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>identity-centric access<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">And when deployed correctly\u2014with a strong IdP, private upstreams, explicit policies, signed identity propagation, encrypted traffic, persistent storage, HA, observability and configuration-as-code\u2014it can become an extremely effective foundation for <strong>Zero Trust access to internal applications, Kubernetes workloads, APIs, SSH, databases and machine-to-machine services<\/strong>. (<a href=\"https:\/\/www.pomerium.com\/docs?utm_source=chatgpt.com\">Pomerium<\/a>)<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Primary technical references<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The tutorial is based mainly on Pomerium&#8217;s current Core architecture, configuration, deployment, policy, Kubernetes and security documentation. The currently published documentation identifies <strong>v0.33 as the latest stable documentation line<\/strong>. (<a href=\"https:\/\/www.pomerium.com\/docs\/versions?utm_source=chatgpt.com\">Pomerium<\/a>) Pomerium&#8217;s official repository is Apache-2.0 licensed, while the current release train includes security-maintenance updates to the underlying Envoy stack, which is another good reason to treat upgrades as part of the operational lifecycle rather than \u201cinstall once and forget.\u201d (<a href=\"https:\/\/github.com\/pomerium\/pomerium?utm_source=chatgpt.com\">GitHub<\/a>)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The tutorial below targets Pomerium Core, the open-source\/self-managed edition, using the current v0.33 stable generation as the baseline. Pomerium\u2019s documentation also has a rolling main version, so&#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_joinchat":[],"footnotes":""},"categories":[11138],"tags":[],"class_list":["post-78578","post","type-post","status-publish","format-standard","hentry","category-best-tools"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/78578","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/comments?post=78578"}],"version-history":[{"count":1,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/78578\/revisions"}],"predecessor-version":[{"id":78579,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/78578\/revisions\/78579"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media?parent=78578"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/categories?post=78578"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/tags?post=78578"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}