diff --git a/CHANGELOG.md b/CHANGELOG.md index a98e65db..76fa1e3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +# Added + +- ✨Add image attachments with access control + ## [Unreleased] diff --git a/src/backend/core/api/utils.py b/src/backend/core/api/utils.py new file mode 100644 index 00000000..53d84f68 --- /dev/null +++ b/src/backend/core/api/utils.py @@ -0,0 +1,33 @@ +"""Util to generate S3 authorization headers for object storage access control""" + +from django.core.files.storage import default_storage + +import botocore + + +def generate_s3_authorization_headers(key): + """ + Generate authorization headers for an s3 object. + These headers can be used as an alternative to signed urls with many benefits: + - the urls of our files never expire and can be stored in our documents' content + - we don't leak authorized urls that could be shared (file access can only be done + with cookies) + - access control is truly realtime + - the object storage service does not need to be exposed on internet + """ + url = default_storage.unsigned_connection.meta.client.generate_presigned_url( + "get_object", + ExpiresIn=0, + Params={"Bucket": default_storage.bucket_name, "Key": key}, + ) + request = botocore.awsrequest.AWSRequest(method="get", url=url) + + s3_client = default_storage.connection.meta.client + # pylint: disable=protected-access + credentials = s3_client._request_signer._credentials # noqa: SLF001 + frozen_credentials = credentials.get_frozen_credentials() + region = s3_client.meta.region_name + auth = botocore.auth.S3SigV4Auth(frozen_credentials, "s3", region) + auth.add_auth(request) + + return request diff --git a/src/backend/core/api/viewsets.py b/src/backend/core/api/viewsets.py index 9248c270..6384fa23 100644 --- a/src/backend/core/api/viewsets.py +++ b/src/backend/core/api/viewsets.py @@ -1,7 +1,9 @@ """API endpoints""" import os +import re import uuid +from urllib.parse import urlparse from django.conf import settings from django.contrib.postgres.aggregates import ArrayAgg @@ -30,7 +32,17 @@ from rest_framework import ( from core import models from core.utils import email_invitation -from . import permissions, serializers +from . import permissions, serializers, utils + +ATTACHMENTS_FOLDER = "attachments" +UUID_REGEX = ( + r"[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" +) +FILE_EXT_REGEX = r"\.[a-zA-Z]{3,4}" +MEDIA_URL_PATTERN = re.compile( + f"{settings.MEDIA_URL:s}({UUID_REGEX:s})/" + f"({ATTACHMENTS_FOLDER:s}/{UUID_REGEX:s}{FILE_EXT_REGEX:s})$" +) # pylint: disable=too-many-ancestors @@ -422,6 +434,48 @@ class DocumentViewSet( {"file": f"{settings.MEDIA_URL:s}{key:s}"}, status=status.HTTP_201_CREATED ) + @decorators.action(detail=False, methods=["get"], url_path="retrieve-auth") + def retrieve_auth(self, request, *args, **kwargs): + """ + This view is used by an Nginx subrequest to control access to a document's + attachment file. + + The original url is passed by nginx in the "HTTP_X_ORIGINAL_URL" header. + See corresponding ingress configuration in Helm chart and read about the + nginx.ingress.kubernetes.io/auth-url annotation to understand how the Nginx ingress + is configured to do this. + + Based on the original url and the logged in user, we must decide if we authorize Nginx + to let this request go through (by returning a 200 code) or if we block it (by returning + a 403 error). Note that we return 403 errors without any further details for security + reasons. + + When we let the request go through, we compute authorization headers that will be added to + the request going through thanks to the nginx.ingress.kubernetes.io/auth-response-headers + annotation. The request will then be proxied to the object storage backend who will + respond with the file after checking the signature included in headers. + """ + original_url = urlparse(request.META.get("HTTP_X_ORIGINAL_URL")) + match = MEDIA_URL_PATTERN.search(original_url.path) + + try: + pk, attachment_key = match.groups() + except AttributeError as excpt: + raise exceptions.PermissionDenied() from excpt + + # Check permission + try: + document = models.Document.objects.get(pk=pk) + except models.Document.DoesNotExist as excpt: + raise exceptions.PermissionDenied() from excpt + + if not document.get_abilities(request.user).get("retrieve", False): + raise exceptions.PermissionDenied() + + # Generate authorization headers and return an authorization to proceed with the request + request = utils.generate_s3_authorization_headers(f"{pk:s}/{attachment_key:s}") + return drf_response.Response("authorized", headers=request.headers, status=200) + class DocumentAccessViewSet( ResourceAccessViewsetMixin, diff --git a/src/backend/core/tests/documents/test_api_documents_attachment_upload.py b/src/backend/core/tests/documents/test_api_documents_attachment_upload.py index b2bb9200..3f1282d3 100644 --- a/src/backend/core/tests/documents/test_api_documents_attachment_upload.py +++ b/src/backend/core/tests/documents/test_api_documents_attachment_upload.py @@ -103,9 +103,7 @@ def test_api_documents_attachment_upload_reader(via, mock_user_get_teams): @pytest.mark.parametrize("role", ["editor", "administrator", "owner"]) @pytest.mark.parametrize("via", VIA) -def test_api_documents_attachment_upload_success( - via, role, mock_user_get_teams, settings -): +def test_api_documents_attachment_upload_success(via, role, mock_user_get_teams): """ Editors, administrators and owners of a document should be able to upload an attachment. """ @@ -130,7 +128,7 @@ def test_api_documents_attachment_upload_success( assert response.status_code == 201 - pattern = re.compile(rf"^{settings.MEDIA_URL}{document.id!s}/attachments/(.*)\.jpg") + pattern = re.compile(rf"^/media/{document.id!s}/attachments/(.*)\.jpg") match = pattern.search(response.json()["file"]) file_id = match.group(1) diff --git a/src/backend/core/tests/documents/test_api_documents_retrieve_auth.py b/src/backend/core/tests/documents/test_api_documents_retrieve_auth.py new file mode 100644 index 00000000..4724fc21 --- /dev/null +++ b/src/backend/core/tests/documents/test_api_documents_retrieve_auth.py @@ -0,0 +1,213 @@ +""" +Test file uploads API endpoint for users in impress's core app. +""" + +import uuid +from io import BytesIO +from urllib.parse import urlparse + +from django.conf import settings +from django.core.files.storage import default_storage +from django.utils import timezone + +import pytest +import requests +from rest_framework.test import APIClient + +from core import factories +from core.tests.conftest import TEAM, USER, VIA + +pytestmark = pytest.mark.django_db + + +def test_api_documents_retrieve_auth_anonymous_public(): + """Anonymous users should be able to retrieve attachments linked to a public document""" + document = factories.DocumentFactory(is_public=True) + + filename = f"{uuid.uuid4()!s}.jpg" + key = f"{document.pk!s}/attachments/{filename:s}" + + default_storage.connection.meta.client.put_object( + Bucket=default_storage.bucket_name, + Key=key, + Body=BytesIO(b"my prose"), + ContentType="text/plain", + ) + + original_url = f"http://localhost/media/{key:s}" + response = APIClient().get( + "/api/v1.0/documents/retrieve-auth/", HTTP_X_ORIGINAL_URL=original_url + ) + + assert response.status_code == 200 + + authorization = response["Authorization"] + assert "AWS4-HMAC-SHA256 Credential=" in authorization + assert ( + "SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=" + in authorization + ) + assert response["X-Amz-Date"] == timezone.now().strftime("%Y%m%dT%H%M%SZ") + + s3_url = urlparse(settings.AWS_S3_ENDPOINT_URL) + file_url = f"{settings.AWS_S3_ENDPOINT_URL:s}/impress-media-storage/{key:s}" + response = requests.get( + file_url, + headers={ + "authorization": authorization, + "x-amz-date": response["x-amz-date"], + "x-amz-content-sha256": response["x-amz-content-sha256"], + "Host": f"{s3_url.hostname:s}:{s3_url.port:d}", + }, + timeout=1, + ) + assert response.content.decode("utf-8") == "my prose" + + +def test_api_documents_retrieve_auth_anonymous_not_public(): + """ + Anonymous users should not be allowed to retrieve attachments linked to a document + that is not public. + """ + document = factories.DocumentFactory(is_public=False) + + filename = f"{uuid.uuid4()!s}.jpg" + media_url = f"http://localhost/media/{document.pk!s}/attachments/{filename:s}" + + response = APIClient().get( + "/api/v1.0/documents/retrieve-auth/", HTTP_X_ORIGINAL_URL=media_url + ) + + assert response.status_code == 403 + assert "Authorization" not in response + + +def test_api_documents_retrieve_auth_authenticated_public(): + """ + Authenticated users who are not related to a document should be able to + retrieve attachments linked to a public document. + """ + document = factories.DocumentFactory(is_public=True) + + user = factories.UserFactory() + client = APIClient() + client.force_login(user) + + filename = f"{uuid.uuid4()!s}.jpg" + key = f"{document.pk!s}/attachments/{filename:s}" + + default_storage.connection.meta.client.put_object( + Bucket=default_storage.bucket_name, + Key=key, + Body=BytesIO(b"my prose"), + ContentType="text/plain", + ) + + original_url = f"http://localhost/media/{key:s}" + response = APIClient().get( + "/api/v1.0/documents/retrieve-auth/", HTTP_X_ORIGINAL_URL=original_url + ) + + assert response.status_code == 200 + + authorization = response["Authorization"] + assert "AWS4-HMAC-SHA256 Credential=" in authorization + assert ( + "SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=" + in authorization + ) + assert response["X-Amz-Date"] == timezone.now().strftime("%Y%m%dT%H%M%SZ") + + s3_url = urlparse(settings.AWS_S3_ENDPOINT_URL) + file_url = f"{settings.AWS_S3_ENDPOINT_URL:s}/impress-media-storage/{key:s}" + response = requests.get( + file_url, + headers={ + "authorization": authorization, + "x-amz-date": response["x-amz-date"], + "x-amz-content-sha256": response["x-amz-content-sha256"], + "Host": f"{s3_url.hostname:s}:{s3_url.port:d}", + }, + timeout=1, + ) + assert response.content.decode("utf-8") == "my prose" + + +def test_api_documents_retrieve_auth_authenticated_not_public(): + """ + Authenticated users who are not related to a document should not be allowed to + retrieve attachments linked to a document that is not public. + """ + document = factories.DocumentFactory(is_public=False) + + user = factories.UserFactory() + client = APIClient() + client.force_login(user) + + filename = f"{uuid.uuid4()!s}.jpg" + media_url = f"http://localhost/media/{document.pk!s}/attachments/{filename:s}" + + response = APIClient().get( + "/api/v1.0/documents/retrieve-auth/", HTTP_X_ORIGINAL_URL=media_url + ) + + assert response.status_code == 403 + assert "Authorization" not in response + + +@pytest.mark.parametrize("is_public", [True, False]) +@pytest.mark.parametrize("via", VIA) +def test_api_documents_retrieve_auth_related(via, is_public, mock_user_get_teams): + """ + Users who have a role on a document, whatever the role, should be able to + retrieve related attachments. + """ + user = factories.UserFactory() + client = APIClient() + client.force_login(user) + + document = factories.DocumentFactory(is_public=is_public) + if via == USER: + factories.UserDocumentAccessFactory(document=document, user=user) + elif via == TEAM: + mock_user_get_teams.return_value = ["lasuite", "unknown"] + factories.TeamDocumentAccessFactory(document=document, team="lasuite") + + filename = f"{uuid.uuid4()!s}.jpg" + key = f"{document.pk!s}/attachments/{filename:s}" + + default_storage.connection.meta.client.put_object( + Bucket=default_storage.bucket_name, + Key=key, + Body=BytesIO(b"my prose"), + ContentType="text/plain", + ) + + original_url = f"http://localhost/media/{key:s}" + response = client.get( + "/api/v1.0/documents/retrieve-auth/", HTTP_X_ORIGINAL_URL=original_url + ) + + assert response.status_code == 200 + + authorization = response["Authorization"] + assert "AWS4-HMAC-SHA256 Credential=" in authorization + assert ( + "SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=" + in authorization + ) + assert response["X-Amz-Date"] == timezone.now().strftime("%Y%m%dT%H%M%SZ") + + s3_url = urlparse(settings.AWS_S3_ENDPOINT_URL) + file_url = f"{settings.AWS_S3_ENDPOINT_URL:s}/impress-media-storage/{key:s}" + response = requests.get( + file_url, + headers={ + "authorization": authorization, + "x-amz-date": response["x-amz-date"], + "x-amz-content-sha256": response["x-amz-content-sha256"], + "Host": f"{s3_url.hostname:s}:{s3_url.port:d}", + }, + timeout=1, + ) + assert response.content.decode("utf-8") == "my prose" diff --git a/src/helm/env.d/dev/values.impress.yaml.gotmpl b/src/helm/env.d/dev/values.impress.yaml.gotmpl index 8f8768b8..a2cf6d96 100644 --- a/src/helm/env.d/dev/values.impress.yaml.gotmpl +++ b/src/helm/env.d/dev/values.impress.yaml.gotmpl @@ -103,3 +103,17 @@ ingressWS: ingressAdmin: enabled: true host: impress.127.0.0.1.nip.io + +ingressMedia: + enabled: true + host: impress.127.0.0.1.nip.io + + annotations: + nginx.ingress.kubernetes.io/auth-url: https://impress.127.0.0.1.nip.io/api/v1.0/documents/retrieve-auth/ + nginx.ingress.kubernetes.io/auth-response-headers: "Authorization, X-Amz-Date, X-Amz-Content-SHA256" + nginx.ingress.kubernetes.io/upstream-vhost: minio.impress.svc.cluster.local:9000 + nginx.ingress.kubernetes.io/rewrite-target: /impress-media-storage/$1 + +serviceMedia: + host: minio.impress.svc.cluster.local + port: 9000 diff --git a/src/helm/env.d/preprod/values.impress.yaml.gotmpl b/src/helm/env.d/preprod/values.impress.yaml.gotmpl index 66513dd9..15502d32 100644 --- a/src/helm/env.d/preprod/values.impress.yaml.gotmpl +++ b/src/helm/env.d/preprod/values.impress.yaml.gotmpl @@ -154,3 +154,24 @@ ingressAdmin: cert-manager.io/cluster-issuer: letsencrypt-prod nginx.ingress.kubernetes.io/auth-signin: https://oauth2-proxy-preprod.beta.numerique.gouv.fr/oauth2/start nginx.ingress.kubernetes.io/auth-url: https://oauth2-proxy-preprod.beta.numerique.gouv.fr/oauth2/auth + +ingressMedia: + enabled: true + host: impress-preprod.beta.numerique.gouv.fr + + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + nginx.ingress.kubernetes.io/auth-url: https://impress-preprod.beta.numerique.gouv.fr/api/v1.0/documents/retrieve-auth/ + nginx.ingress.kubernetes.io/auth-response-headers: "Authorization, X-Amz-Date, X-Amz-Content-SHA256" + nginx.ingress.kubernetes.io/upstream-vhost: + secretKeyRef: + name: impress-media-storage.bucket.libre.sh + key: url + nginx.ingress.kubernetes.io/rewrite-target: /impress-media-storage/$1 + +serviceMedia: + host: + secretKeyRef: + name: impress-media-storage.bucket.libre.sh + key: url + port: 9000 diff --git a/src/helm/env.d/production/values.impress.yaml.gotmpl b/src/helm/env.d/production/values.impress.yaml.gotmpl index 3447c04f..94fafd93 100644 --- a/src/helm/env.d/production/values.impress.yaml.gotmpl +++ b/src/helm/env.d/production/values.impress.yaml.gotmpl @@ -154,3 +154,24 @@ ingressAdmin: cert-manager.io/cluster-issuer: letsencrypt nginx.ingress.kubernetes.io/auth-signin: https://oauth2-proxy.beta.numerique.gouv.fr/oauth2/start nginx.ingress.kubernetes.io/auth-url: https://oauth2-proxy.beta.numerique.gouv.fr/oauth2/auth + +ingressMedia: + enabled: true + host: docs.numerique.gouv.fr + + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + nginx.ingress.kubernetes.io/auth-url: https://docs.numerique.gouv.fr/api/v1.0/documents/retrieve-auth/ + nginx.ingress.kubernetes.io/auth-response-headers: "Authorization, X-Amz-Date, X-Amz-Content-SHA256" + nginx.ingress.kubernetes.io/upstream-vhost: + secretKeyRef: + name: impress-media-storage.bucket.libre.sh + key: url + nginx.ingress.kubernetes.io/rewrite-target: /impress-media-storage/$1 + +serviceMedia: + host: + secretKeyRef: + name: impress-media-storage.bucket.libre.sh + key: url + port: 9000 diff --git a/src/helm/env.d/staging/values.impress.yaml.gotmpl b/src/helm/env.d/staging/values.impress.yaml.gotmpl index ae8b2a8d..be1925dc 100644 --- a/src/helm/env.d/staging/values.impress.yaml.gotmpl +++ b/src/helm/env.d/staging/values.impress.yaml.gotmpl @@ -154,3 +154,24 @@ ingressAdmin: cert-manager.io/cluster-issuer: letsencrypt-prod nginx.ingress.kubernetes.io/auth-signin: https://oauth2-proxy-preprod.beta.numerique.gouv.fr/oauth2/start nginx.ingress.kubernetes.io/auth-url: https://oauth2-proxy-preprod.beta.numerique.gouv.fr/oauth2/auth + +ingressMedia: + enabled: true + host: impress-staging.beta.numerique.gouv.fr + + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + nginx.ingress.kubernetes.io/auth-url: https://impress-staging.beta.numerique.gouv.fr/api/v1.0/documents/retrieve-auth/ + nginx.ingress.kubernetes.io/auth-response-headers: "Authorization, X-Amz-Date, X-Amz-Content-SHA256" + nginx.ingress.kubernetes.io/upstream-vhost: + secretKeyRef: + name: impress-media-storage.bucket.libre.sh + key: url + nginx.ingress.kubernetes.io/rewrite-target: /impress-media-storage/$1 + +serviceMedia: + host: + secretKeyRef: + name: impress-media-storage.bucket.libre.sh + key: url + port: 9000 diff --git a/src/helm/impress/templates/ingress_media.yaml b/src/helm/impress/templates/ingress_media.yaml new file mode 100644 index 00000000..331753a8 --- /dev/null +++ b/src/helm/impress/templates/ingress_media.yaml @@ -0,0 +1,83 @@ +{{- if .Values.ingressMedia.enabled -}} +{{- $fullName := include "impress.fullname" . -}} +{{- if and .Values.ingressMedia.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingressMedia.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingressMedia.annotations "kubernetes.io/ingress.class" .Values.ingressMedia.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }}-media + namespace: {{ .Release.Namespace | quote }} + labels: + {{- include "impress.labels" . | nindent 4 }} + {{- with .Values.ingressMedia.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingressMedia.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingressMedia.className }} + {{- end }} + {{- if .Values.ingressMedia.tls.enabled }} + tls: + {{- if .Values.ingressMedia.host }} + - secretName: {{ $fullName }}-tls + hosts: + - {{ .Values.ingressMedia.host | quote }} + {{- end }} + {{- range .Values.ingressMedia.tls.additional }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- if .Values.ingressMedia.host }} + - host: {{ .Values.ingressMedia.host | quote }} + http: + paths: + - path: {{ .Values.ingressMedia.path | quote }} + {{- if semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion }} + pathType: Prefix + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }}-media + port: + number: {{ .Values.serviceMedia.port }} + {{- else }} + serviceName: {{ $fullName }}-media + servicePort: {{ .Values.serviceMedia.port }} + {{- end }} + {{- end }} + {{- range .Values.ingressMedia.hosts }} + - host: {{ . | quote }} + http: + paths: + - path: {{ $.Values.ingressMedia.path | quote }} + {{- if semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion }} + pathType: Prefix + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }}-media + port: + number: {{ .Values.serviceMedia.port }} + {{- else }} + serviceName: {{ $fullName }}-media + servicePort: {{ .Values.serviceMedia.port }} + {{- end }} + {{- end }} +{{- end }} diff --git a/src/helm/impress/templates/media_svc.yaml b/src/helm/impress/templates/media_svc.yaml new file mode 100644 index 00000000..dc7bf035 --- /dev/null +++ b/src/helm/impress/templates/media_svc.yaml @@ -0,0 +1,14 @@ +{{- $fullName := include "impress.fullname" . -}} +{{- $component := "media" -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ $fullName }}-media + namespace: {{ .Release.Namespace | quote }} + labels: + {{- include "impress.common.labels" (list . $component) | nindent 4 }} + annotations: + {{- toYaml $.Values.serviceMedia.annotations | nindent 4 }} +spec: + type: ExternalName + externalName: {{ $.Values.serviceMedia.host }} diff --git a/src/helm/impress/values.yaml b/src/helm/impress/values.yaml index 60dbbcce..635c1d84 100644 --- a/src/helm/impress/values.yaml +++ b/src/helm/impress/values.yaml @@ -37,7 +37,7 @@ ingress: ## @param ingress.hosts Additional host to configure for the Ingress hosts: [] # - chart-example.local - ## @param ingress.tls.enabled Weather to enable TLS for the Ingress + ## @param ingress.tls.enabled Wether to enable TLS for the Ingress ## @skip ingress.tls.additional ## @extra ingress.tls.additional[].secretName Secret name for additional TLS config ## @extra ingress.tls.additional[].hosts[] Hosts for additional TLS config @@ -60,7 +60,7 @@ ingressWS: ## @param ingress.hosts Additional host to configure for the Ingress hosts: [] # - chart-example.local - ## @param ingressWS.tls.enabled Weather to enable TLS for the Ingress + ## @param ingressWS.tls.enabled Wether to enable TLS for the Ingress ## @skip ingressWS.tls.additional ## @extra ingressWS.tls.additional[].secretName Secret name for additional TLS config ## @extra ingressWS.tls.additional[].hosts[] Hosts for additional TLS config @@ -87,7 +87,7 @@ ingressAdmin: ## @param ingressAdmin.hosts Additional host to configure for the Ingress hosts: [ ] # - chart-example.local - ## @param ingressAdmin.tls.enabled Weather to enable TLS for the Ingress + ## @param ingressAdmin.tls.enabled Wether to enable TLS for the Ingress ## @skip ingressAdmin.tls.additional ## @extra ingressAdmin.tls.additional[].secretName Secret name for additional TLS config ## @extra ingressAdmin.tls.additional[].hosts[] Hosts for additional TLS config @@ -95,6 +95,36 @@ ingressAdmin: enabled: true additional: [] +## @param ingressMedia.enabled whether to enable the Ingress or not +## @param ingressMedia.className IngressClass to use for the Ingress +## @param ingressMedia.host Host for the Ingress +## @param ingressMedia.path Path to use for the Ingress +ingressMedia: + enabled: false + className: null + host: impress.example.com + path: /media/(.*) + ## @param ingressMedia.hosts Additional host to configure for the Ingress + hosts: [ ] + # - chart-example.local + ## @param ingressMedia.tls.enabled Wether to enable TLS for the Ingress + ## @skip ingressMedia.tls.additional + ## @extra ingressMedia.tls.additional[].secretName Secret name for additional TLS config + ## @extra ingressMedia.tls.additional[].hosts[] Hosts for additional TLS config + tls: + enabled: true + additional: [] + + annotations: + nginx.ingress.kubernetes.io/auth-url: https://impress.example.com/api/v1.0/documents/retrieve-auth/ + nginx.ingress.kubernetes.io/auth-response-headers: "Authorization, X-Amz-Date, X-Amz-Content-SHA256" + nginx.ingress.kubernetes.io/upstream-vhost: minio.impress.svc.cluster.local:9000 + +serviceMedia: + host: minio.impress.svc.cluster.local + port: 9000 + annotations: {} + ## @section backend