Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 41 additions & 31 deletions deployment-configuration/compose/templates/auto-gatekeepers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,41 +42,51 @@

{{- define "securedservice.deploy.resources" }}
{{- $tls := not (not .root.Values.tls) }}
{{- $scheme := ternary "https" "http" $tls }}
{{- $readTimeout := .app.harness.proxy.timeout.read | default .root.Values.proxy.timeout.read | default 180 }}
{{- $sendTimeout := .app.harness.proxy.timeout.send | default .root.Values.proxy.timeout.send | default 180 }}
{{- $keepaliveTimeout := .app.harness.proxy.timeout.keepalive | default .root.Values.proxy.timeout.keepalive | default 180 }}
{{- $generatedGatekeeperConfiguration := dict
"verbose" .root.Values.debug
"discovery-url" (printf "%s://%s.%s/realms/%s" $scheme .root.Values.apps.accounts.harness.subdomain .root.Values.domain .root.Values.namespace)
"client-id" .root.Values.apps.accounts.webclient.id
"client-secret" .root.Values.apps.accounts.webclient.secret
"secure-cookie" $tls
"forbidden-page" "/templates/access-denied.html.tmpl"
"enable-default-deny" (eq (.app.harness.secured | toString) "true")
"listen" "0.0.0.0:8080"
"enable-refresh-tokens" true
"server-write-timeout" (printf "%vs" $sendTimeout)
"upstream-timeout" (printf "%vs" $readTimeout)
"upstream-response-header-timeout" (printf "%vs" $readTimeout)
"upstream-expect-continue-timeout" (printf "%vs" $readTimeout)
"server-read-timeout" (printf "%vs" $readTimeout)
"upstream-keepalive-timeout" (printf "%vs" $keepaliveTimeout)
"http-only-cookie" false
"tls-cert" ""
"tls-private-key" ""
"redirection-url" (printf "%s://%s.%s" $scheme .app.harness.subdomain .root.Values.domain)
"encryption-key" "AgXa7xRcoClDEU0ZDSH4X0XhL5Qy2Z2j"
"upstream-url" (printf "http://%s.%s:%v" .app.harness.service.name (.app.namespace | default .root.Release.Namespace) (.app.harness.service.port | default 80))
}}
{{- if and .app.harness.secured .app.harness.uri_role_mapping }}
{{- $_ := set $generatedGatekeeperConfiguration "resources" .app.harness.uri_role_mapping }}
{{- end }}
{{- if or .root.Values.local (not $tls) }}
{{- $_ := set $generatedGatekeeperConfiguration "skip-openid-provider-tls-verify" true }}
{{- $_ := set $generatedGatekeeperConfiguration "skip-upstream-tls-verify" true }}
{{- end }}
{{- $globalGatekeeperConfiguration := deepCopy (.root.Values.proxy.gatekeeper.configuration | default dict) }}
{{- $applicationGatekeeperConfiguration := deepCopy (.app.harness.proxy.gatekeeper.configuration | default dict) }}
{{- $gatekeeperConfiguration := mergeOverwrite $generatedGatekeeperConfiguration $globalGatekeeperConfiguration $applicationGatekeeperConfiguration }}
{{- if and (not $tls) (eq ((get $gatekeeperConfiguration "same-site-cookie") | toString) "None") }}
{{- $_ := set $gatekeeperConfiguration "same-site-cookie" "Lax" }}
{{- end }}
cloudharness-metadata:
path: resources/generated/{{ .app.harness.service.name }}-gk/proxy.yml

data: |-
verbose: {{ .root.Values.debug }}
discovery-url: {{ ternary "https" "http" $tls}}://{{ .root.Values.apps.accounts.harness.subdomain }}.{{ .root.Values.domain }}/realms/{{ .root.Values.namespace }}
client-id: {{ .root.Values.apps.accounts.webclient.id | quote }}
client-secret: {{ .root.Values.apps.accounts.webclient.secret }}
secure-cookie: {{ $tls }}
forbidden-page: /templates/access-denied.html.tmpl
enable-default-deny: {{ eq (.app.harness.secured | toString) "true" }}
listen: 0.0.0.0:8080
enable-refresh-tokens: true
server-write-timeout: {{ .app.harness.proxy.timeout.send | default .root.Values.proxy.timeout.send | default 180 }}s
upstream-timeout: {{ .app.harness.proxy.timeout.read | default .root.Values.proxy.timeout.read | default 180 }}s
upstream-response-header-timeout: {{ .app.harness.proxy.timeout.read | default .root.Values.proxy.timeout.read | default 180 }}s
upstream-expect-continue-timeout: {{ .app.harness.proxy.timeout.read | default .root.Values.proxy.timeout.read | default 180 }}s
server-read-timeout: {{ .app.harness.proxy.timeout.read | default .root.Values.proxy.timeout.read | default 180 }}s
upstream-keepalive-timeout: {{ .app.harness.proxy.timeout.keepalive | default .root.Values.proxy.timeout.keepalive | default 180 }}s
http-only-cookie: false
tls-cert:
tls-private-key:
redirection-url: {{ ternary "https" "http" $tls }}://{{ .app.harness.subdomain }}.{{ .root.Values.domain }}
encryption-key: AgXa7xRcoClDEU0ZDSH4X0XhL5Qy2Z2j
upstream-url: http://{{ .app.harness.service.name }}.{{ .app.namespace | default .root.Release.Namespace }}:{{ .app.harness.service.port | default 80}}
{{ if .app.harness.secured }}
{{ with .app.harness.uri_role_mapping }}
resources:
{{. | toYaml | nindent 4 }}
{{- end }}
{{- end }}
{{ if or .root.Values.local (not $tls) }}
skip-openid-provider-tls-verify: true
skip-upstream-tls-verify: true
{{- end }}
{{ $gatekeeperConfiguration | toYaml | nindent 4 }}
---
cloudharness-metadata:
path: resources/generated/{{ .app.harness.service.name }}-gk/cacert.crt
Expand Down
2 changes: 1 addition & 1 deletion deployment-configuration/helm/templates/auto-database.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,4 @@ spec:
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,4 @@ data:
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
91 changes: 46 additions & 45 deletions deployment-configuration/helm/templates/auto-gatekeepers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,49 @@
{{- define "deploy_utils.securedservice" }}
{{- $tls := not (not .root.Values.tls) }}
{{- $noWildcards := include "check_no_wildcard_uri" (dict "uri_role_mapping" .app.harness.uri_role_mapping) }}
{{- $scheme := ternary "https" "http" $tls }}
{{- $readTimeout := .app.harness.proxy.timeout.read | default .root.Values.proxy.timeout.read | default 180 }}
{{- $sendTimeout := .app.harness.proxy.timeout.send | default .root.Values.proxy.timeout.send | default 180 }}
{{- $keepaliveTimeout := .app.harness.proxy.timeout.keepalive | default .root.Values.proxy.timeout.keepalive | default 180 }}
{{- $generatedGatekeeperConfiguration := dict
"verbose" .root.Values.debug
"discovery-url" (printf "%s://%s.%s/realms/%s" $scheme .root.Values.apps.accounts.harness.subdomain .root.Values.domain .root.Values.namespace)
"client-id" .root.Values.apps.accounts.webclient.id
"client-secret" .root.Values.apps.accounts.webclient.secret
"secure-cookie" $tls
"forbidden-page" "/templates/access-denied.html.tmpl"
"enable-default-deny" (eq $noWildcards "true")
"listen" "0.0.0.0:8080"
"enable-encrypted-token" false
"enable-refresh-tokens" true
"server-write-timeout" (printf "%vs" $sendTimeout)
"upstream-timeout" (printf "%vs" $readTimeout)
"upstream-response-header-timeout" (printf "%vs" $readTimeout)
"upstream-expect-continue-timeout" (printf "%vs" $readTimeout)
"server-read-timeout" (printf "%vs" $readTimeout)
"upstream-keepalive-timeout" (printf "%vs" $keepaliveTimeout)
"http-only-cookie" false
"tls-cert" ""
"tls-private-key" ""
"redirection-url" (printf "%s://%s.%s" $scheme .subdomain .root.Values.domain)
"upstream-url" (printf "http://%s.%s:%v" .app.harness.service.name (.app.namespace | default .root.Release.Namespace) (.app.harness.service.port | default 80))
"cors-origins" (list (printf "*.%s" .root.Values.domain))
"cors-methods" (list "GET" "POST" "PUT" "DELETE" "PATCH")
"cors-headers" (list "Authorization" "Content-Type" "Origin")
}}
{{- if and .app.harness.secured .app.harness.uri_role_mapping }}
{{- $_ := set $generatedGatekeeperConfiguration "resources" .app.harness.uri_role_mapping }}
{{- end }}
{{- if or .root.Values.local (not $tls) }}
{{- $_ := set $generatedGatekeeperConfiguration "skip-openid-provider-tls-verify" true }}
{{- $_ := set $generatedGatekeeperConfiguration "skip-upstream-tls-verify" true }}
{{- end }}
{{- $globalGatekeeperConfiguration := deepCopy (.root.Values.proxy.gatekeeper.configuration | default dict) }}
{{- $applicationGatekeeperConfiguration := deepCopy (.app.harness.proxy.gatekeeper.configuration | default dict) }}
{{- $gatekeeperConfiguration := mergeOverwrite $generatedGatekeeperConfiguration $globalGatekeeperConfiguration $applicationGatekeeperConfiguration }}
{{- if and (not $tls) (eq ((get $gatekeeperConfiguration "same-site-cookie") | toString) "None") }}
{{- $_ := set $gatekeeperConfiguration "same-site-cookie" "Lax" }}
{{- end }}
apiVersion: v1
kind: ConfigMap
metadata:
Expand All @@ -19,49 +62,7 @@ metadata:
app: "{{ .subdomain }}-gk"
data:
proxy.yml: |-
verbose: {{ .root.Values.debug }}
discovery-url: {{ ternary "https" "http" $tls}}://{{ .root.Values.apps.accounts.harness.subdomain }}.{{ .root.Values.domain }}/realms/{{ .root.Values.namespace }}
client-id: {{ .root.Values.apps.accounts.webclient.id | quote }}
client-secret: {{ .root.Values.apps.accounts.webclient.secret }}
secure-cookie: {{ $tls }}
forbidden-page: /templates/access-denied.html.tmpl
enable-default-deny: {{ $noWildcards }}
listen: 0.0.0.0:8080
enable-encrypted-token: false
enable-refresh-tokens: true
server-write-timeout: {{ .app.harness.proxy.timeout.send | default .root.Values.proxy.timeout.send | default 180 }}s
upstream-timeout: {{ .app.harness.proxy.timeout.read | default .root.Values.proxy.timeout.read | default 180 }}s
upstream-response-header-timeout: {{ .app.harness.proxy.timeout.read | default .root.Values.proxy.timeout.read | default 180 }}s
upstream-expect-continue-timeout: {{ .app.harness.proxy.timeout.read | default .root.Values.proxy.timeout.read | default 180 }}s
server-read-timeout: {{ .app.harness.proxy.timeout.read | default .root.Values.proxy.timeout.read | default 180 }}s
upstream-keepalive-timeout: {{ .app.harness.proxy.timeout.keepalive | default .root.Values.proxy.timeout.keepalive | default 180 }}s
http-only-cookie: false
tls-cert:
tls-private-key:
redirection-url: {{ ternary "https" "http" $tls }}://{{ .subdomain }}.{{ .root.Values.domain }}
upstream-url: http://{{ .app.harness.service.name }}.{{ .app.namespace | default .root.Release.Namespace }}:{{ .app.harness.service.port | default 80}}
{{ if .app.harness.secured }}
{{ with .app.harness.uri_role_mapping }}
resources:
{{. | toYaml | nindent 4 }}
{{- end }}
{{- end }}
{{ if or .root.Values.local (not $tls) }}
skip-openid-provider-tls-verify: true
skip-upstream-tls-verify: true
{{- end }}
cors-origins:
- {{ (printf "*.%s" .root.Values.domain) | quote }}
cors-methods:
- GET
- POST
- PUT
- DELETE
- PATCH
cors-headers:
- Authorization
- Content-Type
- Origin
{{ $gatekeeperConfiguration | toYaml | nindent 4 }}
cacert.crt: {{ .files.Get "resources/certs/cacert.crt" | quote }}
access-denied.html.tmpl: |-
<!DOCTYPE html>
Expand Down Expand Up @@ -173,7 +174,7 @@ spec:
template:
metadata:
annotations:
checksum/config: {{ .app.harness.uri_role_mapping | toString | sha256sum }}
checksum/config: {{ $gatekeeperConfiguration | toJson | sha256sum }}
labels:
app: "{{ .subdomain }}-gk"
spec:
Expand Down Expand Up @@ -243,4 +244,4 @@ spec:
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
3 changes: 3 additions & 0 deletions deployment-configuration/value-template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ harness:
image: "quay.io/gogatekeeper/gatekeeper:4.6.0"
# -- Default number of gatekeeper replicas
replicas: 1
# -- Native Gatekeeper proxy.yml settings. Kebab-case keys follow the
# Gatekeeper configuration reference and override global/generated values.
configuration: {}
resources:
requests:
memory: "32Mi"
Expand Down
22 changes: 22 additions & 0 deletions docs/accounts.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,28 @@ harness:
# -- Maximum size of payload in MB
max:
```

Native [Gatekeeper configuration](https://gogatekeeper.github.io/gatekeeper/configuration/)
can also be supplied with its `proxy.yml` kebab-case keys. CloudHarness merges
generated settings first, then the global map, and finally the application map:

```yaml
harness:
proxy:
gatekeeper:
configuration:
same-site-cookie: "None"
enable-pkce: true
no-redirects: false
cors-exposed-headers:
- X-Request-ID
```

Application values are the last writer, including for settings generated by
CloudHarness. Global defaults use `proxy.gatekeeper.configuration`. When TLS is
disabled, CloudHarness renders `same-site-cookie: Lax` even if `None` was
requested because browsers require `SameSite=None` cookies to be secure.

### Secure an enpoint with OpenAPI

In every api endpoint that you want to secure, add the bearerAuth security as in the example:
Expand Down
3 changes: 1 addition & 2 deletions docs/model/GatekeeperConf.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Name | Type | Description | Notes
**replicas** | **int** | | [optional]
**resources** | [**DeploymentResourcesConf**](DeploymentResourcesConf.md) | | [optional]
**secret** | **str** | | [optional]
**configuration** | **Dict[str, Any]** | Native Gatekeeper proxy.yml settings, keyed by the kebab-case names from the Gatekeeper configuration reference. Application values override global values and CloudHarness-generated defaults. | [optional]

## Example

Expand All @@ -29,5 +30,3 @@ gatekeeper_conf_dict = gatekeeper_conf_instance.to_dict()
gatekeeper_conf_from_dict = GatekeeperConf.from_dict(gatekeeper_conf_dict)
```
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)


2 changes: 1 addition & 1 deletion libraries/cloudharness-common/tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tox]
envlist = py3{9,12}
envlist = py312

[testenv]
deps=-r{toxinidir}/test-requirements.txt
Expand Down
3 changes: 1 addition & 2 deletions libraries/cloudharness-utils/tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tox]
envlist = py3{9,12}
envlist = py312
skipsdist = False

[pytest]
Expand All @@ -15,4 +15,3 @@ commands =
python -c "print('cloudharness-utils: No tests to run - utility library only')"
python -c "import cloudharness_utils; print('Import successful')"


9 changes: 9 additions & 0 deletions libraries/models/api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -875,9 +875,18 @@ components:
secret:
description: ''
type: string
configuration:
description: >-
Native Gatekeeper proxy.yml settings, keyed by the kebab-case names from
the Gatekeeper configuration reference. Application values override global
values and CloudHarness-generated defaults.
type: object
additionalProperties: true
example:
image: 'quay.io/gogatekeeper/gatekeeper:2.14.3'
replicas: 5
configuration:
same-site-cookie: Lax
GatewayConfig:
description: ''
type: object
Expand Down
7 changes: 4 additions & 3 deletions libraries/models/cloudharness_model/models/gatekeeper_conf.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions libraries/models/docs/GatekeeperConf.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 15 additions & 2 deletions libraries/models/test/test_deserialize.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import pytest
from os.path import join, dirname as dn, realpath
import oyaml as yaml

from cloudharness_model import HarnessMainConfig, ApplicationConfig, User, ApplicationHarnessConfig, CDCEvent, ApplicationTestConfig, DatabaseConfig
from cloudharness_model import HarnessMainConfig, ApplicationConfig, User, ApplicationHarnessConfig, CDCEvent, ApplicationTestConfig, DatabaseConfig, GatekeeperConf

HERE = dn(realpath(__file__))

Expand Down Expand Up @@ -58,6 +57,20 @@ def test_database_config_parameters_round_trip():
}


def test_gatekeeper_native_configuration_round_trip():
native_configuration = {
"same-site-cookie": "None",
"enable-pkce": True,
"max-token-size": 65536,
"cors-exposed-headers": ["X-Request-ID", "X-Trace-ID"],
}

config = GatekeeperConf.from_dict({"configuration": native_configuration})

assert config.configuration == native_configuration
assert config.to_dict()["configuration"] == native_configuration


def test_robustness():
d = {'aliases': [], 'database': {'auto': True, 'mongo': {'image': 'mongo:5', 'ports': [{'name': 'http', 'port': 27017}]}, 'name': 'keycloak-postgres', 'neo4j': {'dbms_security_auth_enabled': 'false', 'image': 'neo4j:4.1.9', 'memory': {'heap': {'initial': '64M', 'max': '128M'}, 'pagecache': {'size': '64M'}, 'size': '256M'}, 'ports': [{'name': 'http', 'port': 7474}, {'name': 'bolt', 'port': 7687}]}, 'pass': 'password', 'postgres': {'image': 'postgres:10.4', 'initialdb': 'auth_db', 'ports': [{'name': 'http', 'port': 5432}]}, 'resources': {'limits': {'cpu': '1000m', 'memory': '2Gi'}, 'requests': {'cpu': '100m', 'memory': '512Mi'}}, 'size': '2Gi', 'type': 'postgres', 'user': 'user'}, 'dependencies': {'build': [], 'hard': [], 'soft': []}, 'deployment': {'auto': True, 'image': 'osb/accounts:3e02a15477b4696ed554e08cedf4109c67908cbe6b03331072b5b73e83b4fc2b', 'name': 'accounts', 'port': 8080, 'replicas': 1, 'resources': {'limits': {'cpu': '500m', 'memory': '1024Mi'}, 'requests': {'cpu': '10m', 'memory': '512Mi'}}}, 'domain': None, 'env': [{'name': 'KEYCLOAK_IMPORT', 'value': '/tmp/realm.json'},
{'name': 'KEYCLOAK_USER', 'value': 'admin'}, {'name': 'KEYCLOAK_PASSWORD', 'value': 'metacell'}, {'name': 'PROXY_ADDRESS_FORWARDING', 'value': 'true'}, {'name': 'DB_VENDOR', 'value': 'POSTGRES'}, {'name': 'DB_ADDR', 'value': 'keycloak-postgres'}, {'name': 'DB_DATABASE', 'value': 'auth_db'}, {'name': 'DB_USER', 'value': 'user'}, {'name': 'DB_PASSWORD', 'value': 'password'}, {'name': 'JAVA_OPTS', 'value': '-server -Xms64m -Xmx896m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED --add-exports=jdk.unsupported/sun.reflect=ALL-UNNAMED'}], 'name': 'accounts', 'readinessProbe': {'path': '/realms/master'}, 'resources': [{'dst': '/tmp/realm.json', 'name': 'realm-config', 'src': 'realm.json'}], 'secrets': {}, 'secured': False, 'service': {'auto': True, 'name': 'accounts', 'port': 8080}, 'subdomain': 'accounts', 'uri_role_mapping': [{'roles': ['administrator'], 'uri': '/*'}], 'use_services': []}
Expand Down
Loading
Loading