remove forward auth (#3628)

This commit is contained in:
Caleb Doxsey 2022-11-23 15:59:28 -07:00 committed by GitHub
parent ba07afc245
commit fa26587f19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
68 changed files with 302 additions and 5072 deletions

View file

@ -18,20 +18,12 @@ func TestAuthorization(t *testing.T) {
accessType := []string{"direct", "api"}
for _, at := range accessType {
t.Run(at, func(t *testing.T) {
var withAPI, withForwardAuth flows.AuthenticateOption
var withAPI flows.AuthenticateOption
if at == "api" {
if ClusterType == "traefik" || ClusterType == "nginx" {
t.Skip()
return
}
withAPI = flows.WithAPI()
}
if ClusterType == "nginx" {
withForwardAuth = flows.WithForwardAuth(true)
}
t.Run("public", func(t *testing.T) {
client := getClient()
@ -53,7 +45,7 @@ func TestAuthorization(t *testing.T) {
t.Run("allowed", func(t *testing.T) {
client := getClient()
res, err := flows.Authenticate(ctx, client, mustParseURL("https://httpdetails.localhost.pomerium.io/by-domain"),
withAPI, withForwardAuth, flows.WithEmail("user1@dogs.test"))
withAPI, flows.WithEmail("user1@dogs.test"))
if assert.NoError(t, err) {
assert.Equal(t, http.StatusOK, res.StatusCode, "expected OK for dogs.test")
}
@ -61,7 +53,7 @@ func TestAuthorization(t *testing.T) {
t.Run("not allowed", func(t *testing.T) {
client := getClient()
res, err := flows.Authenticate(ctx, client, mustParseURL("https://httpdetails.localhost.pomerium.io/by-domain"),
withAPI, withForwardAuth, flows.WithEmail("user1@cats.test"))
withAPI, flows.WithEmail("user1@cats.test"))
if assert.NoError(t, err) {
assertDeniedAccess(t, res, "expected Forbidden for cats.test, but got: %d", res.StatusCode)
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -48,11 +48,6 @@ func TestDashboard(t *testing.T) {
}
func TestHealth(t *testing.T) {
if ClusterType == "traefik" || ClusterType == "nginx" {
t.Skip()
return
}
ctx, clearTimeout := context.WithTimeout(context.Background(), time.Second*30)
defer clearTimeout()

View file

@ -13,16 +13,14 @@ import (
"time"
"github.com/pomerium/pomerium/integration/forms"
"github.com/pomerium/pomerium/internal/log"
"github.com/pomerium/pomerium/internal/urlutil"
)
const (
authenticateHostname = "authenticate.localhost.pomerium.io"
forwardAuthenticateHostname = "forward-authenticate.localhost.pomerium.io"
idpHostname = "mock-idp.localhost.pomerium.io"
pomeriumCallbackPath = "/.pomerium/callback/"
pomeriumAPIPath = "/.pomerium/api/v1/login"
authenticateHostname = "authenticate.localhost.pomerium.io"
idpHostname = "mock-idp.localhost.pomerium.io"
pomeriumCallbackPath = "/.pomerium/callback/"
pomeriumAPIPath = "/.pomerium/api/v1/login"
)
type authenticateConfig struct {
@ -30,7 +28,6 @@ type authenticateConfig struct {
groups []string
tokenExpiration time.Duration
apiPath string
forwardAuth bool
}
// An AuthenticateOption is an option for authentication.
@ -48,13 +45,6 @@ func getAuthenticateConfig(options ...AuthenticateOption) *authenticateConfig {
return cfg
}
// WithForwardAuth enables/disables forward auth.
func WithForwardAuth(fa bool) AuthenticateOption {
return func(cfg *authenticateConfig) {
cfg.forwardAuth = fa
}
}
// WithEmail sets the email to use.
func WithEmail(email string) AuthenticateOption {
return func(cfg *authenticateConfig) {
@ -145,7 +135,7 @@ func Authenticate(ctx context.Context, client *http.Client, url *url.URL, option
}
// (2) redirect to idp
for req.URL.Hostname() == authenticateHostname || req.URL.Hostname() == forwardAuthenticateHostname {
for req.URL.Hostname() == authenticateHostname {
res, err = client.Do(req)
if err != nil {
return nil, err
@ -201,34 +191,10 @@ func Authenticate(ctx context.Context, client *http.Client, url *url.URL, option
}
// (5) finally to callback
if !cfg.forwardAuth && req.URL.Path != pomeriumCallbackPath {
if req.URL.Path != pomeriumCallbackPath {
return nil, fmt.Errorf("expected to redirect 5 back to %s, but got %s", pomeriumCallbackPath, req.URL.String())
}
if cfg.forwardAuth {
for i := 0; ; i++ {
res, err = client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
if res.StatusCode != 302 {
break
}
originalURL := req.URL.String()
req, err = requestFromRedirectResponse(ctx, res, req)
if err != nil {
return nil, fmt.Errorf("expected redirect to %s: %w", originalHostname, err)
}
log.Info(ctx).
Int("count", i).
Str("from", originalURL).
Str("to", req.URL.String()).
Msg("forward-auth redirect")
}
return res, err
}
res, err = client.Do(req)
if err != nil {
return nil, err

View file

@ -53,11 +53,6 @@ func TestQueryStringParams(t *testing.T) {
}
func TestCORS(t *testing.T) {
if ClusterType == "traefik" || ClusterType == "nginx" {
t.Skip()
return
}
ctx := context.Background()
ctx, clearTimeout := context.WithTimeout(ctx, time.Second*30)
defer clearTimeout()
@ -97,11 +92,6 @@ func TestCORS(t *testing.T) {
}
func TestPreserveHostHeader(t *testing.T) {
if ClusterType == "traefik" || ClusterType == "nginx" {
t.Skip()
return
}
ctx := context.Background()
ctx, clearTimeout := context.WithTimeout(ctx, time.Second*30)
defer clearTimeout()
@ -159,11 +149,6 @@ func TestPreserveHostHeader(t *testing.T) {
}
func TestSetRequestHeaders(t *testing.T) {
if ClusterType == "traefik" || ClusterType == "nginx" {
t.Skip()
return
}
ctx := context.Background()
ctx, clearTimeout := context.WithTimeout(ctx, time.Second*30)
defer clearTimeout()
@ -221,11 +206,6 @@ func TestRemoveRequestHeaders(t *testing.T) {
}
func TestWebsocket(t *testing.T) {
if ClusterType == "traefik" || ClusterType == "nginx" {
t.Skip()
return
}
ctx := context.Background()
ctx, clearTimeout := context.WithTimeout(ctx, time.Second*30)
defer clearTimeout()
@ -290,11 +270,6 @@ func TestGoogleCloudRun(t *testing.T) {
}
func TestLoadBalancer(t *testing.T) {
if ClusterType == "traefik" || ClusterType == "nginx" {
t.Skip()
return
}
ctx, clearTimeout := context.WithTimeout(context.Background(), time.Minute*10)
defer clearTimeout()

View file

@ -1,184 +0,0 @@
local utils = import '../utils.libsonnet';
local Routes = (import './routes.libsonnet').Routes;
local ProxyConfig() =
|||
set $pass_access_scheme $scheme;
set $pass_server_port $server_port;
set $best_http_host $http_host;
set $pass_port $pass_server_port;
set $proxy_alternative_upstream_name "";
client_max_body_size 1m;
proxy_set_header Host $best_http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Host $best_http_host;
proxy_set_header X-Forwarded-Port $pass_port;
proxy_set_header X-Forwarded-Proto $pass_access_scheme;
proxy_set_header X-Scheme $pass_access_scheme;
proxy_set_header X-Original-Forwarded-For $http_x_forwarded_for;
proxy_set_header Proxy "";
proxy_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_buffering off;
proxy_buffer_size 4k;
proxy_buffers 4 4k;
proxy_max_temp_file_size 1024m;
proxy_request_buffering on;
proxy_http_version 1.1;
proxy_cookie_domain off;
proxy_cookie_path off;
proxy_next_upstream error timeout;
proxy_next_upstream_timeout 0;
proxy_next_upstream_tries 3;
proxy_redirect off;
|||;
local AuthenticateConfig() =
|||
server {
listen 443 ssl;
server_name authenticate.localhost.pomerium.io forward-authenticate.localhost.pomerium.io;
ssl_certificate /etc/_wildcard.localhost.pomerium.io.pem;
ssl_certificate_key /etc/_wildcard.localhost.pomerium.io-key.pem;
location / {
proxy_pass http://pomerium;
include /etc/nginx/proxy.conf;
}
}
upstream pomerium {
server pomerium;
}
|||;
local AuthzConfig() =
|||
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Forwarded-Proto "";
proxy_set_header Host forward-authenticate.localhost.pomerium.io;
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
proxy_set_header X-Original-Method $request_method;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Auth-Request-Redirect $request_uri;
proxy_buffering off;
proxy_buffer_size 256k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
proxy_request_buffering on;
proxy_http_version 1.1;
proxy_ssl_server_name on;
proxy_pass_request_headers on;
client_max_body_size 1m;
set $target http://pomerium/verify?uri=$scheme://$http_host$request_uri;
proxy_pass $target;
|||;
local RouteLocationConfig(route) =
local rule =
if std.objectHas(route, 'prefix') then '^~ ' + route.prefix
else if std.objectHas(route, 'path') then '= ' + route.path
else '/';
local to =
if std.isArray(route.to) then route.to[0]
else route.to;
|||
location %s {
proxy_pass %s;
include /etc/nginx/proxy.conf;
# If we get a 401, respond with a named location
error_page 401 = @authredirect;
# this location requires authentication
auth_request /ext_authz;
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
}
||| % [rule, to];
local DomainServerConfig(domain, routes) =
local locations = std.join('\n', std.map(function(route) RouteLocationConfig(route), routes));
|||
server {
listen 443 ssl http2;
server_name %s;
ssl_certificate /etc/_wildcard.localhost.pomerium.io.pem;
ssl_certificate_key /etc/_wildcard.localhost.pomerium.io-key.pem;
location = /ext_authz {
internal;
include /etc/nginx/authz.conf;
}
location @authredirect {
internal;
add_header Set-Cookie $auth_cookie;
return 302 https://forward-authenticate.localhost.pomerium.io/?uri=$scheme://$host$request_uri;
}
%s
}
||| % [domain, locations];
local RoutesConfig(mode, idp, dns_suffix) =
local routes = Routes(mode, idp, dns_suffix);
local domains = std.set(std.map(function(route) utils.ParseURL(route.from).host, routes));
std.join('\n', [
local routesForDomain = std.filter(function(route)
local url = utils.ParseURL(route.from);
url.host == domain && (url.scheme == 'http' || url.scheme == 'https'),
routes);
DomainServerConfig(domain, routesForDomain)
for domain in domains
]);
local WriteFile(path, contents) =
|||
cat <<-'END_OF_NGINX' | tee %s
%s
END_OF_NGINX
||| % [path, std.strReplace(contents, '$', '$$')];
local Command(mode, idp, dns_suffix) =
[
'sh',
'-c',
std.join('\n\n', [
WriteFile('/etc/nginx/conf.d/authenticate.conf', AuthenticateConfig()),
WriteFile('/etc/nginx/conf.d/routes.conf', RoutesConfig(mode, idp, dns_suffix)),
WriteFile('/etc/nginx/authz.conf', AuthzConfig()),
WriteFile('/etc/nginx/proxy.conf', ProxyConfig()),
WriteFile('/etc/_wildcard.localhost.pomerium.io.pem', importstr '../files/trusted.pem'),
WriteFile('/etc/_wildcard.localhost.pomerium.io-key.pem', importstr '../files/trusted-key.pem'),
"nginx -g 'daemon off;'",
]),
];
function(mode, idp, dns_suffix='') {
local image = 'nginx:1.21.1',
compose: {
services: utils.ComposeService('nginx', {
image: image,
depends_on: {
'pomerium-ready': {
condition: 'service_completed_successfully',
},
},
entrypoint: Command(mode, idp, dns_suffix),
ports: [
'80:80/tcp',
'443:443/tcp',
],
}, ['mock-idp.localhost.pomerium.io']),
},
}

View file

@ -103,12 +103,6 @@ local Environment(mode, idp, dns_suffix) =
DATABROKER_SERVICE_URL: 'https://pomerium-databroker:5443',
GRPC_ADDRESS: ':5443',
GRPC_INSECURE: 'false',
} else if mode == 'traefik' then {
FORWARD_AUTH_URL: 'https://forward-authenticate.localhost.pomerium.io',
} else if mode == 'nginx' then {
ADDRESS: ':80',
INSECURE_SERVER: 'true',
FORWARD_AUTH_URL: 'https://forward-authenticate.localhost.pomerium.io',
} else {};
local ComposeService(name, definition, additionalAliases=[]) =
@ -186,24 +180,6 @@ function(mode, idp, dns_suffix='') {
'9901:9901/tcp',
],
}, ['mock-idp.localhost.pomerium.io'])
else if mode == 'traefik' || mode == 'nginx' then
ComposeService(name, {
image: image,
environment: environment,
}, ['authenticate.localhost.pomerium.io', 'forward-authenticate.localhost.pomerium.io']) +
ComposeService(name + '-ready', {
image: 'powerman/dockerize:0.16.3',
command: [
'-skip-tls-verify',
'-wait',
if mode == 'nginx' then
'http://' + name + ':80/healthz'
else
'https://' + name + ':443/healthz',
'-timeout',
'10m',
],
})
else
ComposeService(name, {
image: image,

View file

@ -1,180 +0,0 @@
local utils = import '../utils.libsonnet';
local Routes = (import './routes.libsonnet').Routes;
local StaticConfig() =
{
global: {
checkNewVersion: false,
sendAnonymousUsage: false,
},
log: {
level: 'DEBUG',
},
accessLog: {},
entryPoints: {
web: {
address: ':80',
forwardedheaders: {
insecure: true,
},
},
websecure: {
address: ':443',
forwardedheaders: {
insecure: true,
},
},
},
api: {
insecure: true,
},
providers: {
file: {
filename: 'traefik-dynamic.yaml',
},
},
};
local Rule(route) =
local url = utils.ParseURL(route.from);
std.join(
' && ',
['Host(`' + url.host + '`)'] +
(if std.objectHas(route, 'prefix') then
['PathPrefix(`' + route.prefix + '`)'] else []) +
(if std.objectHas(route, 'path') then
['Path(`' + route.path + '`)'] else [])
);
local DynamicConfig(mode, idp, dns_suffix='') =
{
local routes = Routes(mode, idp, dns_suffix) + [
{
from: 'https://authenticate.localhost.pomerium.io',
to: 'https://pomerium' + dns_suffix + ':443',
allow_public_unauthenticated_access: true,
tls_skip_verify: true,
preserve_host_header: true,
},
],
tls: {
certificates: [{
certFile: '_wildcard.localhost.pomerium.io.pem',
keyFile: '_wildcard.localhost.pomerium.io-key.pem',
}],
},
http: {
serversTransports: {
insecure: {
insecureSkipVerify: true,
},
},
routers: {
['route%d' % i]: {
service: 'route%d' % i,
rule: Rule(routes[i]),
tls: {},
middlewares:
(if routes[i].from == 'https://authenticate.localhost.pomerium.io' then
[]
else
['authz']) +
(if std.objectHas(routes[i], 'set_request_headers') then
['set-request-headers-%d' % i]
else
[]),
}
for i in std.range(0, std.length(routes) - 1)
},
services: {
['route%d' % i]: {
loadBalancer:
{
servers: [{
url: routes[i].to,
}],
} +
(if std.startsWith(routes[i].to, 'https://') then
{ serversTransport: 'insecure' }
else
{}) +
(if std.objectHas(routes[i], 'preserve_host_header') && routes[i].preserve_host_header then
{ passHostHeader: true }
else
{ passHostHeader: false }),
}
for i in std.range(0, std.length(routes) - 1)
},
middlewares: {
authz: {
forwardAuth: {
address: 'https://forward-authenticate.localhost.pomerium.io',
trustForwardHeader: true,
authResponseHeaders: ['x-pomerium-jwt-assertion', 'x-pomerium-claim-email', 'authorization'],
tls: {
insecureSkipVerify: true,
},
},
},
} + {
['set-request-headers-%d' % i]: {
headers: {
customRequestHeaders: {
[k]: routes[i].set_request_headers[k]
for k in std.objectFields(routes[i].set_request_headers)
},
},
}
for i in std.range(0, std.length(routes) - 1)
if std.objectHas(routes[i], 'set_request_headers')
},
},
};
local Command(mode, idp, dns_suffix='') =
[
'sh',
'-c',
|||
cat <<-'END_OF_TRAEFIK' | tee traefik.yaml
%s
END_OF_TRAEFIK
cat <<-'END_OF_TRAEFIK' | tee traefik-dynamic.yaml
%s
END_OF_TRAEFIK
cat <<-'END_OF_TRAEFIK' | tee _wildcard.localhost.pomerium.io.pem
%s
END_OF_TRAEFIK
cat <<-'END_OF_TRAEFIK' | tee _wildcard.localhost.pomerium.io-key.pem
%s
END_OF_TRAEFIK
traefik -configFile=traefik.yaml
||| % [
std.manifestJsonEx(StaticConfig(), ' '),
std.manifestJsonEx(DynamicConfig(mode, idp, dns_suffix), ' '),
importstr '../files/trusted.pem',
importstr '../files/trusted-key.pem',
],
];
function(mode, idp, dns_suffix='') {
local image = 'traefik:latest',
compose: {
services:
utils.ComposeService('traefik', {
image: image,
depends_on: {
pomerium: {
condition: 'service_started',
},
},
command: Command(mode, idp, dns_suffix),
ports: [
'80:80/tcp',
'443:443/tcp',
],
}, ['authenticate.localhost.pomerium.io', 'mock-idp.localhost.pomerium.io']),
},
}

View file

@ -1 +0,0 @@
(import '../../deployments/nginx.libsonnet')('oidc')

View file

@ -1 +0,0 @@
(import '../../deployments/traefik.libsonnet')('oidc')

View file

@ -1,17 +0,0 @@
local utils = import '../utils.libsonnet';
function(idp) utils.Merge([
(import '../backends/fortio.libsonnet')().compose,
(import '../backends/httpdetails.libsonnet')().compose,
(import '../backends/mock-idp.libsonnet')(idp).compose,
(import '../backends/pomerium.libsonnet')('nginx', idp).compose,
(import '../backends/postgres.libsonnet')().compose,
(import '../backends/verify.libsonnet')('nginx').compose,
(import '../backends/websocket-echo.libsonnet')().compose,
(import '../backends/nginx.libsonnet')('single', idp).compose,
{
networks: {
main: {},
},
},
])

View file

@ -1,17 +0,0 @@
local utils = import '../utils.libsonnet';
function(idp) utils.Merge([
(import '../backends/fortio.libsonnet')().compose,
(import '../backends/httpdetails.libsonnet')().compose,
(import '../backends/mock-idp.libsonnet')(idp).compose,
(import '../backends/pomerium.libsonnet')('traefik', idp).compose,
(import '../backends/postgres.libsonnet')().compose,
(import '../backends/traefik.libsonnet')('single', idp).compose,
(import '../backends/verify.libsonnet')('traefik').compose,
(import '../backends/websocket-echo.libsonnet')().compose,
{
networks: {
main: {},
},
},
])