mirror of
https://github.com/holos-run/holos.git
synced 2026-03-19 08:44:58 +00:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e93fe4535 | ||
|
|
2e98df3572 | ||
|
|
3b561de413 | ||
|
|
0d0dae8742 | ||
|
|
61b4b5bd17 | ||
|
|
0060740b76 | ||
|
|
bf8a4af579 | ||
|
|
dc057fe39d | ||
|
|
9877ab131a | ||
|
|
13aba64cb7 | ||
|
|
fe9bc2dbfc | ||
|
|
c53b682852 | ||
|
|
3aca6a9e4c | ||
|
|
40fdfc0317 | ||
|
|
25d9415b0a | ||
|
|
43c8702398 | ||
|
|
ce94776dbb | ||
|
|
78ab6cd848 |
37
docs/examples/authpolicy.cue
Normal file
37
docs/examples/authpolicy.cue
Normal file
@@ -0,0 +1,37 @@
|
||||
package holos
|
||||
|
||||
import ap "security.istio.io/authorizationpolicy/v1"
|
||||
|
||||
// #AuthPolicyRules represents AuthorizationPolicy rules for hosts that need specialized treatment. Entries in this struct are exclused from the blank ingressauth AuthorizationPolicy governing the ingressgateway and included in a spcialized policy
|
||||
#AuthPolicyRules: {
|
||||
// AuthProxySpec represents the identity provider configuration
|
||||
AuthProxySpec: #AuthProxySpec & #Platform.authproxy
|
||||
|
||||
// Hosts are hosts that need specialized treatment
|
||||
hosts: {
|
||||
[Name=_]: {
|
||||
// name is the fully qualifed hostname, a Host: header value.
|
||||
name: Name
|
||||
// slug is the resource name prefix
|
||||
slug: string
|
||||
// Refer to https://istio.io/latest/docs/reference/config/security/authorization-policy/#Rule
|
||||
spec: ap.#AuthorizationPolicySpec & {
|
||||
action: "CUSTOM"
|
||||
provider: name: AuthProxySpec.provider
|
||||
selector: matchLabels: istio: "ingressgateway"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
objects: #APIObjects & {
|
||||
for Host in hosts {
|
||||
apiObjects: {
|
||||
AuthorizationPolicy: "\(Host.slug)-custom": {
|
||||
metadata: namespace: "istio-ingress"
|
||||
metadata: name: "\(Host.slug)-custom"
|
||||
spec: Host.spec
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,189 @@
|
||||
// Code generated by timoni. DO NOT EDIT.
|
||||
|
||||
//timoni:generate timoni vendor crd -f /home/jeff/workspace/holos-run/holos-infra/deploy/clusters/k2/components/prod-platform-argocd/prod-platform-argocd.gen.yaml
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import "strings"
|
||||
|
||||
// AppProject provides a logical grouping of applications,
|
||||
// providing controls for: * where the apps may deploy to
|
||||
// (cluster whitelist) * what may be deployed (repository
|
||||
// whitelist, resource whitelist/blacklist) * who can access
|
||||
// these applications (roles, OIDC group claims bindings) * and
|
||||
// what they can do (RBAC policies) * automation access to these
|
||||
// roles (JWT tokens)
|
||||
#AppProject: {
|
||||
// APIVersion defines the versioned schema of this representation
|
||||
// of an object. Servers should convert recognized schemas to the
|
||||
// latest internal value, and may reject unrecognized values.
|
||||
// More info:
|
||||
// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
apiVersion: "argoproj.io/v1alpha1"
|
||||
|
||||
// Kind is a string value representing the REST resource this
|
||||
// object represents. Servers may infer this from the endpoint
|
||||
// the client submits requests to. Cannot be updated. In
|
||||
// CamelCase. More info:
|
||||
// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
kind: "AppProject"
|
||||
metadata: {
|
||||
name!: strings.MaxRunes(253) & strings.MinRunes(1) & {
|
||||
string
|
||||
}
|
||||
namespace!: strings.MaxRunes(63) & strings.MinRunes(1) & {
|
||||
string
|
||||
}
|
||||
labels?: {
|
||||
[string]: string
|
||||
}
|
||||
annotations?: {
|
||||
[string]: string
|
||||
}
|
||||
}
|
||||
|
||||
// AppProjectSpec is the specification of an AppProject
|
||||
spec!: #AppProjectSpec
|
||||
}
|
||||
|
||||
// AppProjectSpec is the specification of an AppProject
|
||||
#AppProjectSpec: {
|
||||
// ClusterResourceBlacklist contains list of blacklisted cluster
|
||||
// level resources
|
||||
clusterResourceBlacklist?: [...{
|
||||
group: string
|
||||
kind: string
|
||||
}]
|
||||
|
||||
// ClusterResourceWhitelist contains list of whitelisted cluster
|
||||
// level resources
|
||||
clusterResourceWhitelist?: [...{
|
||||
group: string
|
||||
kind: string
|
||||
}]
|
||||
|
||||
// Description contains optional project description
|
||||
description?: string
|
||||
|
||||
// Destinations contains list of destinations available for
|
||||
// deployment
|
||||
destinations?: [...{
|
||||
// Name is an alternate way of specifying the target cluster by
|
||||
// its symbolic name. This must be set if Server is not set.
|
||||
name?: string
|
||||
|
||||
// Namespace specifies the target namespace for the application's
|
||||
// resources. The namespace will only be set for namespace-scoped
|
||||
// resources that have not set a value for .metadata.namespace
|
||||
namespace?: string
|
||||
|
||||
// Server specifies the URL of the target cluster's Kubernetes
|
||||
// control plane API. This must be set if Name is not set.
|
||||
server?: string
|
||||
}]
|
||||
|
||||
// NamespaceResourceBlacklist contains list of blacklisted
|
||||
// namespace level resources
|
||||
namespaceResourceBlacklist?: [...{
|
||||
group: string
|
||||
kind: string
|
||||
}]
|
||||
|
||||
// NamespaceResourceWhitelist contains list of whitelisted
|
||||
// namespace level resources
|
||||
namespaceResourceWhitelist?: [...{
|
||||
group: string
|
||||
kind: string
|
||||
}]
|
||||
|
||||
// OrphanedResources specifies if controller should monitor
|
||||
// orphaned resources of apps in this project
|
||||
orphanedResources?: {
|
||||
// Ignore contains a list of resources that are to be excluded
|
||||
// from orphaned resources monitoring
|
||||
ignore?: [...{
|
||||
group?: string
|
||||
kind?: string
|
||||
name?: string
|
||||
}]
|
||||
|
||||
// Warn indicates if warning condition should be created for apps
|
||||
// which have orphaned resources
|
||||
warn?: bool
|
||||
}
|
||||
|
||||
// PermitOnlyProjectScopedClusters determines whether destinations
|
||||
// can only reference clusters which are project-scoped
|
||||
permitOnlyProjectScopedClusters?: bool
|
||||
|
||||
// Roles are user defined RBAC roles associated with this project
|
||||
roles?: [...{
|
||||
// Description is a description of the role
|
||||
description?: string
|
||||
|
||||
// Groups are a list of OIDC group claims bound to this role
|
||||
groups?: [...string]
|
||||
|
||||
// JWTTokens are a list of generated JWT tokens bound to this role
|
||||
jwtTokens?: [...{
|
||||
exp?: int
|
||||
iat: int
|
||||
id?: string
|
||||
}]
|
||||
|
||||
// Name is a name for this role
|
||||
name: string
|
||||
|
||||
// Policies Stores a list of casbin formatted strings that define
|
||||
// access policies for the role in the project
|
||||
policies?: [...string]
|
||||
}]
|
||||
|
||||
// SignatureKeys contains a list of PGP key IDs that commits in
|
||||
// Git must be signed with in order to be allowed for sync
|
||||
signatureKeys?: [...{
|
||||
// The ID of the key in hexadecimal notation
|
||||
keyID: string
|
||||
}]
|
||||
|
||||
// SourceNamespaces defines the namespaces application resources
|
||||
// are allowed to be created in
|
||||
sourceNamespaces?: [...string]
|
||||
|
||||
// SourceRepos contains list of repository URLs which can be used
|
||||
// for deployment
|
||||
sourceRepos?: [...string]
|
||||
|
||||
// SyncWindows controls when syncs can be run for apps in this
|
||||
// project
|
||||
syncWindows?: [...{
|
||||
// Applications contains a list of applications that the window
|
||||
// will apply to
|
||||
applications?: [...string]
|
||||
|
||||
// Clusters contains a list of clusters that the window will apply
|
||||
// to
|
||||
clusters?: [...string]
|
||||
|
||||
// Duration is the amount of time the sync window will be open
|
||||
duration?: string
|
||||
|
||||
// Kind defines if the window allows or blocks syncs
|
||||
kind?: string
|
||||
|
||||
// ManualSync enables manual syncs when they would otherwise be
|
||||
// blocked
|
||||
manualSync?: bool
|
||||
|
||||
// Namespaces contains a list of namespaces that the window will
|
||||
// apply to
|
||||
namespaces?: [...string]
|
||||
|
||||
// Schedule is the time the window will begin, specified in cron
|
||||
// format
|
||||
schedule?: string
|
||||
|
||||
// TimeZone of the sync that will be applied to the schedule
|
||||
timeZone?: string
|
||||
}]
|
||||
}
|
||||
@@ -18,7 +18,10 @@ import "encoding/yaml"
|
||||
Issuer?: [Name=_]: #Issuer & {metadata: name: Name}
|
||||
Gateway?: [Name=_]: #Gateway & {metadata: name: Name}
|
||||
ConfigMap?: [Name=_]: #ConfigMap & {metadata: name: Name}
|
||||
Deployment?: [_]: #Deployment
|
||||
|
||||
Deployment?: [_]: #Deployment
|
||||
RequestAuthentication?: [_]: #RequestAuthentication
|
||||
AuthorizationPolicy?: [_]: #AuthorizationPolicy
|
||||
}
|
||||
|
||||
// apiObjectMap holds the marshalled representation of apiObjects
|
||||
|
||||
54
docs/examples/meshconfig.cue
Normal file
54
docs/examples/meshconfig.cue
Normal file
@@ -0,0 +1,54 @@
|
||||
package holos
|
||||
|
||||
// #MeshConfig provides the istio meshconfig in the config key given projects.
|
||||
#MeshConfig: {
|
||||
projects: #Projects
|
||||
// clusterName is the value of the --cluster-name flag, the cluster currently being manged / rendered.
|
||||
clusterName: string | *#ClusterName
|
||||
|
||||
// for extAuthzHttp extension providers
|
||||
extensionProviderMap: [Name=_]: #ExtAuthzProxy & {name: Name}
|
||||
// for other extension providers like zipkin
|
||||
extensionProviderExtraMap: [Name=_]: {name: Name}
|
||||
|
||||
config: {
|
||||
accessLogEncoding: string | *"JSON"
|
||||
accessLogFile: string | *"/dev/stdout"
|
||||
defaultConfig: {
|
||||
discoveryAddress: string | *"istiod.istio-system.svc:15012"
|
||||
tracing: zipkin: address: string | *"zipkin.istio-system:9411"
|
||||
}
|
||||
defaultProviders: metrics: [...string] | *["prometheus"]
|
||||
enablePrometheusMerge: false | *true
|
||||
rootNamespace: string | *"istio-system"
|
||||
trustDomain: string | *"cluster.local"
|
||||
extensionProviders: [
|
||||
for x in extensionProviderMap {x},
|
||||
for y in extensionProviderExtraMap {y},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// #ExtAuthzProxy defines the provider configuration for an istio external authorization auth proxy.
|
||||
#ExtAuthzProxy: {
|
||||
name: string
|
||||
envoyExtAuthzHttp: {
|
||||
headersToDownstreamOnDeny: [
|
||||
"content-type",
|
||||
"set-cookie",
|
||||
]
|
||||
headersToUpstreamOnAllow: [
|
||||
"authorization",
|
||||
"path",
|
||||
"x-oidc-id-token",
|
||||
]
|
||||
includeAdditionalHeadersInCheck: "X-Auth-Request-Redirect": "%REQ(x-forwarded-proto)%://%REQ(:authority)%%REQ(:path)%%REQ(:query)%"
|
||||
includeRequestHeadersInCheck: [
|
||||
"authorization",
|
||||
"cookie",
|
||||
"x-forwarded-for",
|
||||
]
|
||||
port: 4180
|
||||
service: string
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package holos
|
||||
|
||||
import "encoding/yaml"
|
||||
|
||||
let ArgoCD = "argocd"
|
||||
let Namespace = "prod-platform"
|
||||
|
||||
spec: components: HelmChartList: [
|
||||
#HelmChart & {
|
||||
_dependsOn: "prod-secrets-stores": _
|
||||
namespace: Namespace
|
||||
|
||||
metadata: name: "\(namespace)-\(ArgoCD)"
|
||||
|
||||
chart: {
|
||||
name: "argo-cd"
|
||||
release: "argocd"
|
||||
version: "6.7.8"
|
||||
repository: {
|
||||
name: "argocd"
|
||||
url: "https://argoproj.github.io/argo-helm"
|
||||
}
|
||||
}
|
||||
|
||||
_values: #ArgoCDValues & {
|
||||
kubeVersionOverride: "1.29.0"
|
||||
global: domain: "argocd.\(#ClusterName).\(#Platform.org.domain)"
|
||||
|
||||
configs: params: "server.insecure": true
|
||||
|
||||
configs: cm: {
|
||||
"admin.enabled": false
|
||||
"oidc.config": yaml.Marshal(OIDCConfig)
|
||||
}
|
||||
}
|
||||
|
||||
// Holos overlay objects
|
||||
apiObjectMap: OBJECTS.apiObjectMap
|
||||
},
|
||||
]
|
||||
|
||||
let OBJECTS = #APIObjects & {
|
||||
apiObjects: {
|
||||
// ExternalSecret: "deploy-key": _
|
||||
VirtualService: (ArgoCD): {
|
||||
metadata: name: ArgoCD
|
||||
metadata: namespace: Namespace
|
||||
spec: hosts: [
|
||||
ArgoCD + ".\(#Platform.org.domain)",
|
||||
ArgoCD + ".\(#ClusterName).\(#Platform.org.domain)",
|
||||
]
|
||||
spec: gateways: ["istio-ingress/\(Namespace)"]
|
||||
spec: http: [{route: [{destination: {
|
||||
host: "argocd-server.\(Namespace).svc.cluster.local"
|
||||
port: number: 80
|
||||
}}]}]
|
||||
}
|
||||
}
|
||||
}
|
||||
let IstioInject = [{op: "add", path: "/spec/template/metadata/labels/sidecar.istio.io~1inject", value: "true"}]
|
||||
|
||||
#Kustomize: _patches: {
|
||||
mesh: {
|
||||
target: {
|
||||
group: "apps"
|
||||
version: "v1"
|
||||
kind: "Deployment"
|
||||
name: "argocd-server"
|
||||
}
|
||||
patch: yaml.Marshal(IstioInject)
|
||||
}
|
||||
}
|
||||
|
||||
// Probably shouldn't use the authproxy struct and should instead define an identity provider struct.
|
||||
let AuthProxySpec = #AuthProxySpec & #Platform.authproxy
|
||||
|
||||
let OIDCConfig = {
|
||||
name: "Holos Platform"
|
||||
issuer: AuthProxySpec.issuer
|
||||
clientID: #Platform.argocd.clientID
|
||||
requestedIDTokenClaims: groups: essential: true
|
||||
requestedScopes: ["openid", "profile", "email", "groups", "urn:zitadel:iam:org:domain:primary:\(AuthProxySpec.orgDomain)"]
|
||||
enablePKCEAuthentication: true
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ spec: components: HelmChartList: [
|
||||
namespace: "istio-system"
|
||||
chart: {
|
||||
name: "base"
|
||||
version: "1.20.3"
|
||||
version: #IstioVersion
|
||||
repository: {
|
||||
name: "istio"
|
||||
url: "https://istio-release.storage.googleapis.com/charts"
|
||||
|
||||
@@ -52,6 +52,12 @@ spec: components: HelmChartList: [
|
||||
}
|
||||
}
|
||||
apiObjectMap: OBJECTS.apiObjectMap
|
||||
// Auth Proxy
|
||||
apiObjectMap: _IngressAuthProxy.Deployment.apiObjectMap
|
||||
// Auth Policy
|
||||
apiObjectMap: _IngressAuthProxy.Policy.apiObjectMap
|
||||
// Auth Policy Exclusions
|
||||
apiObjectMap: _AuthPolicyRules.objects.apiObjectMap
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package holos
|
||||
|
||||
#HelmChart: {
|
||||
chart: {
|
||||
version: "1.20.3"
|
||||
version: #IstioVersion
|
||||
repository: {
|
||||
name: "istio"
|
||||
url: "https://istio-release.storage.googleapis.com/charts"
|
||||
|
||||
@@ -1,74 +1,9 @@
|
||||
package holos
|
||||
|
||||
// Ingress Gateway default auth proxy
|
||||
let Provider = _IngressAuthProxy.AuthProxySpec.provider
|
||||
let Service = _IngressAuthProxy.service
|
||||
#MeshConfig: extensionProviderMap: (Provider): envoyExtAuthzHttp: service: Service
|
||||
|
||||
// Istio meshconfig
|
||||
// TODO: Generate per-project extauthz providers.
|
||||
_MeshConfig: {
|
||||
accessLogEncoding: "JSON"
|
||||
accessLogFile: "/dev/stdout"
|
||||
defaultConfig: {
|
||||
discoveryAddress: "istiod.istio-system.svc:15012"
|
||||
tracing: zipkin: address: "zipkin.istio-system:9411"
|
||||
}
|
||||
defaultProviders: metrics: ["prometheus"]
|
||||
enablePrometheusMerge: true
|
||||
// For PROXY PROTOCOL at the ingress gateway.
|
||||
gatewayTopology: {
|
||||
numTrustedProxies: 2
|
||||
}
|
||||
rootNamespace: "istio-system"
|
||||
trustDomain: "cluster.local"
|
||||
extensionProviders: [{
|
||||
name: "cluster-trace"
|
||||
zipkin: {
|
||||
maxTagLength: 56
|
||||
port: 9411
|
||||
service: "zipkin.istio-system.svc"
|
||||
}
|
||||
}, {
|
||||
name: "cluster-gatekeeper"
|
||||
envoyExtAuthzHttp: {
|
||||
headersToDownstreamOnDeny: [
|
||||
"content-type",
|
||||
"set-cookie",
|
||||
]
|
||||
headersToUpstreamOnAllow: [
|
||||
"authorization",
|
||||
"path",
|
||||
"x-auth-request-user",
|
||||
"x-auth-request-email",
|
||||
"x-auth-request-access-token",
|
||||
]
|
||||
includeAdditionalHeadersInCheck: "X-Auth-Request-Redirect": "%REQ(x-forwarded-proto)%://%REQ(:authority)%%REQ(:path)%%REQ(:query)%"
|
||||
includeRequestHeadersInCheck: [
|
||||
"authorization",
|
||||
"cookie",
|
||||
"x-forwarded-for",
|
||||
]
|
||||
port: 4180
|
||||
service: "oauth2-proxy.istio-ingress.svc.cluster.local"
|
||||
}
|
||||
}, {
|
||||
name: "core-authorizer"
|
||||
envoyExtAuthzHttp: {
|
||||
headersToDownstreamOnDeny: [
|
||||
"content-type",
|
||||
"set-cookie",
|
||||
]
|
||||
headersToUpstreamOnAllow: [
|
||||
"authorization",
|
||||
"path",
|
||||
"x-auth-request-user",
|
||||
"x-auth-request-email",
|
||||
"x-auth-request-access-token",
|
||||
]
|
||||
includeAdditionalHeadersInCheck: "X-Auth-Request-Redirect": "%REQ(x-forwarded-proto)%://%REQ(:authority)%%REQ(:path)%%REQ(:query)%"
|
||||
includeRequestHeadersInCheck: [
|
||||
"authorization",
|
||||
"cookie",
|
||||
"x-forwarded-for",
|
||||
]
|
||||
port: 4180
|
||||
service: "oauth2-proxy.prod-core-system.svc.cluster.local"
|
||||
}
|
||||
}]
|
||||
}
|
||||
_MeshConfig: (#MeshConfig & {projects: _Projects}).config
|
||||
|
||||
@@ -126,7 +126,7 @@ package holos
|
||||
hub: "docker.io/istio"
|
||||
|
||||
// Default tag for Istio images.
|
||||
tag: "1.20.3"
|
||||
tag: #IstioVersion
|
||||
|
||||
// Variant of the image to use.
|
||||
// Currently supported are: [debug, distroless]
|
||||
|
||||
@@ -1,3 +1,323 @@
|
||||
package holos
|
||||
|
||||
import "encoding/yaml"
|
||||
|
||||
#InstancePrefix: "prod-mesh"
|
||||
|
||||
#IstioVersion: "1.21.0"
|
||||
|
||||
// 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"
|
||||
AuthProxySpec: #AuthProxySpec & #Platform.authproxy
|
||||
|
||||
Domains: [DOMAIN=string]: {name: DOMAIN}
|
||||
Domains: (#Platform.org.domain): _
|
||||
Domains: "\(#ClusterName).\(#Platform.org.domain)": _
|
||||
|
||||
let Metadata = {
|
||||
name: string
|
||||
namespace: Namespace
|
||||
labels: "app.kubernetes.io/name": name
|
||||
labels: "app.kubernetes.io/part-of": "istio-ingressgateway"
|
||||
...
|
||||
}
|
||||
|
||||
let ProxyMetadata = Metadata & {name: Name}
|
||||
let RedisMetadata = Metadata & {name: Name + "-redis"}
|
||||
|
||||
// Deployment represents the oauth2-proxy deployment
|
||||
Deployment: #APIObjects & {
|
||||
apiObjects: {
|
||||
// oauth2-proxy
|
||||
ExternalSecret: (Name): metadata: ProxyMetadata
|
||||
// Place the ID token in a header that does not conflict with the Authorization header.
|
||||
// Refer to: https://github.com/oauth2-proxy/oauth2-proxy/issues/1877#issuecomment-1364033723
|
||||
ConfigMap: (Name): {
|
||||
metadata: ProxyMetadata
|
||||
data: "config.yaml": yaml.Marshal(AuthProxyConfig)
|
||||
let AuthProxyConfig = {
|
||||
injectResponseHeaders: [{
|
||||
name: "x-oidc-id-token"
|
||||
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:\(AuthProxySpec.orgDomain)"
|
||||
clientID: AuthProxySpec.clientID
|
||||
clientSecretFile: "/dev/null"
|
||||
code_challenge_method: "S256"
|
||||
loginURLParameters: [{
|
||||
default: ["force"]
|
||||
name: "approval_prompt"
|
||||
}]
|
||||
oidcConfig: {
|
||||
issuerURL: AuthProxySpec.issuer
|
||||
audienceClaims: ["aud"]
|
||||
emailClaim: "email"
|
||||
groupsClaim: "groups"
|
||||
userIDClaim: "sub"
|
||||
}
|
||||
}]
|
||||
server: BindAddress: ":4180"
|
||||
upstreamConfig: upstreams: [{
|
||||
id: "static://200"
|
||||
path: "/"
|
||||
static: true
|
||||
staticCode: 200
|
||||
}]
|
||||
}
|
||||
}
|
||||
Deployment: (Name): #Deployment & {
|
||||
metadata: ProxyMetadata
|
||||
|
||||
spec: {
|
||||
replicas: 1
|
||||
selector: matchLabels: ProxyMetadata.labels
|
||||
template: {
|
||||
metadata: labels: ProxyMetadata.labels
|
||||
metadata: labels: #IstioSidecar
|
||||
spec: {
|
||||
securityContext: seccompProfile: type: "RuntimeDefault"
|
||||
containers: [{
|
||||
image: "quay.io/oauth2-proxy/oauth2-proxy:v7.6.0"
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
name: "oauth2-proxy"
|
||||
volumeMounts: [{
|
||||
name: "config"
|
||||
mountPath: "/config"
|
||||
readOnly: true
|
||||
}]
|
||||
args: [
|
||||
// callback url is proxy prefix + /callback
|
||||
"--proxy-prefix=" + AuthProxySpec.proxyPrefix,
|
||||
"--email-domain=*",
|
||||
"--session-store-type=redis",
|
||||
"--redis-connection-url=redis://\(RedisMetadata.name):6379",
|
||||
"--cookie-refresh=12h",
|
||||
"--cookie-expire=2160h",
|
||||
"--cookie-secure=true",
|
||||
"--cookie-name=__Secure-\(#ClusterName)-ingress-\(Name)",
|
||||
"--cookie-samesite=lax",
|
||||
for domain in Domains {"--cookie-domain=.\(domain.name)"},
|
||||
for domain in Domains {"--cookie-domain=\(domain.name)"},
|
||||
for domain in Domains {"--whitelist-domain=.\(domain.name)"},
|
||||
for domain in Domains {"--whitelist-domain=\(domain.name)"},
|
||||
"--cookie-csrf-per-request=true",
|
||||
"--cookie-csrf-expire=120s",
|
||||
// will skip authentication for OPTIONS requests
|
||||
"--skip-auth-preflight=true",
|
||||
"--real-client-ip-header=X-Forwarded-For",
|
||||
"--skip-provider-button=true",
|
||||
"--auth-logging",
|
||||
"--alpha-config=/config/config.yaml",
|
||||
]
|
||||
env: [{
|
||||
name: "OAUTH2_PROXY_COOKIE_SECRET"
|
||||
// echo '{"cookiesecret":"'$(LC_ALL=C tr -dc "[:alpha:]" </dev/random | tr '[:upper:]' '[:lower:]' | head -c 32)'"}' | holos create secret -n istio-ingress --append-hash=false --data-stdin authproxy
|
||||
valueFrom: secretKeyRef: {
|
||||
key: "cookiesecret"
|
||||
name: Name
|
||||
}
|
||||
}]
|
||||
ports: [{
|
||||
containerPort: 4180
|
||||
protocol: "TCP"
|
||||
}]
|
||||
securityContext: {
|
||||
seccompProfile: type: "RuntimeDefault"
|
||||
allowPrivilegeEscalation: false
|
||||
runAsNonRoot: true
|
||||
runAsUser: 8192
|
||||
runAsGroup: 8192
|
||||
capabilities: drop: ["ALL"]
|
||||
}
|
||||
}]
|
||||
volumes: [{name: "config", configMap: name: Name}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Service: (Name): #Service & {
|
||||
metadata: ProxyMetadata
|
||||
spec: selector: ProxyMetadata.labels
|
||||
spec: ports: [
|
||||
{port: 4180, targetPort: 4180, protocol: "TCP", name: "http"},
|
||||
]
|
||||
}
|
||||
VirtualService: (Name): #VirtualService & {
|
||||
metadata: ProxyMetadata
|
||||
spec: hosts: ["*"]
|
||||
spec: gateways: ["istio-ingress/default"]
|
||||
spec: http: [{
|
||||
match: [{uri: prefix: AuthProxySpec.proxyPrefix}]
|
||||
route: [{
|
||||
destination: host: Name
|
||||
destination: port: number: 4180
|
||||
}]
|
||||
}]
|
||||
}
|
||||
|
||||
// redis
|
||||
ConfigMap: (RedisMetadata.name): {
|
||||
metadata: RedisMetadata
|
||||
data: "redis.conf": """
|
||||
maxmemory 128mb
|
||||
maxmemory-policy allkeys-lru
|
||||
"""
|
||||
}
|
||||
Deployment: (RedisMetadata.name): {
|
||||
metadata: RedisMetadata
|
||||
spec: {
|
||||
selector: matchLabels: RedisMetadata.labels
|
||||
template: {
|
||||
metadata: labels: RedisMetadata.labels
|
||||
metadata: labels: #IstioSidecar
|
||||
spec: securityContext: seccompProfile: type: "RuntimeDefault"
|
||||
spec: {
|
||||
containers: [{
|
||||
command: [
|
||||
"redis-server",
|
||||
"/redis-master/redis.conf",
|
||||
]
|
||||
env: [{
|
||||
name: "MASTER"
|
||||
value: "true"
|
||||
}]
|
||||
image: "quay.io/holos/redis:7.2.4"
|
||||
livenessProbe: {
|
||||
initialDelaySeconds: 15
|
||||
tcpSocket: port: "redis"
|
||||
}
|
||||
name: "redis"
|
||||
ports: [{
|
||||
containerPort: 6379
|
||||
name: "redis"
|
||||
}]
|
||||
readinessProbe: {
|
||||
exec: command: [
|
||||
"redis-cli",
|
||||
"ping",
|
||||
]
|
||||
initialDelaySeconds: 5
|
||||
}
|
||||
resources: limits: cpu: "0.5"
|
||||
securityContext: {
|
||||
seccompProfile: type: "RuntimeDefault"
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities: drop: ["ALL"]
|
||||
runAsNonRoot: true
|
||||
runAsUser: 999
|
||||
runAsGroup: 999
|
||||
}
|
||||
volumeMounts: [{
|
||||
mountPath: "/redis-master-data"
|
||||
name: "data"
|
||||
}, {
|
||||
mountPath: "/redis-master"
|
||||
name: "config"
|
||||
}]
|
||||
}]
|
||||
volumes: [{
|
||||
emptyDir: {}
|
||||
name: "data"
|
||||
}, {
|
||||
configMap: name: RedisMetadata.name
|
||||
name: "config"
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Service: (RedisMetadata.name): #Service & {
|
||||
metadata: RedisMetadata
|
||||
spec: selector: RedisMetadata.labels
|
||||
spec: type: "ClusterIP"
|
||||
spec: ports: [{
|
||||
name: "redis"
|
||||
port: 6379
|
||||
protocol: "TCP"
|
||||
targetPort: 6379
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Policy represents the AuthorizationPolicy and RequestAuthentication policy
|
||||
Policy: #APIObjects & {
|
||||
apiObjects: {
|
||||
RequestAuthentication: (Name): #RequestAuthentication & {
|
||||
metadata: Metadata & {name: Name}
|
||||
spec: jwtRules: [{
|
||||
audiences: ["\(AuthProxySpec.projectID)"]
|
||||
forwardOriginalToken: true
|
||||
fromHeaders: [{name: AuthProxySpec.idTokenHeader}]
|
||||
issuer: AuthProxySpec.issuer
|
||||
}]
|
||||
spec: selector: matchLabels: istio: "ingressgateway"
|
||||
}
|
||||
AuthorizationPolicy: "\(Name)-custom": {
|
||||
_description: "Route all requests through the auth proxy by default"
|
||||
|
||||
metadata: Metadata & {name: "\(Name)-custom"}
|
||||
spec: {
|
||||
action: "CUSTOM"
|
||||
provider: name: AuthProxySpec.provider
|
||||
rules: [
|
||||
{
|
||||
to: [{
|
||||
operation: notHosts: [
|
||||
// Never send requests for the login service through the authorizer, would block login.
|
||||
AuthProxySpec.issuerHost,
|
||||
// Exclude hosts with specialized rules from the catch-all.
|
||||
for x in _AuthPolicyRules.hosts {x.name},
|
||||
]
|
||||
}]
|
||||
when: [
|
||||
{
|
||||
// bypass the external authorizer when the id token is already in the request.
|
||||
// the RequestAuthentication rule will verify the token.
|
||||
key: "request.headers[\(AuthProxySpec.idTokenHeader)]"
|
||||
notValues: ["*"]
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
selector: matchLabels: istio: "ingressgateway"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_AuthPolicyRules: #AuthPolicyRules & {
|
||||
hosts: {
|
||||
let Vault = "vault.core.ois.run"
|
||||
(Vault): {
|
||||
slug: "vault"
|
||||
// Rules for when to route requests through the auth proxy
|
||||
spec: rules: [
|
||||
{
|
||||
to: [{
|
||||
operation: hosts: [Vault]
|
||||
operation: paths: ["/ui", "/ui/*"]
|
||||
}]
|
||||
},
|
||||
{
|
||||
to: [{
|
||||
operation: hosts: [Vault]
|
||||
}]
|
||||
when: [{
|
||||
key: "request.headers[x-vault-request]"
|
||||
notValues: ["true"]
|
||||
}]
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,20 +172,14 @@ package holos
|
||||
enabled: true
|
||||
// Indicates whether to enable WebAssembly runtime for stats filter.
|
||||
wasmEnabled: false
|
||||
// overrides stats EnvoyFilter configuration.
|
||||
configOverride: {
|
||||
gateway: {}
|
||||
inboundSidecar: {}
|
||||
outboundSidecar: {}
|
||||
}
|
||||
}
|
||||
// stackdriver filter settings.
|
||||
stackdriver: {
|
||||
enabled: false
|
||||
logging: false
|
||||
monitoring: false
|
||||
topology: false // deprecated. setting this to true will have no effect, as this option is no longer supported.
|
||||
disableOutbound: false
|
||||
enabled: false
|
||||
logging: false
|
||||
monitoring: false
|
||||
topology: false // deprecated. setting this to true will have no effect, as this option is no longer supported.
|
||||
|
||||
// configOverride parts give you the ability to override the low level configuration params passed to envoy filter.
|
||||
|
||||
configOverride: {}
|
||||
@@ -248,7 +242,7 @@ package holos
|
||||
// Dev builds from prow are on gcr.io
|
||||
hub: string | *"docker.io/istio"
|
||||
// Default tag for Istio images.
|
||||
tag: string | *"1.20.3"
|
||||
tag: #IstioVersion
|
||||
// Variant of the image to use.
|
||||
// Currently supported are: [debug, distroless]
|
||||
variant: string | *""
|
||||
|
||||
@@ -2,15 +2,27 @@ package holos
|
||||
|
||||
#Project: authProxyOrgDomain: "openinfrastructure.co"
|
||||
|
||||
let ZitadelProjectID = 257713952794870157
|
||||
|
||||
_Projects: #Projects & {
|
||||
// The platform project is required and where platform services reside. ArgoCD, Grafana, Prometheus, etc...
|
||||
platform: {
|
||||
resourceId: ZitadelProjectID
|
||||
// platform level services typically run in the core cluster pair.
|
||||
clusters: core1: _
|
||||
clusters: core2: _
|
||||
// for development, probably wouldn't run these services in the workload clusters.
|
||||
clusters: k2: _
|
||||
// Services hosted in the platform project
|
||||
hosts: argocd: _
|
||||
hosts: grafana: _
|
||||
hosts: prometheus: _
|
||||
}
|
||||
|
||||
holos: {
|
||||
resourceId: 260446255245690199
|
||||
clusters: {
|
||||
k1: _
|
||||
k2: _
|
||||
}
|
||||
stages: dev: authProxyClientID: "260505543108527218@holos"
|
||||
stages: prod: authProxyClientID: "260506079325128023@holos"
|
||||
resourceId: ZitadelProjectID
|
||||
clusters: k1: _
|
||||
clusters: k2: _
|
||||
environments: {
|
||||
prod: stage: "prod"
|
||||
dev: stage: "dev"
|
||||
@@ -21,6 +33,7 @@ _Projects: #Projects & {
|
||||
}
|
||||
|
||||
iam: {
|
||||
resourceId: ZitadelProjectID
|
||||
clusters: {
|
||||
core1: _
|
||||
core2: _
|
||||
|
||||
34
docs/examples/platforms/reference/meshconfig.cue
Normal file
34
docs/examples/platforms/reference/meshconfig.cue
Normal file
@@ -0,0 +1,34 @@
|
||||
package holos
|
||||
|
||||
#MeshConfig: {
|
||||
projects: _
|
||||
clusterName: _
|
||||
|
||||
extensionProviderExtraMap: {
|
||||
"cluster-trace": {
|
||||
zipkin: {
|
||||
maxTagLength: 56
|
||||
port: 9411
|
||||
service: "zipkin.istio-system.svc"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config: {
|
||||
// For PROXY PROTOCOL at the ingress gateway.
|
||||
gatewayTopology: {
|
||||
numTrustedProxies: 2
|
||||
}
|
||||
}
|
||||
|
||||
// Configure an ExtAuthzHttp provider for each stage's authproxy
|
||||
for Project in projects {
|
||||
if Project.clusters[clusterName] != _|_ {
|
||||
for Stage in Project.stages {
|
||||
extensionProviderMap: (Stage.extAuthzProviderName): #ExtAuthzProxy & {
|
||||
envoyExtAuthzHttp: service: "authproxy.\(Stage.namespace).svc.cluster.local"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package holos
|
||||
|
||||
import "encoding/yaml"
|
||||
|
||||
// Platform level definition of a project.
|
||||
#Project: {
|
||||
name: string
|
||||
@@ -73,19 +75,32 @@ package holos
|
||||
}
|
||||
|
||||
// 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 & {
|
||||
apiObjectMap: (#APIObjects & {
|
||||
if project.features.httpbin.enabled {
|
||||
for Env in project.environments if Env.stage == stage.name {
|
||||
"\(Env.slug)-httpbin": #KubernetesObjects & {
|
||||
let Project = project
|
||||
apiObjects: (HTTPBIN & {env: Env, project: Project}).apiObjects
|
||||
}).apiObjectMap
|
||||
apiObjectMap: (#APIObjects & {
|
||||
apiObjects: (HTTPBIN & {env: Env, project: Project}).apiObjects
|
||||
}).apiObjectMap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,12 +138,19 @@ let HTTPBIN = {
|
||||
project: #Project
|
||||
env: #Environment
|
||||
let Name = name
|
||||
let Stage = project.stages[env.stage]
|
||||
|
||||
let Metadata = {
|
||||
name: Name
|
||||
namespace: env.namespace
|
||||
labels: app: name
|
||||
}
|
||||
let Labels = {
|
||||
"app.kubernetes.io/name": Name
|
||||
"app.kubernetes.io/instance": env.slug
|
||||
"app.kubernetes.io/part-of": env.project
|
||||
"security.holos.run/authproxy": Stage.extAuthzProviderName
|
||||
}
|
||||
|
||||
apiObjects: {
|
||||
Deployment: (Name): #Deployment & {
|
||||
@@ -136,7 +158,7 @@ let HTTPBIN = {
|
||||
|
||||
spec: selector: matchLabels: Metadata.labels
|
||||
spec: template: {
|
||||
metadata: labels: Metadata.labels & #IstioSidecar
|
||||
metadata: labels: Metadata.labels & #IstioSidecar & Labels
|
||||
spec: securityContext: seccompProfile: type: "RuntimeDefault"
|
||||
spec: containers: [{
|
||||
name: Name
|
||||
@@ -180,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
|
||||
@@ -203,6 +233,45 @@ let AUTHPROXY = {
|
||||
apiObjects: {
|
||||
// oauth2-proxy
|
||||
ExternalSecret: (Name): metadata: Metadata
|
||||
// Place the ID token in a header that does not conflict with the Authorization header.
|
||||
// Refer to: https://github.com/oauth2-proxy/oauth2-proxy/issues/1877#issuecomment-1364033723
|
||||
ConfigMap: (Name): {
|
||||
metadata: Metadata
|
||||
data: "config.yaml": yaml.Marshal(AuthProxyConfig)
|
||||
let AuthProxyConfig = {
|
||||
injectResponseHeaders: [{
|
||||
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:\(AuthProxySpec.orgDomain)"
|
||||
clientID: AuthProxySpec.clientID
|
||||
clientSecretFile: "/dev/null"
|
||||
code_challenge_method: "S256"
|
||||
loginURLParameters: [{
|
||||
default: ["force"]
|
||||
name: "approval_prompt"
|
||||
}]
|
||||
oidcConfig: {
|
||||
issuerURL: AuthProxySpec.issuer
|
||||
audienceClaims: ["aud"]
|
||||
emailClaim: "email"
|
||||
groupsClaim: "groups"
|
||||
userIDClaim: "sub"
|
||||
}
|
||||
}]
|
||||
server: BindAddress: ":4180"
|
||||
upstreamConfig: upstreams: [{
|
||||
id: "static://200"
|
||||
path: "/"
|
||||
static: true
|
||||
staticCode: 200
|
||||
}]
|
||||
}
|
||||
}
|
||||
Deployment: (Name): #Deployment & {
|
||||
metadata: Metadata
|
||||
|
||||
@@ -219,65 +288,64 @@ let AUTHPROXY = {
|
||||
template: {
|
||||
metadata: labels: Metadata.labels
|
||||
metadata: labels: #IstioSidecar
|
||||
spec: securityContext: seccompProfile: type: "RuntimeDefault"
|
||||
spec: containers: [{
|
||||
image: "quay.io/oauth2-proxy/oauth2-proxy:v7.4.0"
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
name: "oauth2-proxy"
|
||||
args: [
|
||||
// callback url is proxy prefix + /callback
|
||||
"--proxy-prefix=" + project.authProxyPrefix,
|
||||
"--email-domain=*",
|
||||
"--session-store-type=redis",
|
||||
"--redis-connection-url=redis://\(RedisMetadata.name):6379",
|
||||
"--cookie-refresh=12h",
|
||||
"--cookie-expire=2160h",
|
||||
"--cookie-secure=true",
|
||||
"--cookie-name=__Secure-\(Name)-\(stage.slug)",
|
||||
"--cookie-samesite=lax",
|
||||
for domain in StageDomains {"--cookie-domain=.\(domain.name)"},
|
||||
for domain in StageDomains {"--whitelist-domain=.\(domain.name)"},
|
||||
"--cookie-csrf-per-request=true",
|
||||
"--cookie-csrf-expire=120s",
|
||||
"--set-authorization-header=false",
|
||||
"--set-xauthrequest=true",
|
||||
"--pass-access-token=true",
|
||||
"--pass-authorization-header=true",
|
||||
"--upstream=static://200",
|
||||
"--reverse-proxy",
|
||||
"--real-client-ip-header=X-Forwarded-For",
|
||||
"--skip-provider-button=true",
|
||||
"--auth-logging",
|
||||
"--provider=oidc",
|
||||
"--scope=openid profile email groups offline_access urn:zitadel:iam:org:domain:primary:\(project.authProxyOrgDomain)",
|
||||
"--client-id=" + stage.authProxyClientID,
|
||||
"--client-secret-file=/dev/null",
|
||||
"--oidc-issuer-url=\(project.authProxyIssuer)",
|
||||
"--code-challenge-method=S256",
|
||||
"--http-address=0.0.0.0:4180",
|
||||
// "--allowed-group=\(project.resourceId):\(stage.name)-access",
|
||||
]
|
||||
env: [{
|
||||
name: "OAUTH2_PROXY_COOKIE_SECRET"
|
||||
// echo '{"cookiesecret":"'$(LC_ALL=C tr -dc "[:alpha:]" </dev/random | tr '[:upper:]' '[:lower:]' | head -c 32)'"}' | holos create secret -n dev-holos-system --append-hash=false --data-stdin authproxy
|
||||
valueFrom: secretKeyRef: {
|
||||
key: "cookiesecret"
|
||||
name: Name
|
||||
spec: {
|
||||
securityContext: seccompProfile: type: "RuntimeDefault"
|
||||
containers: [{
|
||||
image: "quay.io/oauth2-proxy/oauth2-proxy:v7.6.0"
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
name: "oauth2-proxy"
|
||||
volumeMounts: [{
|
||||
name: "config"
|
||||
mountPath: "/config"
|
||||
readOnly: true
|
||||
}]
|
||||
args: [
|
||||
// callback url is proxy prefix + /callback
|
||||
"--proxy-prefix=" + AuthProxySpec.proxyPrefix,
|
||||
"--email-domain=*",
|
||||
"--session-store-type=redis",
|
||||
"--redis-connection-url=redis://\(RedisMetadata.name):6379",
|
||||
"--cookie-refresh=12h",
|
||||
"--cookie-expire=2160h",
|
||||
"--cookie-secure=true",
|
||||
"--cookie-name=__Secure-\(stage.slug)-\(Name)",
|
||||
"--cookie-samesite=lax",
|
||||
for domain in StageDomains {"--cookie-domain=.\(domain.name)"},
|
||||
for domain in StageDomains {"--cookie-domain=\(domain.name)"},
|
||||
for domain in StageDomains {"--whitelist-domain=.\(domain.name)"},
|
||||
for domain in StageDomains {"--whitelist-domain=\(domain.name)"},
|
||||
"--cookie-csrf-per-request=true",
|
||||
"--cookie-csrf-expire=120s",
|
||||
// will skip authentication for OPTIONS requests
|
||||
"--skip-auth-preflight=true",
|
||||
"--real-client-ip-header=X-Forwarded-For",
|
||||
"--skip-provider-button=true",
|
||||
"--auth-logging",
|
||||
"--alpha-config=/config/config.yaml",
|
||||
]
|
||||
env: [{
|
||||
name: "OAUTH2_PROXY_COOKIE_SECRET"
|
||||
// echo '{"cookiesecret":"'$(LC_ALL=C tr -dc "[:alpha:]" </dev/random | tr '[:upper:]' '[:lower:]' | head -c 32)'"}' | holos create secret -n dev-holos-system --append-hash=false --data-stdin authproxy
|
||||
valueFrom: secretKeyRef: {
|
||||
key: "cookiesecret"
|
||||
name: Name
|
||||
}
|
||||
}]
|
||||
ports: [{
|
||||
containerPort: 4180
|
||||
protocol: "TCP"
|
||||
}]
|
||||
securityContext: {
|
||||
seccompProfile: type: "RuntimeDefault"
|
||||
allowPrivilegeEscalation: false
|
||||
runAsNonRoot: true
|
||||
runAsUser: 8192
|
||||
runAsGroup: 8192
|
||||
capabilities: drop: ["ALL"]
|
||||
}
|
||||
}]
|
||||
ports: [{
|
||||
containerPort: 4180
|
||||
protocol: "TCP"
|
||||
}]
|
||||
securityContext: {
|
||||
seccompProfile: type: "RuntimeDefault"
|
||||
allowPrivilegeEscalation: false
|
||||
runAsNonRoot: true
|
||||
runAsUser: 8192
|
||||
runAsGroup: 8192
|
||||
capabilities: drop: ["ALL"]
|
||||
}
|
||||
}]
|
||||
volumes: [{name: "config", configMap: name: Name}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -285,18 +353,18 @@ let AUTHPROXY = {
|
||||
metadata: Metadata
|
||||
spec: selector: Metadata.labels
|
||||
spec: ports: [
|
||||
{port: 80, targetPort: 4180, protocol: "TCP", name: "http"},
|
||||
{port: 4180, targetPort: 4180, protocol: "TCP", name: "http"},
|
||||
]
|
||||
}
|
||||
VirtualService: (Name): #VirtualService & {
|
||||
metadata: Metadata
|
||||
spec: hosts: [for host, v in servers {host}]
|
||||
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: 80
|
||||
destination: port: number: 4180
|
||||
}]
|
||||
}]
|
||||
}
|
||||
@@ -349,6 +417,9 @@ let AUTHPROXY = {
|
||||
seccompProfile: type: "RuntimeDefault"
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities: drop: ["ALL"]
|
||||
runAsNonRoot: true
|
||||
runAsUser: 999
|
||||
runAsGroup: 999
|
||||
}
|
||||
volumeMounts: [{
|
||||
mountPath: "/redis-master-data"
|
||||
@@ -382,3 +453,79 @@ let AUTHPROXY = {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AUTHPOLICY configures the baseline AuthorizationPolicy and RequestAuthentication policy for each stage of each project.
|
||||
let AUTHPOLICY = {
|
||||
project: #Project
|
||||
env: #Environment
|
||||
let Name = "\(stage.slug)-authproxy"
|
||||
let Project = project
|
||||
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
|
||||
labels: {
|
||||
"app.kubernetes.io/name": name
|
||||
"app.kubernetes.io/instance": stage.name
|
||||
"app.kubernetes.io/part-of": stage.project
|
||||
}
|
||||
}
|
||||
|
||||
// Collect all the hosts associated with the stage
|
||||
let Hosts = {
|
||||
for HOST in (#EnvHosts & {project: Project, env: Env}).hosts {
|
||||
(HOST.name): HOST
|
||||
}
|
||||
}
|
||||
|
||||
// HostList is a list of hosts for AuthorizationPolicy rules
|
||||
let HostList = [
|
||||
for host in Hosts {host.name},
|
||||
for host in Hosts {host.name + ":*"},
|
||||
]
|
||||
let MatchLabels = {"security.holos.run/authproxy": AuthProxySpec.provider}
|
||||
|
||||
apiObjects: {
|
||||
RequestAuthentication: (Name): #RequestAuthentication & {
|
||||
metadata: Metadata & {name: Name}
|
||||
spec: jwtRules: [{
|
||||
audiences: [AuthProxySpec.clientID]
|
||||
forwardOriginalToken: true
|
||||
fromHeaders: [{name: AuthProxySpec.idTokenHeader}]
|
||||
issuer: AuthProxySpec.issuer
|
||||
}]
|
||||
spec: selector: matchLabels: MatchLabels
|
||||
}
|
||||
AuthorizationPolicy: "\(Name)-custom": {
|
||||
metadata: Metadata & {name: "\(Name)-custom"}
|
||||
spec: {
|
||||
action: "CUSTOM"
|
||||
// send the request to the auth proxy
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ import "strings"
|
||||
// #Projects is a map of all the projects in the platform.
|
||||
#Projects: [Name=_]: #Project & {name: Name}
|
||||
|
||||
// The platform project is required and where platform services reside. ArgoCD, Grafana, Prometheus, etc...
|
||||
#Projects: platform: _
|
||||
|
||||
#Project: {
|
||||
name: string
|
||||
// resourceId is the zitadel project Resource ID
|
||||
@@ -23,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
|
||||
@@ -60,6 +61,8 @@ import "strings"
|
||||
|
||||
// features is YAGNI maybe?
|
||||
features: [Name=string]: #Feature & {name: Name}
|
||||
features: authproxy: _
|
||||
features: httpbin: _
|
||||
}
|
||||
|
||||
// #Cluster defines a cluster
|
||||
@@ -117,12 +120,14 @@ import "strings"
|
||||
stageSegments: [...string] | *[name]
|
||||
// authProxyClientID is the ClientID registered with the oidc issuer.
|
||||
authProxyClientID: string
|
||||
// extAuthzProviderName is the provider name in the mesh config
|
||||
extAuthzProviderName: "\(slug)-authproxy"
|
||||
}
|
||||
|
||||
#Feature: {
|
||||
name: string
|
||||
description: string
|
||||
enabled: *true | false
|
||||
enabled: true | *false
|
||||
}
|
||||
|
||||
#ProjectTemplate: {
|
||||
|
||||
@@ -13,6 +13,8 @@ import (
|
||||
crt "cert-manager.io/certificate/v1"
|
||||
gw "networking.istio.io/gateway/v1beta1"
|
||||
vs "networking.istio.io/virtualservice/v1beta1"
|
||||
ra "security.istio.io/requestauthentication/v1"
|
||||
ap "security.istio.io/authorizationpolicy/v1"
|
||||
pg "postgres-operator.crunchydata.com/postgrescluster/v1beta1"
|
||||
)
|
||||
|
||||
@@ -63,19 +65,21 @@ _apiVersion: "holos.run/v1alpha1"
|
||||
#ClusterRoleBinding: #ClusterObject & rbacv1.#ClusterRoleBinding
|
||||
#ClusterIssuer: #ClusterObject & ci.#ClusterIssuer & {...}
|
||||
|
||||
#Issuer: #NamespaceObject & is.#Issuer
|
||||
#Role: #NamespaceObject & rbacv1.#Role
|
||||
#RoleBinding: #NamespaceObject & rbacv1.#RoleBinding
|
||||
#ConfigMap: #NamespaceObject & corev1.#ConfigMap
|
||||
#ServiceAccount: #NamespaceObject & corev1.#ServiceAccount
|
||||
#Pod: #NamespaceObject & corev1.#Pod
|
||||
#Service: #NamespaceObject & corev1.#Service
|
||||
#Job: #NamespaceObject & batchv1.#Job
|
||||
#CronJob: #NamespaceObject & batchv1.#CronJob
|
||||
#Deployment: #NamespaceObject & appsv1.#Deployment
|
||||
#VirtualService: #NamespaceObject & vs.#VirtualService
|
||||
#Certificate: #NamespaceObject & crt.#Certificate
|
||||
#PostgresCluster: #NamespaceObject & pg.#PostgresCluster
|
||||
#Issuer: #NamespaceObject & is.#Issuer
|
||||
#Role: #NamespaceObject & rbacv1.#Role
|
||||
#RoleBinding: #NamespaceObject & rbacv1.#RoleBinding
|
||||
#ConfigMap: #NamespaceObject & corev1.#ConfigMap
|
||||
#ServiceAccount: #NamespaceObject & corev1.#ServiceAccount
|
||||
#Pod: #NamespaceObject & corev1.#Pod
|
||||
#Service: #NamespaceObject & corev1.#Service
|
||||
#Job: #NamespaceObject & batchv1.#Job
|
||||
#CronJob: #NamespaceObject & batchv1.#CronJob
|
||||
#Deployment: #NamespaceObject & appsv1.#Deployment
|
||||
#VirtualService: #NamespaceObject & vs.#VirtualService
|
||||
#RequestAuthentication: #NamespaceObject & ra.#RequestAuthentication
|
||||
#AuthorizationPolicy: #NamespaceObject & ap.#AuthorizationPolicy
|
||||
#Certificate: #NamespaceObject & crt.#Certificate
|
||||
#PostgresCluster: #NamespaceObject & pg.#PostgresCluster
|
||||
|
||||
#Gateway: #NamespaceObject & gw.#Gateway & {
|
||||
metadata: namespace: string | *"istio-ingress"
|
||||
@@ -108,7 +112,7 @@ _apiVersion: "holos.run/v1alpha1"
|
||||
_name: string
|
||||
metadata: {
|
||||
name: _name
|
||||
namespace: #TargetNamespace
|
||||
namespace: string | *#TargetNamespace
|
||||
}
|
||||
spec: {
|
||||
refreshInterval: string | *"1h"
|
||||
@@ -221,6 +225,32 @@ _apiVersion: "holos.run/v1alpha1"
|
||||
services: [ID=_]: {
|
||||
name: string & ID
|
||||
}
|
||||
// authproxy configures the auth proxy attached to the default ingress gateway in the istio-ingress namespace.
|
||||
authproxy: #AuthProxySpec & {
|
||||
namespace: "istio-ingress"
|
||||
provider: "ingressauth"
|
||||
}
|
||||
}
|
||||
|
||||
#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://\(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/authproxy/\(namespace)"
|
||||
// idTokenHeader represents the header where the id token is placed
|
||||
idTokenHeader: string | *"x-oidc-id-token"
|
||||
}
|
||||
|
||||
// ManagedNamespace is a namespace to manage across all clusters in the holos platform.
|
||||
|
||||
@@ -1 +1 @@
|
||||
61
|
||||
62
|
||||
|
||||
@@ -1 +1 @@
|
||||
3
|
||||
0
|
||||
|
||||
Reference in New Issue
Block a user