Compare commits

..

2 Commits

Author SHA1 Message Date
Jeff McCune
0d0dae8742 (#89) Disable project auth proxies by default
Focus on the ingress gateway auth proxy for now and see how far it gets
us.
2024-04-01 21:48:08 -07:00
Jeff McCune
61b4b5bd17 (#89) Refactor auth proxy callbacks
The ingress gateway auth proxy callback conflicts with the project stage
auth proxy callback for the same backend Host: header value.

This patch disambiguates them by the namespace the auth proxy resides
in.
2024-04-01 21:37:52 -07:00
6 changed files with 99 additions and 53 deletions

View File

@@ -1,7 +1,7 @@
package holos
// Ingress Gateway default auth proxy
let Provider = _IngressAuthProxy.authproxy.provider
let Provider = _IngressAuthProxy.AuthProxySpec.provider
let Service = _IngressAuthProxy.service
#MeshConfig: extensionProviderMap: (Provider): envoyExtAuthzHttp: service: Service

View File

@@ -9,10 +9,10 @@ import "encoding/yaml"
// The ingress gateway auth proxy is used by multiple cue instances.
// AUTHPROXY configures one oauth2-proxy deployment for each host in each stage of a project. Multiple deployments per stage are used to narrow down the cookie domain.
_IngressAuthProxy: {
Name: "authproxy"
Namespace: "istio-ingress"
service: "\(Name).\(Namespace).svc.cluster.local"
authproxy: #IngressAuthProxySpec | *#Platform.authproxy
Name: "authproxy"
Namespace: "istio-ingress"
service: "\(Name).\(Namespace).svc.cluster.local"
AuthProxySpec: #AuthProxySpec & #Platform.authproxy
Domains: [DOMAIN=string]: {name: DOMAIN}
Domains: (#Platform.org.domain): _
@@ -48,8 +48,8 @@ _IngressAuthProxy: {
id: "Holos Platform"
name: "Holos Platform"
provider: "oidc"
scope: "openid profile email groups offline_access urn:zitadel:iam:org:domain:primary:\(authproxy.orgDomain)"
clientID: authproxy.clientID
scope: "openid profile email groups offline_access urn:zitadel:iam:org:domain:primary:\(AuthProxySpec.orgDomain)"
clientID: AuthProxySpec.clientID
clientSecretFile: "/dev/null"
code_challenge_method: "S256"
loginURLParameters: [{
@@ -57,7 +57,7 @@ _IngressAuthProxy: {
name: "approval_prompt"
}]
oidcConfig: {
issuerURL: authproxy.issuer
issuerURL: AuthProxySpec.issuer
audienceClaims: ["aud"]
emailClaim: "email"
groupsClaim: "groups"
@@ -95,7 +95,7 @@ _IngressAuthProxy: {
}]
args: [
// callback url is proxy prefix + /callback
"--proxy-prefix=" + authproxy.proxyPrefix,
"--proxy-prefix=" + AuthProxySpec.proxyPrefix,
"--email-domain=*",
"--session-store-type=redis",
"--redis-connection-url=redis://\(RedisMetadata.name):6379",
@@ -155,7 +155,7 @@ _IngressAuthProxy: {
spec: hosts: ["*"]
spec: gateways: ["istio-ingress/default"]
spec: http: [{
match: [{uri: prefix: authproxy.proxyPrefix}]
match: [{uri: prefix: AuthProxySpec.proxyPrefix}]
route: [{
destination: host: Name
destination: port: number: 4180
@@ -254,10 +254,10 @@ _IngressAuthProxy: {
RequestAuthentication: (Name): #RequestAuthentication & {
metadata: Metadata & {name: Name}
spec: jwtRules: [{
audiences: ["\(authproxy.projectID)"]
audiences: ["\(AuthProxySpec.projectID)"]
forwardOriginalToken: true
fromHeaders: [{name: authproxy.idTokenHeader}]
issuer: authproxy.issuer
fromHeaders: [{name: AuthProxySpec.idTokenHeader}]
issuer: AuthProxySpec.issuer
}]
spec: selector: matchLabels: istio: "ingressgateway"
}
@@ -265,10 +265,14 @@ _IngressAuthProxy: {
metadata: Metadata & {name: "\(Name)-custom"}
spec: {
action: "CUSTOM"
provider: name: authproxy.provider
provider: name: AuthProxySpec.provider
// bypass the external authorizer when the id token is already in the request.
// the RequestAuthentication rule will verify the token.
rules: [{when: [{key: "request.headers[\(authproxy.idTokenHeader)]", notValues: ["*"]}]}]
rules: [{when: [
{key: "request.headers[\(AuthProxySpec.idTokenHeader)]", notValues: ["*"]},
// TODO: Define a way for hosts to be excluded.
{key: "request.headers[host]", notValues: [AuthProxySpec.issuerHost]},
]}]
selector: matchLabels: istio: "ingressgateway"
}
}

View File

@@ -75,24 +75,32 @@ import "encoding/yaml"
}
// Manage auth-proxy in each stage
"\(stage.slug)-authproxy": #KubernetesObjects & {
apiObjectMap: (#APIObjects & {
apiObjects: (AUTHPROXY & {stage: Stage, project: Project, servers: GatewayServers[stage.name]}).apiObjects
}).apiObjectMap
if project.features.authproxy.enabled {
"\(stage.slug)-authproxy": #KubernetesObjects & {
apiObjectMap: (#APIObjects & {
apiObjects: (AUTHPROXY & {stage: Stage, project: Project, servers: GatewayServers[stage.name]}).apiObjects
}).apiObjectMap
}
for Env in project.environments if Env.stage == stage.name {
"\(Env.slug)-authpolicy": #KubernetesObjects & {
// Manage auth policy in each env
apiObjectMap: (#APIObjects & {
apiObjects: (AUTHPOLICY & {env: Env, project: Project, servers: GatewayServers[stage.name]}).apiObjects
}).apiObjectMap
}
}
}
// Manage httpbin in each environment
for Env in project.environments if Env.stage == stage.name {
"\(Env.slug)-httpbin": #KubernetesObjects & {
let Project = project
apiObjectMap: (#APIObjects & {
apiObjects: (HTTPBIN & {env: Env, project: Project}).apiObjects
}).apiObjectMap
// Manage auth policy in each env
apiObjectMap: (#APIObjects & {
apiObjects: (AUTHPOLICY & {env: Env, project: Project, servers: GatewayServers[stage.name]}).apiObjects
}).apiObjectMap
if project.features.httpbin.enabled {
for Env in project.environments if Env.stage == stage.name {
"\(Env.slug)-httpbin": #KubernetesObjects & {
let Project = project
apiObjectMap: (#APIObjects & {
apiObjects: (HTTPBIN & {env: Env, project: Project}).apiObjects
}).apiObjectMap
}
}
}
}
@@ -194,6 +202,14 @@ let AUTHPROXY = {
let Project = project
let Stage = stage
let AuthProxySpec = #AuthProxySpec & {
namespace: stage.namespace
projectID: project.resourceId
clientID: stage.authProxyClientID
orgDomain: project.authProxyOrgDomain
provider: stage.extAuthzProviderName
}
let Metadata = {
name: Name
namespace: stage.namespace
@@ -224,15 +240,15 @@ let AUTHPROXY = {
data: "config.yaml": yaml.Marshal(AuthProxyConfig)
let AuthProxyConfig = {
injectResponseHeaders: [{
name: "x-oidc-id-token"
name: AuthProxySpec.idTokenHeader
values: [{claim: "id_token"}]
}]
providers: [{
id: "Holos Platform"
name: "Holos Platform"
provider: "oidc"
scope: "openid profile email groups offline_access urn:zitadel:iam:org:domain:primary:\(project.authProxyOrgDomain)"
clientID: stage.authProxyClientID
scope: "openid profile email groups offline_access urn:zitadel:iam:org:domain:primary:\(AuthProxySpec.orgDomain)"
clientID: AuthProxySpec.clientID
clientSecretFile: "/dev/null"
code_challenge_method: "S256"
loginURLParameters: [{
@@ -240,7 +256,7 @@ let AUTHPROXY = {
name: "approval_prompt"
}]
oidcConfig: {
issuerURL: project.authProxyIssuer
issuerURL: AuthProxySpec.issuer
audienceClaims: ["aud"]
emailClaim: "email"
groupsClaim: "groups"
@@ -285,7 +301,7 @@ let AUTHPROXY = {
}]
args: [
// callback url is proxy prefix + /callback
"--proxy-prefix=" + project.authProxyPrefix,
"--proxy-prefix=" + AuthProxySpec.proxyPrefix,
"--email-domain=*",
"--session-store-type=redis",
"--redis-connection-url=redis://\(RedisMetadata.name):6379",
@@ -345,7 +361,7 @@ let AUTHPROXY = {
spec: hosts: ["*"]
spec: gateways: ["istio-ingress/\(stage.slug)"]
spec: http: [{
match: [{uri: prefix: project.authProxyPrefix}]
match: [{uri: prefix: AuthProxySpec.proxyPrefix}]
route: [{
destination: host: Name
destination: port: number: 4180
@@ -447,6 +463,14 @@ let AUTHPOLICY = {
let stage = project.stages[env.stage]
let Env = env
let AuthProxySpec = #AuthProxySpec & {
namespace: stage.namespace
projectID: project.resourceId
clientID: stage.authProxyClientID
orgDomain: project.authProxyOrgDomain
provider: stage.extAuthzProviderName
}
let Metadata = {
name: string
namespace: env.namespace
@@ -469,16 +493,16 @@ let AUTHPOLICY = {
for host in Hosts {host.name},
for host in Hosts {host.name + ":*"},
]
let MatchLabels = {"security.holos.run/authproxy": stage.extAuthzProviderName}
let MatchLabels = {"security.holos.run/authproxy": AuthProxySpec.provider}
apiObjects: {
RequestAuthentication: (Name): #RequestAuthentication & {
metadata: Metadata & {name: Name}
spec: jwtRules: [{
audiences: [stage.authProxyClientID]
audiences: [AuthProxySpec.clientID]
forwardOriginalToken: true
fromHeaders: [{name: "x-oidc-id-token"}]
issuer: project.authProxyIssuer
fromHeaders: [{name: AuthProxySpec.idTokenHeader}]
issuer: AuthProxySpec.issuer
}]
spec: selector: matchLabels: MatchLabels
}
@@ -487,8 +511,19 @@ let AUTHPOLICY = {
spec: {
action: "CUSTOM"
// send the request to the auth proxy
provider: name: stage.extAuthzProviderName
rules: [{to: [{operation: hosts: HostList}]}]
provider: name: AuthProxySpec.provider
rules: [{
to: [{operation: hosts: HostList}]
when: [
{
key: "request.headers[\(AuthProxySpec.idTokenHeader)]"
notValues: ["*"]
},
{
key: "request.headers[host]"
notValues: [AuthProxySpec.issuerHost]
},
]}]
selector: matchLabels: MatchLabels
}
}

View File

@@ -26,8 +26,6 @@ import "strings"
}
domain: string | *#Platform.org.domain
// authProxyPrefix is the path routed to the ext auth proxy.
authProxyPrefix: string | *"/holos/oidc"
// authProxyOrgDomain is the primary org domain for zitadel.
authProxyOrgDomain: string | *#Platform.org.domain
// authProxyIssuer is the issuer url
@@ -63,6 +61,8 @@ import "strings"
// features is YAGNI maybe?
features: [Name=string]: #Feature & {name: Name}
features: authproxy: _
features: httpbin: _
}
// #Cluster defines a cluster
@@ -127,7 +127,7 @@ import "strings"
#Feature: {
name: string
description: string
enabled: *true | false
enabled: true | *false
}
#ProjectTemplate: {

View File

@@ -226,24 +226,31 @@ _apiVersion: "holos.run/v1alpha1"
name: string & ID
}
// authproxy configures the auth proxy attached to the default ingress gateway in the istio-ingress namespace.
authproxy: #IngressAuthProxySpec
authproxy: #AuthProxySpec & {
namespace: "istio-ingress"
provider: "ingressauth"
}
}
#IngressAuthProxySpec: {
#AuthProxySpec: {
// projectID is the zitadel project resource id.
projectID: number
// clientID is the zitadel application client id.
clientID: string
// namespace is the namespace
namespace: string
// provider is the istio extension provider name in the mesh config.
provider: string
// orgDomain is the zitadel organization domain for logins.
orgDomain: string | *#Platform.org.domain
// issuerHost is the Host: header value of the oidc issuer
issuerHost: string | *"login.\(#Platform.org.domain)"
// issuer is the oidc identity provider issuer url
issuer: string | *"https://login.\(#Platform.org.domain)"
issuer: string | *"https://\(issuerHost)"
// path is the oauth2-proxy --proxy-prefix value. The default callback url is the Host: value with a path of /holos/oidc/callback
proxyPrefix: string | *"/holos/oidc"
// provider is the istio extension provider name in the mesh config.
provider: "ingressauth"
proxyPrefix: string | *"/holos/authproxy/\(namespace)"
// idTokenHeader represents the header where the id token is placed
idTokenHeader: "x-oidc-id-token"
idTokenHeader: string | *"x-oidc-id-token"
}
// ManagedNamespace is a namespace to manage across all clusters in the holos platform.

View File

@@ -1 +1 @@
6
7